1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2448 @@ 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 "nsOfflineCacheUpdate.h" 1.14 + 1.15 +#include "nsCPrefetchService.h" 1.16 +#include "nsCURILoader.h" 1.17 +#include "nsIApplicationCacheContainer.h" 1.18 +#include "nsIApplicationCacheChannel.h" 1.19 +#include "nsIApplicationCacheService.h" 1.20 +#include "nsICache.h" 1.21 +#include "nsICacheService.h" 1.22 +#include "nsICacheSession.h" 1.23 +#include "nsICachingChannel.h" 1.24 +#include "nsIContent.h" 1.25 +#include "mozilla/dom/Element.h" 1.26 +#include "nsIDocumentLoader.h" 1.27 +#include "nsIDOMElement.h" 1.28 +#include "nsIDOMWindow.h" 1.29 +#include "nsIDOMOfflineResourceList.h" 1.30 +#include "nsIDocument.h" 1.31 +#include "nsIObserverService.h" 1.32 +#include "nsIURL.h" 1.33 +#include "nsIWebProgress.h" 1.34 +#include "nsICryptoHash.h" 1.35 +#include "nsICacheEntry.h" 1.36 +#include "nsIPermissionManager.h" 1.37 +#include "nsIPrincipal.h" 1.38 +#include "nsNetCID.h" 1.39 +#include "nsNetUtil.h" 1.40 +#include "nsServiceManagerUtils.h" 1.41 +#include "nsStreamUtils.h" 1.42 +#include "nsThreadUtils.h" 1.43 +#include "nsProxyRelease.h" 1.44 +#include "nsIConsoleService.h" 1.45 +#include "prlog.h" 1.46 +#include "nsIAsyncVerifyRedirectCallback.h" 1.47 +#include "mozilla/Preferences.h" 1.48 +#include "mozilla/Attributes.h" 1.49 + 1.50 +#include "nsXULAppAPI.h" 1.51 + 1.52 +using namespace mozilla; 1.53 + 1.54 +static const uint32_t kRescheduleLimit = 3; 1.55 +// Max number of retries for every entry of pinned app. 1.56 +static const uint32_t kPinnedEntryRetriesLimit = 3; 1.57 +// Maximum number of parallel items loads 1.58 +static const uint32_t kParallelLoadLimit = 15; 1.59 + 1.60 +// Quota for offline apps when preloading 1.61 +static const int32_t kCustomProfileQuota = 512000; 1.62 + 1.63 +#if defined(PR_LOGGING) 1.64 +// 1.65 +// To enable logging (see prlog.h for full details): 1.66 +// 1.67 +// set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 1.68 +// set NSPR_LOG_FILE=offlineupdate.log 1.69 +// 1.70 +// this enables PR_LOG_ALWAYS level information and places all output in 1.71 +// the file offlineupdate.log 1.72 +// 1.73 +extern PRLogModuleInfo *gOfflineCacheUpdateLog; 1.74 +#endif 1.75 + 1.76 +#undef LOG 1.77 +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) 1.78 + 1.79 +#undef LOG_ENABLED 1.80 +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) 1.81 + 1.82 +class AutoFreeArray { 1.83 +public: 1.84 + AutoFreeArray(uint32_t count, char **values) 1.85 + : mCount(count), mValues(values) {}; 1.86 + ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); } 1.87 +private: 1.88 + uint32_t mCount; 1.89 + char **mValues; 1.90 +}; 1.91 + 1.92 +namespace { // anon 1.93 + 1.94 +nsresult 1.95 +DropReferenceFromURL(nsIURI * aURI) 1.96 +{ 1.97 + // XXXdholbert If this SetRef fails, callers of this method probably 1.98 + // want to call aURI->CloneIgnoringRef() and use the result of that. 1.99 + return aURI->SetRef(EmptyCString()); 1.100 +} 1.101 + 1.102 +void 1.103 +LogToConsole(const char * message, nsOfflineCacheUpdateItem * item = nullptr) 1.104 +{ 1.105 + nsCOMPtr<nsIConsoleService> consoleService = 1.106 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.107 + if (consoleService) 1.108 + { 1.109 + nsAutoString messageUTF16 = NS_ConvertUTF8toUTF16(message); 1.110 + if (item && item->mURI) { 1.111 + nsAutoCString uriSpec; 1.112 + item->mURI->GetSpec(uriSpec); 1.113 + 1.114 + messageUTF16.Append(NS_LITERAL_STRING(", URL=")); 1.115 + messageUTF16.Append(NS_ConvertUTF8toUTF16(uriSpec)); 1.116 + } 1.117 + consoleService->LogStringMessage(messageUTF16.get()); 1.118 + } 1.119 +} 1.120 + 1.121 +} // anon namespace 1.122 + 1.123 +//----------------------------------------------------------------------------- 1.124 +// nsManifestCheck 1.125 +//----------------------------------------------------------------------------- 1.126 + 1.127 +class nsManifestCheck MOZ_FINAL : public nsIStreamListener 1.128 + , public nsIChannelEventSink 1.129 + , public nsIInterfaceRequestor 1.130 +{ 1.131 +public: 1.132 + nsManifestCheck(nsOfflineCacheUpdate *aUpdate, 1.133 + nsIURI *aURI, 1.134 + nsIURI *aReferrerURI) 1.135 + : mUpdate(aUpdate) 1.136 + , mURI(aURI) 1.137 + , mReferrerURI(aReferrerURI) 1.138 + {} 1.139 + 1.140 + NS_DECL_ISUPPORTS 1.141 + NS_DECL_NSIREQUESTOBSERVER 1.142 + NS_DECL_NSISTREAMLISTENER 1.143 + NS_DECL_NSICHANNELEVENTSINK 1.144 + NS_DECL_NSIINTERFACEREQUESTOR 1.145 + 1.146 + nsresult Begin(); 1.147 + 1.148 +private: 1.149 + 1.150 + static NS_METHOD ReadManifest(nsIInputStream *aInputStream, 1.151 + void *aClosure, 1.152 + const char *aFromSegment, 1.153 + uint32_t aOffset, 1.154 + uint32_t aCount, 1.155 + uint32_t *aBytesConsumed); 1.156 + 1.157 + nsRefPtr<nsOfflineCacheUpdate> mUpdate; 1.158 + nsCOMPtr<nsIURI> mURI; 1.159 + nsCOMPtr<nsIURI> mReferrerURI; 1.160 + nsCOMPtr<nsICryptoHash> mManifestHash; 1.161 + nsCOMPtr<nsIChannel> mChannel; 1.162 +}; 1.163 + 1.164 +//----------------------------------------------------------------------------- 1.165 +// nsManifestCheck::nsISupports 1.166 +//----------------------------------------------------------------------------- 1.167 +NS_IMPL_ISUPPORTS(nsManifestCheck, 1.168 + nsIRequestObserver, 1.169 + nsIStreamListener, 1.170 + nsIChannelEventSink, 1.171 + nsIInterfaceRequestor) 1.172 + 1.173 +//----------------------------------------------------------------------------- 1.174 +// nsManifestCheck <public> 1.175 +//----------------------------------------------------------------------------- 1.176 + 1.177 +nsresult 1.178 +nsManifestCheck::Begin() 1.179 +{ 1.180 + nsresult rv; 1.181 + mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); 1.182 + NS_ENSURE_SUCCESS(rv, rv); 1.183 + 1.184 + rv = mManifestHash->Init(nsICryptoHash::MD5); 1.185 + NS_ENSURE_SUCCESS(rv, rv); 1.186 + 1.187 + rv = NS_NewChannel(getter_AddRefs(mChannel), 1.188 + mURI, 1.189 + nullptr, nullptr, nullptr, 1.190 + nsIRequest::LOAD_BYPASS_CACHE); 1.191 + NS_ENSURE_SUCCESS(rv, rv); 1.192 + 1.193 + // configure HTTP specific stuff 1.194 + nsCOMPtr<nsIHttpChannel> httpChannel = 1.195 + do_QueryInterface(mChannel); 1.196 + if (httpChannel) { 1.197 + httpChannel->SetReferrer(mReferrerURI); 1.198 + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), 1.199 + NS_LITERAL_CSTRING("offline-resource"), 1.200 + false); 1.201 + } 1.202 + 1.203 + rv = mChannel->AsyncOpen(this, nullptr); 1.204 + NS_ENSURE_SUCCESS(rv, rv); 1.205 + 1.206 + return NS_OK; 1.207 +} 1.208 + 1.209 +//----------------------------------------------------------------------------- 1.210 +// nsManifestCheck <public> 1.211 +//----------------------------------------------------------------------------- 1.212 + 1.213 +/* static */ 1.214 +NS_METHOD 1.215 +nsManifestCheck::ReadManifest(nsIInputStream *aInputStream, 1.216 + void *aClosure, 1.217 + const char *aFromSegment, 1.218 + uint32_t aOffset, 1.219 + uint32_t aCount, 1.220 + uint32_t *aBytesConsumed) 1.221 +{ 1.222 + nsManifestCheck *manifestCheck = 1.223 + static_cast<nsManifestCheck*>(aClosure); 1.224 + 1.225 + nsresult rv; 1.226 + *aBytesConsumed = aCount; 1.227 + 1.228 + rv = manifestCheck->mManifestHash->Update( 1.229 + reinterpret_cast<const uint8_t *>(aFromSegment), aCount); 1.230 + NS_ENSURE_SUCCESS(rv, rv); 1.231 + 1.232 + return NS_OK; 1.233 +} 1.234 + 1.235 +//----------------------------------------------------------------------------- 1.236 +// nsManifestCheck::nsIStreamListener 1.237 +//----------------------------------------------------------------------------- 1.238 + 1.239 +NS_IMETHODIMP 1.240 +nsManifestCheck::OnStartRequest(nsIRequest *aRequest, 1.241 + nsISupports *aContext) 1.242 +{ 1.243 + return NS_OK; 1.244 +} 1.245 + 1.246 +NS_IMETHODIMP 1.247 +nsManifestCheck::OnDataAvailable(nsIRequest *aRequest, 1.248 + nsISupports *aContext, 1.249 + nsIInputStream *aStream, 1.250 + uint64_t aOffset, 1.251 + uint32_t aCount) 1.252 +{ 1.253 + uint32_t bytesRead; 1.254 + aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead); 1.255 + return NS_OK; 1.256 +} 1.257 + 1.258 +NS_IMETHODIMP 1.259 +nsManifestCheck::OnStopRequest(nsIRequest *aRequest, 1.260 + nsISupports *aContext, 1.261 + nsresult aStatus) 1.262 +{ 1.263 + nsAutoCString manifestHash; 1.264 + if (NS_SUCCEEDED(aStatus)) { 1.265 + mManifestHash->Finish(true, manifestHash); 1.266 + } 1.267 + 1.268 + mUpdate->ManifestCheckCompleted(aStatus, manifestHash); 1.269 + 1.270 + return NS_OK; 1.271 +} 1.272 + 1.273 +//----------------------------------------------------------------------------- 1.274 +// nsManifestCheck::nsIInterfaceRequestor 1.275 +//----------------------------------------------------------------------------- 1.276 + 1.277 +NS_IMETHODIMP 1.278 +nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult) 1.279 +{ 1.280 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.281 + NS_ADDREF_THIS(); 1.282 + *aResult = static_cast<nsIChannelEventSink *>(this); 1.283 + return NS_OK; 1.284 + } 1.285 + 1.286 + return NS_ERROR_NO_INTERFACE; 1.287 +} 1.288 + 1.289 +//----------------------------------------------------------------------------- 1.290 +// nsManifestCheck::nsIChannelEventSink 1.291 +//----------------------------------------------------------------------------- 1.292 + 1.293 +NS_IMETHODIMP 1.294 +nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.295 + nsIChannel *aNewChannel, 1.296 + uint32_t aFlags, 1.297 + nsIAsyncVerifyRedirectCallback *callback) 1.298 +{ 1.299 + // Redirects should cause the load (and therefore the update) to fail. 1.300 + if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) { 1.301 + callback->OnRedirectVerifyCallback(NS_OK); 1.302 + return NS_OK; 1.303 + } 1.304 + 1.305 + LogToConsole("Manifest check failed because its response is a redirect"); 1.306 + 1.307 + aOldChannel->Cancel(NS_ERROR_ABORT); 1.308 + return NS_ERROR_ABORT; 1.309 +} 1.310 + 1.311 +//----------------------------------------------------------------------------- 1.312 +// nsOfflineCacheUpdateItem::nsISupports 1.313 +//----------------------------------------------------------------------------- 1.314 + 1.315 +NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateItem, 1.316 + nsIRequestObserver, 1.317 + nsIStreamListener, 1.318 + nsIRunnable, 1.319 + nsIInterfaceRequestor, 1.320 + nsIChannelEventSink) 1.321 + 1.322 +//----------------------------------------------------------------------------- 1.323 +// nsOfflineCacheUpdateItem <public> 1.324 +//----------------------------------------------------------------------------- 1.325 + 1.326 +nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsIURI *aURI, 1.327 + nsIURI *aReferrerURI, 1.328 + nsIApplicationCache *aApplicationCache, 1.329 + nsIApplicationCache *aPreviousApplicationCache, 1.330 + uint32_t type) 1.331 + : mURI(aURI) 1.332 + , mReferrerURI(aReferrerURI) 1.333 + , mApplicationCache(aApplicationCache) 1.334 + , mPreviousApplicationCache(aPreviousApplicationCache) 1.335 + , mItemType(type) 1.336 + , mChannel(nullptr) 1.337 + , mState(LoadStatus::UNINITIALIZED) 1.338 + , mBytesRead(0) 1.339 +{ 1.340 +} 1.341 + 1.342 +nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem() 1.343 +{ 1.344 +} 1.345 + 1.346 +nsresult 1.347 +nsOfflineCacheUpdateItem::OpenChannel(nsOfflineCacheUpdate *aUpdate) 1.348 +{ 1.349 +#if defined(PR_LOGGING) 1.350 + if (LOG_ENABLED()) { 1.351 + nsAutoCString spec; 1.352 + mURI->GetSpec(spec); 1.353 + LOG(("%p: Opening channel for %s", this, spec.get())); 1.354 + } 1.355 +#endif 1.356 + 1.357 + if (mUpdate) { 1.358 + // Holding a reference to the update means this item is already 1.359 + // in progress (has a channel, or is just in between OnStopRequest() 1.360 + // and its Run() call. We must never open channel on this item again. 1.361 + LOG((" %p is already running! ignoring", this)); 1.362 + return NS_ERROR_ALREADY_OPENED; 1.363 + } 1.364 + 1.365 + nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey); 1.366 + NS_ENSURE_SUCCESS(rv, rv); 1.367 + 1.368 + uint32_t flags = nsIRequest::LOAD_BACKGROUND | 1.369 + nsICachingChannel::LOAD_ONLY_IF_MODIFIED | 1.370 + nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE; 1.371 + 1.372 + if (mApplicationCache == mPreviousApplicationCache) { 1.373 + // Same app cache to read from and to write to is used during 1.374 + // an only-update-check procedure. Here we protect the existing 1.375 + // cache from being modified. 1.376 + flags |= nsIRequest::INHIBIT_CACHING; 1.377 + } 1.378 + 1.379 + rv = NS_NewChannel(getter_AddRefs(mChannel), 1.380 + mURI, 1.381 + nullptr, nullptr, this, 1.382 + flags); 1.383 + NS_ENSURE_SUCCESS(rv, rv); 1.384 + 1.385 + nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = 1.386 + do_QueryInterface(mChannel, &rv); 1.387 + 1.388 + // Support for nsIApplicationCacheChannel is required. 1.389 + NS_ENSURE_SUCCESS(rv, rv); 1.390 + 1.391 + // Use the existing application cache as the cache to check. 1.392 + rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache); 1.393 + NS_ENSURE_SUCCESS(rv, rv); 1.394 + 1.395 + // Set the new application cache as the target for write. 1.396 + rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache); 1.397 + NS_ENSURE_SUCCESS(rv, rv); 1.398 + 1.399 + // configure HTTP specific stuff 1.400 + nsCOMPtr<nsIHttpChannel> httpChannel = 1.401 + do_QueryInterface(mChannel); 1.402 + if (httpChannel) { 1.403 + httpChannel->SetReferrer(mReferrerURI); 1.404 + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), 1.405 + NS_LITERAL_CSTRING("offline-resource"), 1.406 + false); 1.407 + } 1.408 + 1.409 + rv = mChannel->AsyncOpen(this, nullptr); 1.410 + NS_ENSURE_SUCCESS(rv, rv); 1.411 + 1.412 + mUpdate = aUpdate; 1.413 + 1.414 + mState = LoadStatus::REQUESTED; 1.415 + 1.416 + return NS_OK; 1.417 +} 1.418 + 1.419 +nsresult 1.420 +nsOfflineCacheUpdateItem::Cancel() 1.421 +{ 1.422 + if (mChannel) { 1.423 + mChannel->Cancel(NS_ERROR_ABORT); 1.424 + mChannel = nullptr; 1.425 + } 1.426 + 1.427 + mState = LoadStatus::UNINITIALIZED; 1.428 + 1.429 + return NS_OK; 1.430 +} 1.431 + 1.432 +//----------------------------------------------------------------------------- 1.433 +// nsOfflineCacheUpdateItem::nsIStreamListener 1.434 +//----------------------------------------------------------------------------- 1.435 + 1.436 +NS_IMETHODIMP 1.437 +nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest, 1.438 + nsISupports *aContext) 1.439 +{ 1.440 + mState = LoadStatus::RECEIVING; 1.441 + 1.442 + return NS_OK; 1.443 +} 1.444 + 1.445 +NS_IMETHODIMP 1.446 +nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest, 1.447 + nsISupports *aContext, 1.448 + nsIInputStream *aStream, 1.449 + uint64_t aOffset, 1.450 + uint32_t aCount) 1.451 +{ 1.452 + uint32_t bytesRead = 0; 1.453 + aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead); 1.454 + mBytesRead += bytesRead; 1.455 + LOG(("loaded %u bytes into offline cache [offset=%llu]\n", 1.456 + bytesRead, aOffset)); 1.457 + 1.458 + mUpdate->OnByteProgress(bytesRead); 1.459 + 1.460 + return NS_OK; 1.461 +} 1.462 + 1.463 +NS_IMETHODIMP 1.464 +nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest, 1.465 + nsISupports *aContext, 1.466 + nsresult aStatus) 1.467 +{ 1.468 +#if defined(PR_LOGGING) 1.469 + if (LOG_ENABLED()) { 1.470 + nsAutoCString spec; 1.471 + mURI->GetSpec(spec); 1.472 + LOG(("%p: Done fetching offline item %s [status=%x]\n", 1.473 + this, spec.get(), aStatus)); 1.474 + } 1.475 +#endif 1.476 + 1.477 + if (mBytesRead == 0 && aStatus == NS_OK) { 1.478 + // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was 1.479 + // specified), but the object should report loadedSize as if it 1.480 + // did. 1.481 + mChannel->GetContentLength(&mBytesRead); 1.482 + mUpdate->OnByteProgress(mBytesRead); 1.483 + } 1.484 + 1.485 + if (NS_FAILED(aStatus)) { 1.486 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 1.487 + if (httpChannel) { 1.488 + bool isNoStore; 1.489 + if (NS_SUCCEEDED(httpChannel->IsNoStoreResponse(&isNoStore)) 1.490 + && isNoStore) { 1.491 + LogToConsole("Offline cache manifest item has Cache-control: no-store header", 1.492 + this); 1.493 + } 1.494 + } 1.495 + } 1.496 + 1.497 + // We need to notify the update that the load is complete, but we 1.498 + // want to give the channel a chance to close the cache entries. 1.499 + NS_DispatchToCurrentThread(this); 1.500 + 1.501 + return NS_OK; 1.502 +} 1.503 + 1.504 + 1.505 +//----------------------------------------------------------------------------- 1.506 +// nsOfflineCacheUpdateItem::nsIRunnable 1.507 +//----------------------------------------------------------------------------- 1.508 +NS_IMETHODIMP 1.509 +nsOfflineCacheUpdateItem::Run() 1.510 +{ 1.511 + // Set mState to LOADED here rather than in OnStopRequest to prevent 1.512 + // race condition when checking state of all mItems in ProcessNextURI(). 1.513 + // If state would have been set in OnStopRequest we could mistakenly 1.514 + // take this item as already finished and finish the update process too 1.515 + // early when ProcessNextURI() would get called between OnStopRequest() 1.516 + // and Run() of this item. Finish() would then have been called twice. 1.517 + mState = LoadStatus::LOADED; 1.518 + 1.519 + nsRefPtr<nsOfflineCacheUpdate> update; 1.520 + update.swap(mUpdate); 1.521 + update->LoadCompleted(this); 1.522 + 1.523 + return NS_OK; 1.524 +} 1.525 + 1.526 +//----------------------------------------------------------------------------- 1.527 +// nsOfflineCacheUpdateItem::nsIInterfaceRequestor 1.528 +//----------------------------------------------------------------------------- 1.529 + 1.530 +NS_IMETHODIMP 1.531 +nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult) 1.532 +{ 1.533 + if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1.534 + NS_ADDREF_THIS(); 1.535 + *aResult = static_cast<nsIChannelEventSink *>(this); 1.536 + return NS_OK; 1.537 + } 1.538 + 1.539 + return NS_ERROR_NO_INTERFACE; 1.540 +} 1.541 + 1.542 +//----------------------------------------------------------------------------- 1.543 +// nsOfflineCacheUpdateItem::nsIChannelEventSink 1.544 +//----------------------------------------------------------------------------- 1.545 + 1.546 +NS_IMETHODIMP 1.547 +nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.548 + nsIChannel *aNewChannel, 1.549 + uint32_t aFlags, 1.550 + nsIAsyncVerifyRedirectCallback *cb) 1.551 +{ 1.552 + if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { 1.553 + // Don't allow redirect in case of non-internal redirect and cancel 1.554 + // the channel to clean the cache entry. 1.555 + LogToConsole("Offline cache manifest failed because an item redirects", this); 1.556 + 1.557 + aOldChannel->Cancel(NS_ERROR_ABORT); 1.558 + return NS_ERROR_ABORT; 1.559 + } 1.560 + 1.561 + nsCOMPtr<nsIURI> newURI; 1.562 + nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI)); 1.563 + if (NS_FAILED(rv)) 1.564 + return rv; 1.565 + 1.566 + nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = 1.567 + do_QueryInterface(aNewChannel); 1.568 + if (appCacheChannel) { 1.569 + rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache); 1.570 + NS_ENSURE_SUCCESS(rv, rv); 1.571 + } 1.572 + 1.573 + nsAutoCString oldScheme; 1.574 + mURI->GetScheme(oldScheme); 1.575 + 1.576 + bool match; 1.577 + if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) { 1.578 + LOG(("rejected: redirected to a different scheme\n")); 1.579 + return NS_ERROR_ABORT; 1.580 + } 1.581 + 1.582 + // HTTP request headers are not automatically forwarded to the new channel. 1.583 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel); 1.584 + NS_ENSURE_STATE(httpChannel); 1.585 + 1.586 + httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"), 1.587 + NS_LITERAL_CSTRING("offline-resource"), 1.588 + false); 1.589 + 1.590 + mChannel = aNewChannel; 1.591 + 1.592 + cb->OnRedirectVerifyCallback(NS_OK); 1.593 + return NS_OK; 1.594 +} 1.595 + 1.596 +nsresult 1.597 +nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded) 1.598 +{ 1.599 + *succeeded = false; 1.600 + 1.601 + if (!mChannel) 1.602 + return NS_OK; 1.603 + 1.604 + nsresult rv; 1.605 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv); 1.606 + NS_ENSURE_SUCCESS(rv, rv); 1.607 + 1.608 + bool reqSucceeded; 1.609 + rv = httpChannel->GetRequestSucceeded(&reqSucceeded); 1.610 + if (NS_ERROR_NOT_AVAILABLE == rv) 1.611 + return NS_OK; 1.612 + NS_ENSURE_SUCCESS(rv, rv); 1.613 + 1.614 + if (!reqSucceeded) { 1.615 + LOG(("Request failed")); 1.616 + return NS_OK; 1.617 + } 1.618 + 1.619 + nsresult channelStatus; 1.620 + rv = httpChannel->GetStatus(&channelStatus); 1.621 + NS_ENSURE_SUCCESS(rv, rv); 1.622 + 1.623 + if (NS_FAILED(channelStatus)) { 1.624 + LOG(("Channel status=0x%08x", channelStatus)); 1.625 + return NS_OK; 1.626 + } 1.627 + 1.628 + *succeeded = true; 1.629 + return NS_OK; 1.630 +} 1.631 + 1.632 +bool 1.633 +nsOfflineCacheUpdateItem::IsScheduled() 1.634 +{ 1.635 + return mState == LoadStatus::UNINITIALIZED; 1.636 +} 1.637 + 1.638 +bool 1.639 +nsOfflineCacheUpdateItem::IsInProgress() 1.640 +{ 1.641 + return mState == LoadStatus::REQUESTED || 1.642 + mState == LoadStatus::RECEIVING; 1.643 +} 1.644 + 1.645 +bool 1.646 +nsOfflineCacheUpdateItem::IsCompleted() 1.647 +{ 1.648 + return mState == LoadStatus::LOADED; 1.649 +} 1.650 + 1.651 +nsresult 1.652 +nsOfflineCacheUpdateItem::GetStatus(uint16_t *aStatus) 1.653 +{ 1.654 + if (!mChannel) { 1.655 + *aStatus = 0; 1.656 + return NS_OK; 1.657 + } 1.658 + 1.659 + nsresult rv; 1.660 + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv); 1.661 + NS_ENSURE_SUCCESS(rv, rv); 1.662 + 1.663 + uint32_t httpStatus; 1.664 + rv = httpChannel->GetResponseStatus(&httpStatus); 1.665 + if (rv == NS_ERROR_NOT_AVAILABLE) { 1.666 + *aStatus = 0; 1.667 + return NS_OK; 1.668 + } 1.669 + 1.670 + NS_ENSURE_SUCCESS(rv, rv); 1.671 + *aStatus = uint16_t(httpStatus); 1.672 + return NS_OK; 1.673 +} 1.674 + 1.675 +//----------------------------------------------------------------------------- 1.676 +// nsOfflineManifestItem 1.677 +//----------------------------------------------------------------------------- 1.678 + 1.679 +//----------------------------------------------------------------------------- 1.680 +// nsOfflineManifestItem <public> 1.681 +//----------------------------------------------------------------------------- 1.682 + 1.683 +nsOfflineManifestItem::nsOfflineManifestItem(nsIURI *aURI, 1.684 + nsIURI *aReferrerURI, 1.685 + nsIApplicationCache *aApplicationCache, 1.686 + nsIApplicationCache *aPreviousApplicationCache) 1.687 + : nsOfflineCacheUpdateItem(aURI, aReferrerURI, 1.688 + aApplicationCache, aPreviousApplicationCache, 1.689 + nsIApplicationCache::ITEM_MANIFEST) 1.690 + , mParserState(PARSE_INIT) 1.691 + , mNeedsUpdate(true) 1.692 + , mManifestHashInitialized(false) 1.693 +{ 1.694 + ReadStrictFileOriginPolicyPref(); 1.695 +} 1.696 + 1.697 +nsOfflineManifestItem::~nsOfflineManifestItem() 1.698 +{ 1.699 +} 1.700 + 1.701 +//----------------------------------------------------------------------------- 1.702 +// nsOfflineManifestItem <private> 1.703 +//----------------------------------------------------------------------------- 1.704 + 1.705 +/* static */ 1.706 +NS_METHOD 1.707 +nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream, 1.708 + void *aClosure, 1.709 + const char *aFromSegment, 1.710 + uint32_t aOffset, 1.711 + uint32_t aCount, 1.712 + uint32_t *aBytesConsumed) 1.713 +{ 1.714 + nsOfflineManifestItem *manifest = 1.715 + static_cast<nsOfflineManifestItem*>(aClosure); 1.716 + 1.717 + nsresult rv; 1.718 + 1.719 + *aBytesConsumed = aCount; 1.720 + 1.721 + if (manifest->mParserState == PARSE_ERROR) { 1.722 + // parse already failed, ignore this 1.723 + return NS_OK; 1.724 + } 1.725 + 1.726 + if (!manifest->mManifestHashInitialized) { 1.727 + // Avoid re-creation of crypto hash when it fails from some reason the first time 1.728 + manifest->mManifestHashInitialized = true; 1.729 + 1.730 + manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); 1.731 + if (NS_SUCCEEDED(rv)) { 1.732 + rv = manifest->mManifestHash->Init(nsICryptoHash::MD5); 1.733 + if (NS_FAILED(rv)) { 1.734 + manifest->mManifestHash = nullptr; 1.735 + LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08x", rv)); 1.736 + } 1.737 + } 1.738 + } 1.739 + 1.740 + if (manifest->mManifestHash) { 1.741 + rv = manifest->mManifestHash->Update(reinterpret_cast<const uint8_t *>(aFromSegment), aCount); 1.742 + if (NS_FAILED(rv)) { 1.743 + manifest->mManifestHash = nullptr; 1.744 + LOG(("Could not update manifest hash, rv=%08x", rv)); 1.745 + } 1.746 + } 1.747 + 1.748 + manifest->mReadBuf.Append(aFromSegment, aCount); 1.749 + 1.750 + nsCString::const_iterator begin, iter, end; 1.751 + manifest->mReadBuf.BeginReading(begin); 1.752 + manifest->mReadBuf.EndReading(end); 1.753 + 1.754 + for (iter = begin; iter != end; iter++) { 1.755 + if (*iter == '\r' || *iter == '\n') { 1.756 + nsresult rv = manifest->HandleManifestLine(begin, iter); 1.757 + 1.758 + if (NS_FAILED(rv)) { 1.759 + LOG(("HandleManifestLine failed with 0x%08x", rv)); 1.760 + *aBytesConsumed = 0; // Avoid assertion failure in stream tee 1.761 + return NS_ERROR_ABORT; 1.762 + } 1.763 + 1.764 + begin = iter; 1.765 + begin++; 1.766 + } 1.767 + } 1.768 + 1.769 + // any leftovers are saved for next time 1.770 + manifest->mReadBuf = Substring(begin, end); 1.771 + 1.772 + return NS_OK; 1.773 +} 1.774 + 1.775 +nsresult 1.776 +nsOfflineManifestItem::AddNamespace(uint32_t namespaceType, 1.777 + const nsCString &namespaceSpec, 1.778 + const nsCString &data) 1.779 + 1.780 +{ 1.781 + nsresult rv; 1.782 + if (!mNamespaces) { 1.783 + mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 1.784 + NS_ENSURE_SUCCESS(rv, rv); 1.785 + } 1.786 + 1.787 + nsCOMPtr<nsIApplicationCacheNamespace> ns = 1.788 + do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv); 1.789 + NS_ENSURE_SUCCESS(rv, rv); 1.790 + 1.791 + rv = ns->Init(namespaceType, namespaceSpec, data); 1.792 + NS_ENSURE_SUCCESS(rv, rv); 1.793 + 1.794 + rv = mNamespaces->AppendElement(ns, false); 1.795 + NS_ENSURE_SUCCESS(rv, rv); 1.796 + 1.797 + return NS_OK; 1.798 +} 1.799 + 1.800 +nsresult 1.801 +nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin, 1.802 + const nsCString::const_iterator &aEnd) 1.803 +{ 1.804 + nsCString::const_iterator begin = aBegin; 1.805 + nsCString::const_iterator end = aEnd; 1.806 + 1.807 + // all lines ignore trailing spaces and tabs 1.808 + nsCString::const_iterator last = end; 1.809 + --last; 1.810 + while (end != begin && (*last == ' ' || *last == '\t')) { 1.811 + --end; 1.812 + --last; 1.813 + } 1.814 + 1.815 + if (mParserState == PARSE_INIT) { 1.816 + // Allow a UTF-8 BOM 1.817 + if (begin != end && static_cast<unsigned char>(*begin) == 0xef) { 1.818 + if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb || 1.819 + ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) { 1.820 + mParserState = PARSE_ERROR; 1.821 + LogToConsole("Offline cache manifest BOM error", this); 1.822 + return NS_OK; 1.823 + } 1.824 + ++begin; 1.825 + } 1.826 + 1.827 + const nsCSubstring &magic = Substring(begin, end); 1.828 + 1.829 + if (!magic.EqualsLiteral("CACHE MANIFEST")) { 1.830 + mParserState = PARSE_ERROR; 1.831 + LogToConsole("Offline cache manifest magic incorect", this); 1.832 + return NS_OK; 1.833 + } 1.834 + 1.835 + mParserState = PARSE_CACHE_ENTRIES; 1.836 + return NS_OK; 1.837 + } 1.838 + 1.839 + // lines other than the first ignore leading spaces and tabs 1.840 + while (begin != end && (*begin == ' ' || *begin == '\t')) 1.841 + begin++; 1.842 + 1.843 + // ignore blank lines and comments 1.844 + if (begin == end || *begin == '#') 1.845 + return NS_OK; 1.846 + 1.847 + const nsCSubstring &line = Substring(begin, end); 1.848 + 1.849 + if (line.EqualsLiteral("CACHE:")) { 1.850 + mParserState = PARSE_CACHE_ENTRIES; 1.851 + return NS_OK; 1.852 + } 1.853 + 1.854 + if (line.EqualsLiteral("FALLBACK:")) { 1.855 + mParserState = PARSE_FALLBACK_ENTRIES; 1.856 + return NS_OK; 1.857 + } 1.858 + 1.859 + if (line.EqualsLiteral("NETWORK:")) { 1.860 + mParserState = PARSE_BYPASS_ENTRIES; 1.861 + return NS_OK; 1.862 + } 1.863 + 1.864 + // Every other section type we don't know must be silently ignored. 1.865 + nsCString::const_iterator lastChar = end; 1.866 + if (*(--lastChar) == ':') { 1.867 + mParserState = PARSE_UNKNOWN_SECTION; 1.868 + return NS_OK; 1.869 + } 1.870 + 1.871 + nsresult rv; 1.872 + 1.873 + switch(mParserState) { 1.874 + case PARSE_INIT: 1.875 + case PARSE_ERROR: { 1.876 + // this should have been dealt with earlier 1.877 + return NS_ERROR_FAILURE; 1.878 + } 1.879 + 1.880 + case PARSE_UNKNOWN_SECTION: { 1.881 + // just jump over 1.882 + return NS_OK; 1.883 + } 1.884 + 1.885 + case PARSE_CACHE_ENTRIES: { 1.886 + nsCOMPtr<nsIURI> uri; 1.887 + rv = NS_NewURI(getter_AddRefs(uri), line, nullptr, mURI); 1.888 + if (NS_FAILED(rv)) 1.889 + break; 1.890 + if (NS_FAILED(DropReferenceFromURL(uri))) 1.891 + break; 1.892 + 1.893 + nsAutoCString scheme; 1.894 + uri->GetScheme(scheme); 1.895 + 1.896 + // Manifest URIs must have the same scheme as the manifest. 1.897 + bool match; 1.898 + if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match) 1.899 + break; 1.900 + 1.901 + mExplicitURIs.AppendObject(uri); 1.902 + break; 1.903 + } 1.904 + 1.905 + case PARSE_FALLBACK_ENTRIES: { 1.906 + int32_t separator = line.FindChar(' '); 1.907 + if (separator == kNotFound) { 1.908 + separator = line.FindChar('\t'); 1.909 + if (separator == kNotFound) 1.910 + break; 1.911 + } 1.912 + 1.913 + nsCString namespaceSpec(Substring(line, 0, separator)); 1.914 + nsCString fallbackSpec(Substring(line, separator + 1)); 1.915 + namespaceSpec.CompressWhitespace(); 1.916 + fallbackSpec.CompressWhitespace(); 1.917 + 1.918 + nsCOMPtr<nsIURI> namespaceURI; 1.919 + rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nullptr, mURI); 1.920 + if (NS_FAILED(rv)) 1.921 + break; 1.922 + if (NS_FAILED(DropReferenceFromURL(namespaceURI))) 1.923 + break; 1.924 + rv = namespaceURI->GetAsciiSpec(namespaceSpec); 1.925 + if (NS_FAILED(rv)) 1.926 + break; 1.927 + 1.928 + 1.929 + nsCOMPtr<nsIURI> fallbackURI; 1.930 + rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nullptr, mURI); 1.931 + if (NS_FAILED(rv)) 1.932 + break; 1.933 + if (NS_FAILED(DropReferenceFromURL(fallbackURI))) 1.934 + break; 1.935 + rv = fallbackURI->GetAsciiSpec(fallbackSpec); 1.936 + if (NS_FAILED(rv)) 1.937 + break; 1.938 + 1.939 + // Manifest and namespace must be same origin 1.940 + if (!NS_SecurityCompareURIs(mURI, namespaceURI, 1.941 + mStrictFileOriginPolicy)) 1.942 + break; 1.943 + 1.944 + // Fallback and namespace must be same origin 1.945 + if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI, 1.946 + mStrictFileOriginPolicy)) 1.947 + break; 1.948 + 1.949 + mFallbackURIs.AppendObject(fallbackURI); 1.950 + 1.951 + AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK, 1.952 + namespaceSpec, fallbackSpec); 1.953 + break; 1.954 + } 1.955 + 1.956 + case PARSE_BYPASS_ENTRIES: { 1.957 + if (line[0] == '*' && (line.Length() == 1 || line[1] == ' ' || line[1] == '\t')) 1.958 + { 1.959 + // '*' indicates to make the online whitelist wildcard flag open, 1.960 + // i.e. do allow load of resources not present in the offline cache 1.961 + // or not conforming any namespace. 1.962 + // We achive that simply by adding an 'empty' - i.e. universal 1.963 + // namespace of BYPASS type into the cache. 1.964 + AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS, 1.965 + EmptyCString(), EmptyCString()); 1.966 + break; 1.967 + } 1.968 + 1.969 + nsCOMPtr<nsIURI> bypassURI; 1.970 + rv = NS_NewURI(getter_AddRefs(bypassURI), line, nullptr, mURI); 1.971 + if (NS_FAILED(rv)) 1.972 + break; 1.973 + 1.974 + nsAutoCString scheme; 1.975 + bypassURI->GetScheme(scheme); 1.976 + bool equals; 1.977 + if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals) 1.978 + break; 1.979 + if (NS_FAILED(DropReferenceFromURL(bypassURI))) 1.980 + break; 1.981 + nsCString spec; 1.982 + if (NS_FAILED(bypassURI->GetAsciiSpec(spec))) 1.983 + break; 1.984 + 1.985 + AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS, 1.986 + spec, EmptyCString()); 1.987 + break; 1.988 + } 1.989 + } 1.990 + 1.991 + return NS_OK; 1.992 +} 1.993 + 1.994 +nsresult 1.995 +nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest) 1.996 +{ 1.997 + nsresult rv; 1.998 + 1.999 + nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv); 1.1000 + NS_ENSURE_SUCCESS(rv, rv); 1.1001 + 1.1002 + // load the main cache token that is actually the old offline cache token and 1.1003 + // read previous manifest content hash value 1.1004 + nsCOMPtr<nsISupports> cacheToken; 1.1005 + cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); 1.1006 + if (cacheToken) { 1.1007 + nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv)); 1.1008 + NS_ENSURE_SUCCESS(rv, rv); 1.1009 + 1.1010 + rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue)); 1.1011 + if (NS_FAILED(rv)) 1.1012 + mOldManifestHashValue.Truncate(); 1.1013 + } 1.1014 + 1.1015 + return NS_OK; 1.1016 +} 1.1017 + 1.1018 +nsresult 1.1019 +nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest) 1.1020 +{ 1.1021 + nsresult rv; 1.1022 + 1.1023 + if (!mManifestHash) { 1.1024 + // Nothing to compare against... 1.1025 + return NS_OK; 1.1026 + } 1.1027 + 1.1028 + nsCString newManifestHashValue; 1.1029 + rv = mManifestHash->Finish(true, mManifestHashValue); 1.1030 + mManifestHash = nullptr; 1.1031 + 1.1032 + if (NS_FAILED(rv)) { 1.1033 + LOG(("Could not finish manifest hash, rv=%08x", rv)); 1.1034 + // This is not critical error 1.1035 + return NS_OK; 1.1036 + } 1.1037 + 1.1038 + if (!ParseSucceeded()) { 1.1039 + // Parsing failed, the hash is not valid 1.1040 + return NS_OK; 1.1041 + } 1.1042 + 1.1043 + if (mOldManifestHashValue == mManifestHashValue) { 1.1044 + LOG(("Update not needed, downloaded manifest content is byte-for-byte identical")); 1.1045 + mNeedsUpdate = false; 1.1046 + } 1.1047 + 1.1048 + // Store the manifest content hash value to the new 1.1049 + // offline cache token 1.1050 + nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv); 1.1051 + NS_ENSURE_SUCCESS(rv, rv); 1.1052 + 1.1053 + nsCOMPtr<nsISupports> cacheToken; 1.1054 + cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken)); 1.1055 + if (cacheToken) { 1.1056 + nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv)); 1.1057 + NS_ENSURE_SUCCESS(rv, rv); 1.1058 + 1.1059 + rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get()); 1.1060 + NS_ENSURE_SUCCESS(rv, rv); 1.1061 + } 1.1062 + 1.1063 + return NS_OK; 1.1064 +} 1.1065 + 1.1066 +void 1.1067 +nsOfflineManifestItem::ReadStrictFileOriginPolicyPref() 1.1068 +{ 1.1069 + mStrictFileOriginPolicy = 1.1070 + Preferences::GetBool("security.fileuri.strict_origin_policy", true); 1.1071 +} 1.1072 + 1.1073 +NS_IMETHODIMP 1.1074 +nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest, 1.1075 + nsISupports *aContext) 1.1076 +{ 1.1077 + nsresult rv; 1.1078 + 1.1079 + nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv); 1.1080 + NS_ENSURE_SUCCESS(rv, rv); 1.1081 + 1.1082 + bool succeeded; 1.1083 + rv = channel->GetRequestSucceeded(&succeeded); 1.1084 + NS_ENSURE_SUCCESS(rv, rv); 1.1085 + 1.1086 + if (!succeeded) { 1.1087 + LOG(("HTTP request failed")); 1.1088 + LogToConsole("Offline cache manifest HTTP request failed", this); 1.1089 + mParserState = PARSE_ERROR; 1.1090 + return NS_ERROR_ABORT; 1.1091 + } 1.1092 + 1.1093 + rv = GetOldManifestContentHash(aRequest); 1.1094 + NS_ENSURE_SUCCESS(rv, rv); 1.1095 + 1.1096 + return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext); 1.1097 +} 1.1098 + 1.1099 +NS_IMETHODIMP 1.1100 +nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest, 1.1101 + nsISupports *aContext, 1.1102 + nsIInputStream *aStream, 1.1103 + uint64_t aOffset, 1.1104 + uint32_t aCount) 1.1105 +{ 1.1106 + uint32_t bytesRead = 0; 1.1107 + aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead); 1.1108 + mBytesRead += bytesRead; 1.1109 + 1.1110 + if (mParserState == PARSE_ERROR) { 1.1111 + LOG(("OnDataAvailable is canceling the request due a parse error\n")); 1.1112 + return NS_ERROR_ABORT; 1.1113 + } 1.1114 + 1.1115 + LOG(("loaded %u bytes into offline cache [offset=%u]\n", 1.1116 + bytesRead, aOffset)); 1.1117 + 1.1118 + // All the parent method does is read and discard, don't bother 1.1119 + // chaining up. 1.1120 + 1.1121 + return NS_OK; 1.1122 +} 1.1123 + 1.1124 +NS_IMETHODIMP 1.1125 +nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest, 1.1126 + nsISupports *aContext, 1.1127 + nsresult aStatus) 1.1128 +{ 1.1129 + if (mBytesRead == 0) { 1.1130 + // We didn't need to read (because LOAD_ONLY_IF_MODIFIED was 1.1131 + // specified). 1.1132 + mNeedsUpdate = false; 1.1133 + } else { 1.1134 + // Handle any leftover manifest data. 1.1135 + nsCString::const_iterator begin, end; 1.1136 + mReadBuf.BeginReading(begin); 1.1137 + mReadBuf.EndReading(end); 1.1138 + nsresult rv = HandleManifestLine(begin, end); 1.1139 + NS_ENSURE_SUCCESS(rv, rv); 1.1140 + 1.1141 + rv = CheckNewManifestContentHash(aRequest); 1.1142 + NS_ENSURE_SUCCESS(rv, rv); 1.1143 + } 1.1144 + 1.1145 + return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus); 1.1146 +} 1.1147 + 1.1148 +//----------------------------------------------------------------------------- 1.1149 +// nsOfflineCacheUpdate::nsISupports 1.1150 +//----------------------------------------------------------------------------- 1.1151 + 1.1152 +NS_IMPL_ISUPPORTS(nsOfflineCacheUpdate, 1.1153 + nsIOfflineCacheUpdateObserver, 1.1154 + nsIOfflineCacheUpdate, 1.1155 + nsIRunnable) 1.1156 + 1.1157 +//----------------------------------------------------------------------------- 1.1158 +// nsOfflineCacheUpdate <public> 1.1159 +//----------------------------------------------------------------------------- 1.1160 + 1.1161 +nsOfflineCacheUpdate::nsOfflineCacheUpdate() 1.1162 + : mState(STATE_UNINITIALIZED) 1.1163 + , mAddedItems(false) 1.1164 + , mPartialUpdate(false) 1.1165 + , mOnlyCheckUpdate(false) 1.1166 + , mSucceeded(true) 1.1167 + , mObsolete(false) 1.1168 + , mAppID(NECKO_NO_APP_ID) 1.1169 + , mInBrowser(false) 1.1170 + , mItemsInProgress(0) 1.1171 + , mRescheduleCount(0) 1.1172 + , mPinnedEntryRetriesCount(0) 1.1173 + , mPinned(false) 1.1174 +{ 1.1175 +} 1.1176 + 1.1177 +nsOfflineCacheUpdate::~nsOfflineCacheUpdate() 1.1178 +{ 1.1179 + LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this)); 1.1180 +} 1.1181 + 1.1182 +/* static */ 1.1183 +nsresult 1.1184 +nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey) 1.1185 +{ 1.1186 + aKey.Truncate(); 1.1187 + 1.1188 + nsCOMPtr<nsIURI> newURI; 1.1189 + nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(newURI)); 1.1190 + NS_ENSURE_SUCCESS(rv, rv); 1.1191 + 1.1192 + rv = newURI->GetAsciiSpec(aKey); 1.1193 + NS_ENSURE_SUCCESS(rv, rv); 1.1194 + 1.1195 + return NS_OK; 1.1196 +} 1.1197 + 1.1198 +nsresult 1.1199 +nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI) 1.1200 +{ 1.1201 + nsresult rv; 1.1202 + 1.1203 + // Only http and https applications are supported. 1.1204 + bool match; 1.1205 + rv = aManifestURI->SchemeIs("http", &match); 1.1206 + NS_ENSURE_SUCCESS(rv, rv); 1.1207 + 1.1208 + if (!match) { 1.1209 + rv = aManifestURI->SchemeIs("https", &match); 1.1210 + NS_ENSURE_SUCCESS(rv, rv); 1.1211 + if (!match) 1.1212 + return NS_ERROR_ABORT; 1.1213 + } 1.1214 + 1.1215 + mManifestURI = aManifestURI; 1.1216 + 1.1217 + rv = mManifestURI->GetAsciiHost(mUpdateDomain); 1.1218 + NS_ENSURE_SUCCESS(rv, rv); 1.1219 + 1.1220 + mPartialUpdate = false; 1.1221 + 1.1222 + return NS_OK; 1.1223 +} 1.1224 + 1.1225 +nsresult 1.1226 +nsOfflineCacheUpdate::Init(nsIURI *aManifestURI, 1.1227 + nsIURI *aDocumentURI, 1.1228 + nsIDOMDocument *aDocument, 1.1229 + nsIFile *aCustomProfileDir, 1.1230 + uint32_t aAppID, 1.1231 + bool aInBrowser) 1.1232 +{ 1.1233 + nsresult rv; 1.1234 + 1.1235 + // Make sure the service has been initialized 1.1236 + nsOfflineCacheUpdateService* service = 1.1237 + nsOfflineCacheUpdateService::EnsureService(); 1.1238 + if (!service) 1.1239 + return NS_ERROR_FAILURE; 1.1240 + 1.1241 + LOG(("nsOfflineCacheUpdate::Init [%p]", this)); 1.1242 + 1.1243 + rv = InitInternal(aManifestURI); 1.1244 + NS_ENSURE_SUCCESS(rv, rv); 1.1245 + 1.1246 + nsCOMPtr<nsIApplicationCacheService> cacheService = 1.1247 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.1248 + NS_ENSURE_SUCCESS(rv, rv); 1.1249 + 1.1250 + mDocumentURI = aDocumentURI; 1.1251 + 1.1252 + if (aCustomProfileDir) { 1.1253 + rv = cacheService->BuildGroupIDForApp(aManifestURI, 1.1254 + aAppID, aInBrowser, 1.1255 + mGroupID); 1.1256 + NS_ENSURE_SUCCESS(rv, rv); 1.1257 + 1.1258 + // Create only a new offline application cache in the custom profile 1.1259 + // This is a preload of a new cache. 1.1260 + 1.1261 + // XXX Custom updates don't support "updating" of an existing cache 1.1262 + // in the custom profile at the moment. This support can be, though, 1.1263 + // simply added as well when needed. 1.1264 + mPreviousApplicationCache = nullptr; 1.1265 + 1.1266 + rv = cacheService->CreateCustomApplicationCache(mGroupID, 1.1267 + aCustomProfileDir, 1.1268 + kCustomProfileQuota, 1.1269 + getter_AddRefs(mApplicationCache)); 1.1270 + NS_ENSURE_SUCCESS(rv, rv); 1.1271 + 1.1272 + mCustomProfileDir = aCustomProfileDir; 1.1273 + } 1.1274 + else { 1.1275 + rv = cacheService->BuildGroupIDForApp(aManifestURI, 1.1276 + aAppID, aInBrowser, 1.1277 + mGroupID); 1.1278 + NS_ENSURE_SUCCESS(rv, rv); 1.1279 + 1.1280 + rv = cacheService->GetActiveCache(mGroupID, 1.1281 + getter_AddRefs(mPreviousApplicationCache)); 1.1282 + NS_ENSURE_SUCCESS(rv, rv); 1.1283 + 1.1284 + rv = cacheService->CreateApplicationCache(mGroupID, 1.1285 + getter_AddRefs(mApplicationCache)); 1.1286 + NS_ENSURE_SUCCESS(rv, rv); 1.1287 + } 1.1288 + 1.1289 + rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI, 1.1290 + nullptr, 1.1291 + &mPinned); 1.1292 + NS_ENSURE_SUCCESS(rv, rv); 1.1293 + 1.1294 + mAppID = aAppID; 1.1295 + mInBrowser = aInBrowser; 1.1296 + 1.1297 + mState = STATE_INITIALIZED; 1.1298 + return NS_OK; 1.1299 +} 1.1300 + 1.1301 +nsresult 1.1302 +nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI, 1.1303 + uint32_t aAppID, 1.1304 + bool aInBrowser, 1.1305 + nsIObserver *aObserver) 1.1306 +{ 1.1307 + nsresult rv; 1.1308 + 1.1309 + // Make sure the service has been initialized 1.1310 + nsOfflineCacheUpdateService* service = 1.1311 + nsOfflineCacheUpdateService::EnsureService(); 1.1312 + if (!service) 1.1313 + return NS_ERROR_FAILURE; 1.1314 + 1.1315 + LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this)); 1.1316 + 1.1317 + rv = InitInternal(aManifestURI); 1.1318 + NS_ENSURE_SUCCESS(rv, rv); 1.1319 + 1.1320 + nsCOMPtr<nsIApplicationCacheService> cacheService = 1.1321 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.1322 + NS_ENSURE_SUCCESS(rv, rv); 1.1323 + 1.1324 + rv = cacheService->BuildGroupIDForApp(aManifestURI, 1.1325 + aAppID, aInBrowser, 1.1326 + mGroupID); 1.1327 + NS_ENSURE_SUCCESS(rv, rv); 1.1328 + 1.1329 + rv = cacheService->GetActiveCache(mGroupID, 1.1330 + getter_AddRefs(mPreviousApplicationCache)); 1.1331 + NS_ENSURE_SUCCESS(rv, rv); 1.1332 + 1.1333 + // To load the manifest properly using current app cache to satisfy and 1.1334 + // also to compare the cached content hash value we have to set 'some' 1.1335 + // app cache to write to on the channel. Otherwise the cached version will 1.1336 + // be used and no actual network request will be made. We use the same 1.1337 + // app cache here. OpenChannel prevents caching in this case using 1.1338 + // INHIBIT_CACHING load flag. 1.1339 + mApplicationCache = mPreviousApplicationCache; 1.1340 + 1.1341 + rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI, 1.1342 + nullptr, 1.1343 + &mPinned); 1.1344 + NS_ENSURE_SUCCESS(rv, rv); 1.1345 + 1.1346 + mUpdateAvailableObserver = aObserver; 1.1347 + mOnlyCheckUpdate = true; 1.1348 + 1.1349 + mState = STATE_INITIALIZED; 1.1350 + return NS_OK; 1.1351 +} 1.1352 + 1.1353 +nsresult 1.1354 +nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI, 1.1355 + const nsACString& clientID, 1.1356 + nsIURI *aDocumentURI) 1.1357 +{ 1.1358 + nsresult rv; 1.1359 + 1.1360 + // Make sure the service has been initialized 1.1361 + nsOfflineCacheUpdateService* service = 1.1362 + nsOfflineCacheUpdateService::EnsureService(); 1.1363 + if (!service) 1.1364 + return NS_ERROR_FAILURE; 1.1365 + 1.1366 + LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this)); 1.1367 + 1.1368 + mPartialUpdate = true; 1.1369 + mDocumentURI = aDocumentURI; 1.1370 + 1.1371 + mManifestURI = aManifestURI; 1.1372 + rv = mManifestURI->GetAsciiHost(mUpdateDomain); 1.1373 + NS_ENSURE_SUCCESS(rv, rv); 1.1374 + 1.1375 + nsCOMPtr<nsIApplicationCacheService> cacheService = 1.1376 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.1377 + NS_ENSURE_SUCCESS(rv, rv); 1.1378 + 1.1379 + rv = cacheService->GetApplicationCache(clientID, 1.1380 + getter_AddRefs(mApplicationCache)); 1.1381 + NS_ENSURE_SUCCESS(rv, rv); 1.1382 + 1.1383 + if (!mApplicationCache) { 1.1384 + nsAutoCString manifestSpec; 1.1385 + rv = GetCacheKey(mManifestURI, manifestSpec); 1.1386 + NS_ENSURE_SUCCESS(rv, rv); 1.1387 + 1.1388 + rv = cacheService->CreateApplicationCache 1.1389 + (manifestSpec, getter_AddRefs(mApplicationCache)); 1.1390 + NS_ENSURE_SUCCESS(rv, rv); 1.1391 + } 1.1392 + 1.1393 + rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI)); 1.1394 + NS_ENSURE_SUCCESS(rv, rv); 1.1395 + 1.1396 + nsAutoCString groupID; 1.1397 + rv = mApplicationCache->GetGroupID(groupID); 1.1398 + NS_ENSURE_SUCCESS(rv, rv); 1.1399 + 1.1400 + rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI, 1.1401 + nullptr, 1.1402 + &mPinned); 1.1403 + NS_ENSURE_SUCCESS(rv, rv); 1.1404 + 1.1405 + mState = STATE_INITIALIZED; 1.1406 + return NS_OK; 1.1407 +} 1.1408 + 1.1409 +nsresult 1.1410 +nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate) 1.1411 +{ 1.1412 + // Be pessimistic 1.1413 + *aDoUpdate = false; 1.1414 + 1.1415 + bool succeeded; 1.1416 + nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded); 1.1417 + NS_ENSURE_SUCCESS(rv, rv); 1.1418 + 1.1419 + if (!succeeded || !mManifestItem->ParseSucceeded()) { 1.1420 + return NS_ERROR_FAILURE; 1.1421 + } 1.1422 + 1.1423 + if (!mManifestItem->NeedsUpdate()) { 1.1424 + return NS_OK; 1.1425 + } 1.1426 + 1.1427 + // Add items requested by the manifest. 1.1428 + const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs(); 1.1429 + for (int32_t i = 0; i < manifestURIs.Count(); i++) { 1.1430 + rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT); 1.1431 + NS_ENSURE_SUCCESS(rv, rv); 1.1432 + } 1.1433 + 1.1434 + const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs(); 1.1435 + for (int32_t i = 0; i < fallbackURIs.Count(); i++) { 1.1436 + rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK); 1.1437 + NS_ENSURE_SUCCESS(rv, rv); 1.1438 + } 1.1439 + 1.1440 + // The document that requested the manifest is implicitly included 1.1441 + // as part of that manifest update. 1.1442 + rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT); 1.1443 + NS_ENSURE_SUCCESS(rv, rv); 1.1444 + 1.1445 + // Add items previously cached implicitly 1.1446 + rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT); 1.1447 + NS_ENSURE_SUCCESS(rv, rv); 1.1448 + 1.1449 + // Add items requested by the script API 1.1450 + rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC); 1.1451 + NS_ENSURE_SUCCESS(rv, rv); 1.1452 + 1.1453 + // Add opportunistically cached items conforming current opportunistic 1.1454 + // namespace list 1.1455 + rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC, 1.1456 + &mManifestItem->GetOpportunisticNamespaces()); 1.1457 + NS_ENSURE_SUCCESS(rv, rv); 1.1458 + 1.1459 + *aDoUpdate = true; 1.1460 + 1.1461 + return NS_OK; 1.1462 +} 1.1463 + 1.1464 +bool 1.1465 +nsOfflineCacheUpdate::CheckUpdateAvailability() 1.1466 +{ 1.1467 + nsresult rv; 1.1468 + 1.1469 + bool succeeded; 1.1470 + rv = mManifestItem->GetRequestSucceeded(&succeeded); 1.1471 + NS_ENSURE_SUCCESS(rv, false); 1.1472 + 1.1473 + if (!succeeded || !mManifestItem->ParseSucceeded()) { 1.1474 + return false; 1.1475 + } 1.1476 + 1.1477 + if (!mPinned) { 1.1478 + uint16_t status; 1.1479 + rv = mManifestItem->GetStatus(&status); 1.1480 + NS_ENSURE_SUCCESS(rv, false); 1.1481 + 1.1482 + // Treat these as there would be an update available, 1.1483 + // since this is indication of demand to remove this 1.1484 + // offline cache. 1.1485 + if (status == 404 || status == 410) { 1.1486 + return true; 1.1487 + } 1.1488 + } 1.1489 + 1.1490 + return mManifestItem->NeedsUpdate(); 1.1491 +} 1.1492 + 1.1493 +void 1.1494 +nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem) 1.1495 +{ 1.1496 + nsresult rv; 1.1497 + 1.1498 + LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this)); 1.1499 + 1.1500 + if (mState == STATE_FINISHED) { 1.1501 + LOG((" after completion, ignoring")); 1.1502 + return; 1.1503 + } 1.1504 + 1.1505 + // Keep the object alive through a Finish() call. 1.1506 + nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); 1.1507 + 1.1508 + if (mState == STATE_CANCELLED) { 1.1509 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1510 + Finish(); 1.1511 + return; 1.1512 + } 1.1513 + 1.1514 + if (mState == STATE_CHECKING) { 1.1515 + // Manifest load finished. 1.1516 + 1.1517 + if (mOnlyCheckUpdate) { 1.1518 + Finish(); 1.1519 + NotifyUpdateAvailability(CheckUpdateAvailability()); 1.1520 + return; 1.1521 + } 1.1522 + 1.1523 + NS_ASSERTION(mManifestItem, 1.1524 + "Must have a manifest item in STATE_CHECKING."); 1.1525 + NS_ASSERTION(mManifestItem == aItem, 1.1526 + "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted"); 1.1527 + 1.1528 + // A 404 or 410 is interpreted as an intentional removal of 1.1529 + // the manifest file, rather than a transient server error. 1.1530 + // Obsolete this cache group if one of these is returned. 1.1531 + uint16_t status; 1.1532 + rv = mManifestItem->GetStatus(&status); 1.1533 + if (status == 404 || status == 410) { 1.1534 + LogToConsole("Offline cache manifest removed, cache cleared", mManifestItem); 1.1535 + mSucceeded = false; 1.1536 + if (mPreviousApplicationCache) { 1.1537 + if (mPinned) { 1.1538 + // Do not obsolete a pinned application. 1.1539 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); 1.1540 + } else { 1.1541 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE); 1.1542 + mObsolete = true; 1.1543 + } 1.1544 + } else { 1.1545 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1546 + mObsolete = true; 1.1547 + } 1.1548 + Finish(); 1.1549 + return; 1.1550 + } 1.1551 + 1.1552 + bool doUpdate; 1.1553 + if (NS_FAILED(HandleManifest(&doUpdate))) { 1.1554 + mSucceeded = false; 1.1555 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1556 + Finish(); 1.1557 + return; 1.1558 + } 1.1559 + 1.1560 + if (!doUpdate) { 1.1561 + LogToConsole("Offline cache doesn't need to update", mManifestItem); 1.1562 + 1.1563 + mSucceeded = false; 1.1564 + 1.1565 + AssociateDocuments(mPreviousApplicationCache); 1.1566 + 1.1567 + ScheduleImplicit(); 1.1568 + 1.1569 + // If we didn't need an implicit update, we can 1.1570 + // send noupdate and end the update now. 1.1571 + if (!mImplicitUpdate) { 1.1572 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); 1.1573 + Finish(); 1.1574 + } 1.1575 + return; 1.1576 + } 1.1577 + 1.1578 + rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey, 1.1579 + mManifestItem->mItemType); 1.1580 + if (NS_FAILED(rv)) { 1.1581 + mSucceeded = false; 1.1582 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1583 + Finish(); 1.1584 + return; 1.1585 + } 1.1586 + 1.1587 + mState = STATE_DOWNLOADING; 1.1588 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); 1.1589 + 1.1590 + // Start fetching resources. 1.1591 + ProcessNextURI(); 1.1592 + 1.1593 + return; 1.1594 + } 1.1595 + 1.1596 + // Normal load finished. 1.1597 + if (mItemsInProgress) // Just to be safe here! 1.1598 + --mItemsInProgress; 1.1599 + 1.1600 + bool succeeded; 1.1601 + rv = aItem->GetRequestSucceeded(&succeeded); 1.1602 + 1.1603 + if (mPinned && NS_SUCCEEDED(rv) && succeeded) { 1.1604 + uint32_t dummy_cache_type; 1.1605 + rv = mApplicationCache->GetTypes(aItem->mCacheKey, &dummy_cache_type); 1.1606 + bool item_doomed = NS_FAILED(rv); // can not find it? -> doomed 1.1607 + 1.1608 + if (item_doomed && 1.1609 + mPinnedEntryRetriesCount < kPinnedEntryRetriesLimit && 1.1610 + (aItem->mItemType & (nsIApplicationCache::ITEM_EXPLICIT | 1.1611 + nsIApplicationCache::ITEM_FALLBACK))) { 1.1612 + rv = EvictOneNonPinned(); 1.1613 + if (NS_FAILED(rv)) { 1.1614 + mSucceeded = false; 1.1615 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1616 + Finish(); 1.1617 + return; 1.1618 + } 1.1619 + 1.1620 + // This reverts the item state to UNINITIALIZED that makes it to 1.1621 + // be scheduled for download again. 1.1622 + rv = aItem->Cancel(); 1.1623 + if (NS_FAILED(rv)) { 1.1624 + mSucceeded = false; 1.1625 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1626 + Finish(); 1.1627 + return; 1.1628 + } 1.1629 + 1.1630 + mPinnedEntryRetriesCount++; 1.1631 + 1.1632 + LogToConsole("An unpinned offline cache deleted"); 1.1633 + 1.1634 + // Retry this item. 1.1635 + ProcessNextURI(); 1.1636 + return; 1.1637 + } 1.1638 + } 1.1639 + 1.1640 + // According to parallelism this may imply more pinned retries count, 1.1641 + // but that is not critical, since at one moment the algoritm will 1.1642 + // stop anyway. Also, this code may soon be completely removed 1.1643 + // after we have a separate storage for pinned apps. 1.1644 + mPinnedEntryRetriesCount = 0; 1.1645 + 1.1646 + // Check for failures. 3XX, 4XX and 5XX errors on items explicitly 1.1647 + // listed in the manifest will cause the update to fail. 1.1648 + if (NS_FAILED(rv) || !succeeded) { 1.1649 + if (aItem->mItemType & 1.1650 + (nsIApplicationCache::ITEM_EXPLICIT | 1.1651 + nsIApplicationCache::ITEM_FALLBACK)) { 1.1652 + LogToConsole("Offline cache manifest item failed to load", aItem); 1.1653 + mSucceeded = false; 1.1654 + } 1.1655 + } else { 1.1656 + rv = mApplicationCache->MarkEntry(aItem->mCacheKey, aItem->mItemType); 1.1657 + if (NS_FAILED(rv)) { 1.1658 + mSucceeded = false; 1.1659 + } 1.1660 + } 1.1661 + 1.1662 + if (!mSucceeded) { 1.1663 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1664 + Finish(); 1.1665 + return; 1.1666 + } 1.1667 + 1.1668 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED); 1.1669 + 1.1670 + ProcessNextURI(); 1.1671 +} 1.1672 + 1.1673 +void 1.1674 +nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus, 1.1675 + const nsCString &aManifestHash) 1.1676 +{ 1.1677 + // Keep the object alive through a Finish() call. 1.1678 + nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); 1.1679 + 1.1680 + if (NS_SUCCEEDED(aStatus)) { 1.1681 + nsAutoCString firstManifestHash; 1.1682 + mManifestItem->GetManifestHash(firstManifestHash); 1.1683 + if (aManifestHash != firstManifestHash) { 1.1684 + LOG(("Manifest has changed during cache items download [%p]", this)); 1.1685 + LogToConsole("Offline cache manifest changed during update", mManifestItem); 1.1686 + aStatus = NS_ERROR_FAILURE; 1.1687 + } 1.1688 + } 1.1689 + 1.1690 + if (NS_FAILED(aStatus)) { 1.1691 + mSucceeded = false; 1.1692 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1693 + } 1.1694 + 1.1695 + if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) { 1.1696 + // Do the final stuff but prevent notification of STATE_FINISHED. 1.1697 + // That would disconnect listeners that are responsible for document 1.1698 + // association after a successful update. Forwarding notifications 1.1699 + // from a new update through this dead update to them is absolutely 1.1700 + // correct. 1.1701 + FinishNoNotify(); 1.1702 + 1.1703 + nsRefPtr<nsOfflineCacheUpdate> newUpdate = 1.1704 + new nsOfflineCacheUpdate(); 1.1705 + // Leave aDocument argument null. Only glues and children keep 1.1706 + // document instances. 1.1707 + newUpdate->Init(mManifestURI, mDocumentURI, nullptr, 1.1708 + mCustomProfileDir, mAppID, mInBrowser); 1.1709 + 1.1710 + // In a rare case the manifest will not be modified on the next refetch 1.1711 + // transfer all master document URIs to the new update to ensure that 1.1712 + // all documents refering it will be properly cached. 1.1713 + for (int32_t i = 0; i < mDocumentURIs.Count(); i++) { 1.1714 + newUpdate->StickDocument(mDocumentURIs[i]); 1.1715 + } 1.1716 + 1.1717 + newUpdate->mRescheduleCount = mRescheduleCount + 1; 1.1718 + newUpdate->AddObserver(this, false); 1.1719 + newUpdate->Schedule(); 1.1720 + } 1.1721 + else { 1.1722 + LogToConsole("Offline cache update done", mManifestItem); 1.1723 + Finish(); 1.1724 + } 1.1725 +} 1.1726 + 1.1727 +nsresult 1.1728 +nsOfflineCacheUpdate::Begin() 1.1729 +{ 1.1730 + LOG(("nsOfflineCacheUpdate::Begin [%p]", this)); 1.1731 + 1.1732 + // Keep the object alive through a ProcessNextURI()/Finish() call. 1.1733 + nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); 1.1734 + 1.1735 + mItemsInProgress = 0; 1.1736 + 1.1737 + if (mState == STATE_CANCELLED) { 1.1738 + nsRefPtr<nsRunnableMethod<nsOfflineCacheUpdate> > errorNotification = 1.1739 + NS_NewRunnableMethod(this, 1.1740 + &nsOfflineCacheUpdate::AsyncFinishWithError); 1.1741 + nsresult rv = NS_DispatchToMainThread(errorNotification); 1.1742 + NS_ENSURE_SUCCESS(rv, rv); 1.1743 + 1.1744 + return NS_OK; 1.1745 + } 1.1746 + 1.1747 + if (mPartialUpdate) { 1.1748 + mState = STATE_DOWNLOADING; 1.1749 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); 1.1750 + ProcessNextURI(); 1.1751 + return NS_OK; 1.1752 + } 1.1753 + 1.1754 + // Start checking the manifest. 1.1755 + nsCOMPtr<nsIURI> uri; 1.1756 + 1.1757 + mManifestItem = new nsOfflineManifestItem(mManifestURI, 1.1758 + mDocumentURI, 1.1759 + mApplicationCache, 1.1760 + mPreviousApplicationCache); 1.1761 + if (!mManifestItem) { 1.1762 + return NS_ERROR_OUT_OF_MEMORY; 1.1763 + } 1.1764 + 1.1765 + mState = STATE_CHECKING; 1.1766 + mByteProgress = 0; 1.1767 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING); 1.1768 + 1.1769 + nsresult rv = mManifestItem->OpenChannel(this); 1.1770 + if (NS_FAILED(rv)) { 1.1771 + LoadCompleted(mManifestItem); 1.1772 + } 1.1773 + 1.1774 + return NS_OK; 1.1775 +} 1.1776 + 1.1777 +//----------------------------------------------------------------------------- 1.1778 +// nsOfflineCacheUpdate <private> 1.1779 +//----------------------------------------------------------------------------- 1.1780 + 1.1781 +nsresult 1.1782 +nsOfflineCacheUpdate::AddExistingItems(uint32_t aType, 1.1783 + nsTArray<nsCString>* namespaceFilter) 1.1784 +{ 1.1785 + if (!mPreviousApplicationCache) { 1.1786 + return NS_OK; 1.1787 + } 1.1788 + 1.1789 + if (namespaceFilter && namespaceFilter->Length() == 0) { 1.1790 + // Don't bother to walk entries when there are no namespaces 1.1791 + // defined. 1.1792 + return NS_OK; 1.1793 + } 1.1794 + 1.1795 + uint32_t count = 0; 1.1796 + char **keys = nullptr; 1.1797 + nsresult rv = mPreviousApplicationCache->GatherEntries(aType, 1.1798 + &count, &keys); 1.1799 + NS_ENSURE_SUCCESS(rv, rv); 1.1800 + 1.1801 + AutoFreeArray autoFree(count, keys); 1.1802 + 1.1803 + for (uint32_t i = 0; i < count; i++) { 1.1804 + if (namespaceFilter) { 1.1805 + bool found = false; 1.1806 + for (uint32_t j = 0; j < namespaceFilter->Length() && !found; j++) { 1.1807 + found = StringBeginsWith(nsDependentCString(keys[i]), 1.1808 + namespaceFilter->ElementAt(j)); 1.1809 + } 1.1810 + 1.1811 + if (!found) 1.1812 + continue; 1.1813 + } 1.1814 + 1.1815 + nsCOMPtr<nsIURI> uri; 1.1816 + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) { 1.1817 + rv = AddURI(uri, aType); 1.1818 + NS_ENSURE_SUCCESS(rv, rv); 1.1819 + } 1.1820 + } 1.1821 + 1.1822 + return NS_OK; 1.1823 +} 1.1824 + 1.1825 +nsresult 1.1826 +nsOfflineCacheUpdate::ProcessNextURI() 1.1827 +{ 1.1828 + // Keep the object alive through a Finish() call. 1.1829 + nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); 1.1830 + 1.1831 + LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, inprogress=%d, numItems=%d]", 1.1832 + this, mItemsInProgress, mItems.Length())); 1.1833 + 1.1834 + if (mState != STATE_DOWNLOADING) { 1.1835 + LOG((" should only be called from the DOWNLOADING state, ignoring")); 1.1836 + return NS_ERROR_UNEXPECTED; 1.1837 + } 1.1838 + 1.1839 + nsOfflineCacheUpdateItem * runItem = nullptr; 1.1840 + uint32_t completedItems = 0; 1.1841 + for (uint32_t i = 0; i < mItems.Length(); ++i) { 1.1842 + nsOfflineCacheUpdateItem * item = mItems[i]; 1.1843 + 1.1844 + if (item->IsScheduled()) { 1.1845 + runItem = item; 1.1846 + break; 1.1847 + } 1.1848 + 1.1849 + if (item->IsCompleted()) 1.1850 + ++completedItems; 1.1851 + } 1.1852 + 1.1853 + if (completedItems == mItems.Length()) { 1.1854 + LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]: all items loaded", this)); 1.1855 + 1.1856 + if (mPartialUpdate) { 1.1857 + return Finish(); 1.1858 + } else { 1.1859 + // Verify that the manifest wasn't changed during the 1.1860 + // update, to prevent capturing a cache while the server 1.1861 + // is being updated. The check will call 1.1862 + // ManifestCheckCompleted() when it's done. 1.1863 + nsRefPtr<nsManifestCheck> manifestCheck = 1.1864 + new nsManifestCheck(this, mManifestURI, mDocumentURI); 1.1865 + if (NS_FAILED(manifestCheck->Begin())) { 1.1866 + mSucceeded = false; 1.1867 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.1868 + return Finish(); 1.1869 + } 1.1870 + 1.1871 + return NS_OK; 1.1872 + } 1.1873 + } 1.1874 + 1.1875 + if (!runItem) { 1.1876 + LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:" 1.1877 + " No more items to include in parallel load", this)); 1.1878 + return NS_OK; 1.1879 + } 1.1880 + 1.1881 +#if defined(PR_LOGGING) 1.1882 + if (LOG_ENABLED()) { 1.1883 + nsAutoCString spec; 1.1884 + runItem->mURI->GetSpec(spec); 1.1885 + LOG(("%p: Opening channel for %s", this, spec.get())); 1.1886 + } 1.1887 +#endif 1.1888 + 1.1889 + ++mItemsInProgress; 1.1890 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED); 1.1891 + 1.1892 + nsresult rv = runItem->OpenChannel(this); 1.1893 + if (NS_FAILED(rv)) { 1.1894 + LoadCompleted(runItem); 1.1895 + return rv; 1.1896 + } 1.1897 + 1.1898 + if (mItemsInProgress >= kParallelLoadLimit) { 1.1899 + LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:" 1.1900 + " At parallel load limit", this)); 1.1901 + return NS_OK; 1.1902 + } 1.1903 + 1.1904 + // This calls this method again via a post triggering 1.1905 + // a parallel item load 1.1906 + return NS_DispatchToCurrentThread(this); 1.1907 +} 1.1908 + 1.1909 +void 1.1910 +nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers) 1.1911 +{ 1.1912 + for (int32_t i = 0; i < mWeakObservers.Count(); i++) { 1.1913 + nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = 1.1914 + do_QueryReferent(mWeakObservers[i]); 1.1915 + if (observer) 1.1916 + aObservers.AppendObject(observer); 1.1917 + else 1.1918 + mWeakObservers.RemoveObjectAt(i--); 1.1919 + } 1.1920 + 1.1921 + for (int32_t i = 0; i < mObservers.Count(); i++) { 1.1922 + aObservers.AppendObject(mObservers[i]); 1.1923 + } 1.1924 +} 1.1925 + 1.1926 +void 1.1927 +nsOfflineCacheUpdate::NotifyState(uint32_t state) 1.1928 +{ 1.1929 + LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state)); 1.1930 + 1.1931 + if (state == STATE_ERROR) { 1.1932 + LogToConsole("Offline cache update error", mManifestItem); 1.1933 + } 1.1934 + 1.1935 + nsCOMArray<nsIOfflineCacheUpdateObserver> observers; 1.1936 + GatherObservers(observers); 1.1937 + 1.1938 + for (int32_t i = 0; i < observers.Count(); i++) { 1.1939 + observers[i]->UpdateStateChanged(this, state); 1.1940 + } 1.1941 +} 1.1942 + 1.1943 +void 1.1944 +nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable) 1.1945 +{ 1.1946 + if (!mUpdateAvailableObserver) 1.1947 + return; 1.1948 + 1.1949 + LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]", 1.1950 + this, updateAvailable)); 1.1951 + 1.1952 + const char* topic = updateAvailable 1.1953 + ? "offline-cache-update-available" 1.1954 + : "offline-cache-update-unavailable"; 1.1955 + 1.1956 + nsCOMPtr<nsIObserver> observer; 1.1957 + observer.swap(mUpdateAvailableObserver); 1.1958 + observer->Observe(mManifestURI, topic, nullptr); 1.1959 +} 1.1960 + 1.1961 +void 1.1962 +nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache) 1.1963 +{ 1.1964 + if (!cache) { 1.1965 + LOG(("nsOfflineCacheUpdate::AssociateDocuments bypassed" 1.1966 + ", no cache provided [this=%p]", this)); 1.1967 + return; 1.1968 + } 1.1969 + 1.1970 + nsCOMArray<nsIOfflineCacheUpdateObserver> observers; 1.1971 + GatherObservers(observers); 1.1972 + 1.1973 + for (int32_t i = 0; i < observers.Count(); i++) { 1.1974 + observers[i]->ApplicationCacheAvailable(cache); 1.1975 + } 1.1976 +} 1.1977 + 1.1978 +void 1.1979 +nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI) 1.1980 +{ 1.1981 + if (!aDocumentURI) 1.1982 + return; 1.1983 + 1.1984 + mDocumentURIs.AppendObject(aDocumentURI); 1.1985 +} 1.1986 + 1.1987 +void 1.1988 +nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner) 1.1989 +{ 1.1990 + NS_ASSERTION(!mOwner, "Tried to set cache update owner twice."); 1.1991 + mOwner = aOwner->asWeakPtr(); 1.1992 +} 1.1993 + 1.1994 +bool 1.1995 +nsOfflineCacheUpdate::IsForGroupID(const nsCSubstring &groupID) 1.1996 +{ 1.1997 + return mGroupID == groupID; 1.1998 +} 1.1999 + 1.2000 +nsresult 1.2001 +nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate) 1.2002 +{ 1.2003 + // Keep the object alive through a Finish() call. 1.2004 + nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this); 1.2005 + 1.2006 + mImplicitUpdate = nullptr; 1.2007 + 1.2008 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); 1.2009 + Finish(); 1.2010 + 1.2011 + return NS_OK; 1.2012 +} 1.2013 + 1.2014 +void 1.2015 +nsOfflineCacheUpdate::OnByteProgress(uint64_t byteIncrement) 1.2016 +{ 1.2017 + mByteProgress += byteIncrement; 1.2018 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMPROGRESS); 1.2019 +} 1.2020 + 1.2021 +nsresult 1.2022 +nsOfflineCacheUpdate::ScheduleImplicit() 1.2023 +{ 1.2024 + if (mDocumentURIs.Count() == 0) 1.2025 + return NS_OK; 1.2026 + 1.2027 + nsresult rv; 1.2028 + 1.2029 + nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate(); 1.2030 + NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); 1.2031 + 1.2032 + nsAutoCString clientID; 1.2033 + if (mPreviousApplicationCache) { 1.2034 + rv = mPreviousApplicationCache->GetClientID(clientID); 1.2035 + NS_ENSURE_SUCCESS(rv, rv); 1.2036 + } 1.2037 + else if (mApplicationCache) { 1.2038 + rv = mApplicationCache->GetClientID(clientID); 1.2039 + NS_ENSURE_SUCCESS(rv, rv); 1.2040 + } 1.2041 + else { 1.2042 + NS_ERROR("Offline cache update not having set mApplicationCache?"); 1.2043 + } 1.2044 + 1.2045 + rv = update->InitPartial(mManifestURI, clientID, mDocumentURI); 1.2046 + NS_ENSURE_SUCCESS(rv, rv); 1.2047 + 1.2048 + for (int32_t i = 0; i < mDocumentURIs.Count(); i++) { 1.2049 + rv = update->AddURI(mDocumentURIs[i], 1.2050 + nsIApplicationCache::ITEM_IMPLICIT); 1.2051 + NS_ENSURE_SUCCESS(rv, rv); 1.2052 + } 1.2053 + 1.2054 + update->SetOwner(this); 1.2055 + rv = update->Begin(); 1.2056 + NS_ENSURE_SUCCESS(rv, rv); 1.2057 + 1.2058 + mImplicitUpdate = update; 1.2059 + 1.2060 + return NS_OK; 1.2061 +} 1.2062 + 1.2063 +nsresult 1.2064 +nsOfflineCacheUpdate::FinishNoNotify() 1.2065 +{ 1.2066 + LOG(("nsOfflineCacheUpdate::Finish [%p]", this)); 1.2067 + 1.2068 + mState = STATE_FINISHED; 1.2069 + 1.2070 + if (!mPartialUpdate && !mOnlyCheckUpdate) { 1.2071 + if (mSucceeded) { 1.2072 + nsIArray *namespaces = mManifestItem->GetNamespaces(); 1.2073 + nsresult rv = mApplicationCache->AddNamespaces(namespaces); 1.2074 + if (NS_FAILED(rv)) { 1.2075 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.2076 + mSucceeded = false; 1.2077 + } 1.2078 + 1.2079 + rv = mApplicationCache->Activate(); 1.2080 + if (NS_FAILED(rv)) { 1.2081 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); 1.2082 + mSucceeded = false; 1.2083 + } 1.2084 + 1.2085 + AssociateDocuments(mApplicationCache); 1.2086 + } 1.2087 + 1.2088 + if (mObsolete) { 1.2089 + nsCOMPtr<nsIApplicationCacheService> appCacheService = 1.2090 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID); 1.2091 + if (appCacheService) { 1.2092 + nsAutoCString groupID; 1.2093 + mApplicationCache->GetGroupID(groupID); 1.2094 + appCacheService->DeactivateGroup(groupID); 1.2095 + } 1.2096 + } 1.2097 + 1.2098 + if (!mSucceeded) { 1.2099 + // Update was not merged, mark all the loads as failures 1.2100 + for (uint32_t i = 0; i < mItems.Length(); i++) { 1.2101 + mItems[i]->Cancel(); 1.2102 + } 1.2103 + 1.2104 + mApplicationCache->Discard(); 1.2105 + } 1.2106 + } 1.2107 + 1.2108 + nsresult rv = NS_OK; 1.2109 + 1.2110 + if (mOwner) { 1.2111 + rv = mOwner->UpdateFinished(this); 1.2112 + // mozilla::WeakPtr is missing some key features, like setting it to 1.2113 + // null explicitly. 1.2114 + mOwner = mozilla::WeakPtr<nsOfflineCacheUpdateOwner>(); 1.2115 + } 1.2116 + 1.2117 + return rv; 1.2118 +} 1.2119 + 1.2120 +nsresult 1.2121 +nsOfflineCacheUpdate::Finish() 1.2122 +{ 1.2123 + nsresult rv = FinishNoNotify(); 1.2124 + 1.2125 + NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED); 1.2126 + 1.2127 + return rv; 1.2128 +} 1.2129 + 1.2130 +void 1.2131 +nsOfflineCacheUpdate::AsyncFinishWithError() 1.2132 +{ 1.2133 + NotifyState(nsOfflineCacheUpdate::STATE_ERROR); 1.2134 + Finish(); 1.2135 +} 1.2136 + 1.2137 +static nsresult 1.2138 +EvictOneOfCacheGroups(nsIApplicationCacheService *cacheService, 1.2139 + uint32_t count, const char * const *groups) 1.2140 +{ 1.2141 + nsresult rv; 1.2142 + unsigned int i; 1.2143 + 1.2144 + for (i = 0; i < count; i++) { 1.2145 + nsCOMPtr<nsIURI> uri; 1.2146 + rv = NS_NewURI(getter_AddRefs(uri), groups[i]); 1.2147 + NS_ENSURE_SUCCESS(rv, rv); 1.2148 + 1.2149 + nsDependentCString group_name(groups[i]); 1.2150 + nsCOMPtr<nsIApplicationCache> cache; 1.2151 + rv = cacheService->GetActiveCache(group_name, getter_AddRefs(cache)); 1.2152 + // Maybe someone in another thread or process have deleted it. 1.2153 + if (NS_FAILED(rv) || !cache) 1.2154 + continue; 1.2155 + 1.2156 + bool pinned; 1.2157 + rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(uri, 1.2158 + nullptr, 1.2159 + &pinned); 1.2160 + NS_ENSURE_SUCCESS(rv, rv); 1.2161 + 1.2162 + if (!pinned) { 1.2163 + rv = cache->Discard(); 1.2164 + return NS_OK; 1.2165 + } 1.2166 + } 1.2167 + 1.2168 + return NS_ERROR_FILE_NOT_FOUND; 1.2169 +} 1.2170 + 1.2171 +nsresult 1.2172 +nsOfflineCacheUpdate::EvictOneNonPinned() 1.2173 +{ 1.2174 + nsresult rv; 1.2175 + 1.2176 + nsCOMPtr<nsIApplicationCacheService> cacheService = 1.2177 + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); 1.2178 + NS_ENSURE_SUCCESS(rv, rv); 1.2179 + 1.2180 + uint32_t count; 1.2181 + char **groups; 1.2182 + rv = cacheService->GetGroupsTimeOrdered(&count, &groups); 1.2183 + NS_ENSURE_SUCCESS(rv, rv); 1.2184 + 1.2185 + rv = EvictOneOfCacheGroups(cacheService, count, groups); 1.2186 + 1.2187 + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, groups); 1.2188 + return rv; 1.2189 +} 1.2190 + 1.2191 +//----------------------------------------------------------------------------- 1.2192 +// nsOfflineCacheUpdate::nsIOfflineCacheUpdate 1.2193 +//----------------------------------------------------------------------------- 1.2194 + 1.2195 +NS_IMETHODIMP 1.2196 +nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain) 1.2197 +{ 1.2198 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2199 + 1.2200 + aUpdateDomain = mUpdateDomain; 1.2201 + return NS_OK; 1.2202 +} 1.2203 + 1.2204 +NS_IMETHODIMP 1.2205 +nsOfflineCacheUpdate::GetStatus(uint16_t *aStatus) 1.2206 +{ 1.2207 + switch (mState) { 1.2208 + case STATE_CHECKING : 1.2209 + *aStatus = nsIDOMOfflineResourceList::CHECKING; 1.2210 + return NS_OK; 1.2211 + case STATE_DOWNLOADING : 1.2212 + *aStatus = nsIDOMOfflineResourceList::DOWNLOADING; 1.2213 + return NS_OK; 1.2214 + default : 1.2215 + *aStatus = nsIDOMOfflineResourceList::IDLE; 1.2216 + return NS_OK; 1.2217 + } 1.2218 + 1.2219 + return NS_ERROR_FAILURE; 1.2220 +} 1.2221 + 1.2222 +NS_IMETHODIMP 1.2223 +nsOfflineCacheUpdate::GetPartial(bool *aPartial) 1.2224 +{ 1.2225 + *aPartial = mPartialUpdate || mOnlyCheckUpdate; 1.2226 + return NS_OK; 1.2227 +} 1.2228 + 1.2229 +NS_IMETHODIMP 1.2230 +nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI) 1.2231 +{ 1.2232 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2233 + 1.2234 + NS_IF_ADDREF(*aManifestURI = mManifestURI); 1.2235 + return NS_OK; 1.2236 +} 1.2237 + 1.2238 +NS_IMETHODIMP 1.2239 +nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded) 1.2240 +{ 1.2241 + NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); 1.2242 + 1.2243 + *aSucceeded = mSucceeded; 1.2244 + 1.2245 + return NS_OK; 1.2246 +} 1.2247 + 1.2248 +NS_IMETHODIMP 1.2249 +nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade) 1.2250 +{ 1.2251 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2252 + 1.2253 + *aIsUpgrade = (mPreviousApplicationCache != nullptr); 1.2254 + 1.2255 + return NS_OK; 1.2256 +} 1.2257 + 1.2258 +nsresult 1.2259 +nsOfflineCacheUpdate::AddURI(nsIURI *aURI, uint32_t aType) 1.2260 +{ 1.2261 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2262 + 1.2263 + if (mState >= STATE_DOWNLOADING) 1.2264 + return NS_ERROR_NOT_AVAILABLE; 1.2265 + 1.2266 + // Resource URIs must have the same scheme as the manifest. 1.2267 + nsAutoCString scheme; 1.2268 + aURI->GetScheme(scheme); 1.2269 + 1.2270 + bool match; 1.2271 + if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match) 1.2272 + return NS_ERROR_FAILURE; 1.2273 + 1.2274 + // Don't fetch the same URI twice. 1.2275 + for (uint32_t i = 0; i < mItems.Length(); i++) { 1.2276 + bool equals; 1.2277 + if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) { 1.2278 + // retain both types. 1.2279 + mItems[i]->mItemType |= aType; 1.2280 + return NS_OK; 1.2281 + } 1.2282 + } 1.2283 + 1.2284 + nsRefPtr<nsOfflineCacheUpdateItem> item = 1.2285 + new nsOfflineCacheUpdateItem(aURI, 1.2286 + mDocumentURI, 1.2287 + mApplicationCache, 1.2288 + mPreviousApplicationCache, 1.2289 + aType); 1.2290 + if (!item) return NS_ERROR_OUT_OF_MEMORY; 1.2291 + 1.2292 + mItems.AppendElement(item); 1.2293 + mAddedItems = true; 1.2294 + 1.2295 + return NS_OK; 1.2296 +} 1.2297 + 1.2298 +NS_IMETHODIMP 1.2299 +nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI) 1.2300 +{ 1.2301 + if (GeckoProcessType_Default != XRE_GetProcessType()) 1.2302 + return NS_ERROR_NOT_IMPLEMENTED; 1.2303 + 1.2304 + // If this is a partial update and the resource is already in the 1.2305 + // cache, we should only mark the entry, not fetch it again. 1.2306 + if (mPartialUpdate) { 1.2307 + nsAutoCString key; 1.2308 + GetCacheKey(aURI, key); 1.2309 + 1.2310 + uint32_t types; 1.2311 + nsresult rv = mApplicationCache->GetTypes(key, &types); 1.2312 + if (NS_SUCCEEDED(rv)) { 1.2313 + if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) { 1.2314 + mApplicationCache->MarkEntry 1.2315 + (key, nsIApplicationCache::ITEM_DYNAMIC); 1.2316 + } 1.2317 + return NS_OK; 1.2318 + } 1.2319 + } 1.2320 + 1.2321 + return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC); 1.2322 +} 1.2323 + 1.2324 +NS_IMETHODIMP 1.2325 +nsOfflineCacheUpdate::Cancel() 1.2326 +{ 1.2327 + LOG(("nsOfflineCacheUpdate::Cancel [%p]", this)); 1.2328 + 1.2329 + if ((mState == STATE_FINISHED) || (mState == STATE_CANCELLED)) { 1.2330 + return NS_ERROR_NOT_AVAILABLE; 1.2331 + } 1.2332 + 1.2333 + mState = STATE_CANCELLED; 1.2334 + mSucceeded = false; 1.2335 + 1.2336 + // Cancel all running downloads 1.2337 + for (uint32_t i = 0; i < mItems.Length(); ++i) { 1.2338 + nsOfflineCacheUpdateItem * item = mItems[i]; 1.2339 + 1.2340 + if (item->IsInProgress()) 1.2341 + item->Cancel(); 1.2342 + } 1.2343 + 1.2344 + return NS_OK; 1.2345 +} 1.2346 + 1.2347 +NS_IMETHODIMP 1.2348 +nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, 1.2349 + bool aHoldWeak) 1.2350 +{ 1.2351 + LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this)); 1.2352 + 1.2353 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2354 + 1.2355 + if (aHoldWeak) { 1.2356 + nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver); 1.2357 + mWeakObservers.AppendObject(weakRef); 1.2358 + } else { 1.2359 + mObservers.AppendObject(aObserver); 1.2360 + } 1.2361 + 1.2362 + return NS_OK; 1.2363 +} 1.2364 + 1.2365 +NS_IMETHODIMP 1.2366 +nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) 1.2367 +{ 1.2368 + LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this)); 1.2369 + 1.2370 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.2371 + 1.2372 + for (int32_t i = 0; i < mWeakObservers.Count(); i++) { 1.2373 + nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = 1.2374 + do_QueryReferent(mWeakObservers[i]); 1.2375 + if (observer == aObserver) { 1.2376 + mWeakObservers.RemoveObjectAt(i); 1.2377 + return NS_OK; 1.2378 + } 1.2379 + } 1.2380 + 1.2381 + for (int32_t i = 0; i < mObservers.Count(); i++) { 1.2382 + if (mObservers[i] == aObserver) { 1.2383 + mObservers.RemoveObjectAt(i); 1.2384 + return NS_OK; 1.2385 + } 1.2386 + } 1.2387 + 1.2388 + return NS_OK; 1.2389 +} 1.2390 + 1.2391 +NS_IMETHODIMP 1.2392 +nsOfflineCacheUpdate::GetByteProgress(uint64_t * _result) 1.2393 +{ 1.2394 + NS_ENSURE_ARG(_result); 1.2395 + 1.2396 + *_result = mByteProgress; 1.2397 + return NS_OK; 1.2398 +} 1.2399 + 1.2400 +NS_IMETHODIMP 1.2401 +nsOfflineCacheUpdate::Schedule() 1.2402 +{ 1.2403 + LOG(("nsOfflineCacheUpdate::Schedule [%p]", this)); 1.2404 + 1.2405 + nsOfflineCacheUpdateService* service = 1.2406 + nsOfflineCacheUpdateService::EnsureService(); 1.2407 + 1.2408 + if (!service) { 1.2409 + return NS_ERROR_FAILURE; 1.2410 + } 1.2411 + 1.2412 + return service->ScheduleUpdate(this); 1.2413 +} 1.2414 + 1.2415 +NS_IMETHODIMP 1.2416 +nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, 1.2417 + uint32_t aState) 1.2418 +{ 1.2419 + if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) { 1.2420 + // Take the mSucceeded flag from the underlying update, we will be 1.2421 + // queried for it soon. mSucceeded of this update is false (manifest 1.2422 + // check failed) but the subsequent re-fetch update might succeed 1.2423 + bool succeeded; 1.2424 + aUpdate->GetSucceeded(&succeeded); 1.2425 + mSucceeded = succeeded; 1.2426 + } 1.2427 + 1.2428 + NotifyState(aState); 1.2429 + if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) 1.2430 + aUpdate->RemoveObserver(this); 1.2431 + 1.2432 + return NS_OK; 1.2433 +} 1.2434 + 1.2435 +NS_IMETHODIMP 1.2436 +nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache) 1.2437 +{ 1.2438 + AssociateDocuments(applicationCache); 1.2439 + return NS_OK; 1.2440 +} 1.2441 + 1.2442 +//----------------------------------------------------------------------------- 1.2443 +// nsOfflineCacheUpdate::nsIRunable 1.2444 +//----------------------------------------------------------------------------- 1.2445 + 1.2446 +NS_IMETHODIMP 1.2447 +nsOfflineCacheUpdate::Run() 1.2448 +{ 1.2449 + ProcessNextURI(); 1.2450 + return NS_OK; 1.2451 +}