uriloader/prefetch/nsOfflineCacheUpdate.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #if defined(MOZ_LOGGING)
michael@0 7 #define FORCE_PR_LOG
michael@0 8 #endif
michael@0 9
michael@0 10 #include "nsOfflineCacheUpdate.h"
michael@0 11
michael@0 12 #include "nsCPrefetchService.h"
michael@0 13 #include "nsCURILoader.h"
michael@0 14 #include "nsIApplicationCacheContainer.h"
michael@0 15 #include "nsIApplicationCacheChannel.h"
michael@0 16 #include "nsIApplicationCacheService.h"
michael@0 17 #include "nsICache.h"
michael@0 18 #include "nsICacheService.h"
michael@0 19 #include "nsICacheSession.h"
michael@0 20 #include "nsICachingChannel.h"
michael@0 21 #include "nsIContent.h"
michael@0 22 #include "mozilla/dom/Element.h"
michael@0 23 #include "nsIDocumentLoader.h"
michael@0 24 #include "nsIDOMElement.h"
michael@0 25 #include "nsIDOMWindow.h"
michael@0 26 #include "nsIDOMOfflineResourceList.h"
michael@0 27 #include "nsIDocument.h"
michael@0 28 #include "nsIObserverService.h"
michael@0 29 #include "nsIURL.h"
michael@0 30 #include "nsIWebProgress.h"
michael@0 31 #include "nsICryptoHash.h"
michael@0 32 #include "nsICacheEntry.h"
michael@0 33 #include "nsIPermissionManager.h"
michael@0 34 #include "nsIPrincipal.h"
michael@0 35 #include "nsNetCID.h"
michael@0 36 #include "nsNetUtil.h"
michael@0 37 #include "nsServiceManagerUtils.h"
michael@0 38 #include "nsStreamUtils.h"
michael@0 39 #include "nsThreadUtils.h"
michael@0 40 #include "nsProxyRelease.h"
michael@0 41 #include "nsIConsoleService.h"
michael@0 42 #include "prlog.h"
michael@0 43 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 44 #include "mozilla/Preferences.h"
michael@0 45 #include "mozilla/Attributes.h"
michael@0 46
michael@0 47 #include "nsXULAppAPI.h"
michael@0 48
michael@0 49 using namespace mozilla;
michael@0 50
michael@0 51 static const uint32_t kRescheduleLimit = 3;
michael@0 52 // Max number of retries for every entry of pinned app.
michael@0 53 static const uint32_t kPinnedEntryRetriesLimit = 3;
michael@0 54 // Maximum number of parallel items loads
michael@0 55 static const uint32_t kParallelLoadLimit = 15;
michael@0 56
michael@0 57 // Quota for offline apps when preloading
michael@0 58 static const int32_t kCustomProfileQuota = 512000;
michael@0 59
michael@0 60 #if defined(PR_LOGGING)
michael@0 61 //
michael@0 62 // To enable logging (see prlog.h for full details):
michael@0 63 //
michael@0 64 // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
michael@0 65 // set NSPR_LOG_FILE=offlineupdate.log
michael@0 66 //
michael@0 67 // this enables PR_LOG_ALWAYS level information and places all output in
michael@0 68 // the file offlineupdate.log
michael@0 69 //
michael@0 70 extern PRLogModuleInfo *gOfflineCacheUpdateLog;
michael@0 71 #endif
michael@0 72
michael@0 73 #undef LOG
michael@0 74 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
michael@0 75
michael@0 76 #undef LOG_ENABLED
michael@0 77 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
michael@0 78
michael@0 79 class AutoFreeArray {
michael@0 80 public:
michael@0 81 AutoFreeArray(uint32_t count, char **values)
michael@0 82 : mCount(count), mValues(values) {};
michael@0 83 ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
michael@0 84 private:
michael@0 85 uint32_t mCount;
michael@0 86 char **mValues;
michael@0 87 };
michael@0 88
michael@0 89 namespace { // anon
michael@0 90
michael@0 91 nsresult
michael@0 92 DropReferenceFromURL(nsIURI * aURI)
michael@0 93 {
michael@0 94 // XXXdholbert If this SetRef fails, callers of this method probably
michael@0 95 // want to call aURI->CloneIgnoringRef() and use the result of that.
michael@0 96 return aURI->SetRef(EmptyCString());
michael@0 97 }
michael@0 98
michael@0 99 void
michael@0 100 LogToConsole(const char * message, nsOfflineCacheUpdateItem * item = nullptr)
michael@0 101 {
michael@0 102 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 103 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 104 if (consoleService)
michael@0 105 {
michael@0 106 nsAutoString messageUTF16 = NS_ConvertUTF8toUTF16(message);
michael@0 107 if (item && item->mURI) {
michael@0 108 nsAutoCString uriSpec;
michael@0 109 item->mURI->GetSpec(uriSpec);
michael@0 110
michael@0 111 messageUTF16.Append(NS_LITERAL_STRING(", URL="));
michael@0 112 messageUTF16.Append(NS_ConvertUTF8toUTF16(uriSpec));
michael@0 113 }
michael@0 114 consoleService->LogStringMessage(messageUTF16.get());
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 } // anon namespace
michael@0 119
michael@0 120 //-----------------------------------------------------------------------------
michael@0 121 // nsManifestCheck
michael@0 122 //-----------------------------------------------------------------------------
michael@0 123
michael@0 124 class nsManifestCheck MOZ_FINAL : public nsIStreamListener
michael@0 125 , public nsIChannelEventSink
michael@0 126 , public nsIInterfaceRequestor
michael@0 127 {
michael@0 128 public:
michael@0 129 nsManifestCheck(nsOfflineCacheUpdate *aUpdate,
michael@0 130 nsIURI *aURI,
michael@0 131 nsIURI *aReferrerURI)
michael@0 132 : mUpdate(aUpdate)
michael@0 133 , mURI(aURI)
michael@0 134 , mReferrerURI(aReferrerURI)
michael@0 135 {}
michael@0 136
michael@0 137 NS_DECL_ISUPPORTS
michael@0 138 NS_DECL_NSIREQUESTOBSERVER
michael@0 139 NS_DECL_NSISTREAMLISTENER
michael@0 140 NS_DECL_NSICHANNELEVENTSINK
michael@0 141 NS_DECL_NSIINTERFACEREQUESTOR
michael@0 142
michael@0 143 nsresult Begin();
michael@0 144
michael@0 145 private:
michael@0 146
michael@0 147 static NS_METHOD ReadManifest(nsIInputStream *aInputStream,
michael@0 148 void *aClosure,
michael@0 149 const char *aFromSegment,
michael@0 150 uint32_t aOffset,
michael@0 151 uint32_t aCount,
michael@0 152 uint32_t *aBytesConsumed);
michael@0 153
michael@0 154 nsRefPtr<nsOfflineCacheUpdate> mUpdate;
michael@0 155 nsCOMPtr<nsIURI> mURI;
michael@0 156 nsCOMPtr<nsIURI> mReferrerURI;
michael@0 157 nsCOMPtr<nsICryptoHash> mManifestHash;
michael@0 158 nsCOMPtr<nsIChannel> mChannel;
michael@0 159 };
michael@0 160
michael@0 161 //-----------------------------------------------------------------------------
michael@0 162 // nsManifestCheck::nsISupports
michael@0 163 //-----------------------------------------------------------------------------
michael@0 164 NS_IMPL_ISUPPORTS(nsManifestCheck,
michael@0 165 nsIRequestObserver,
michael@0 166 nsIStreamListener,
michael@0 167 nsIChannelEventSink,
michael@0 168 nsIInterfaceRequestor)
michael@0 169
michael@0 170 //-----------------------------------------------------------------------------
michael@0 171 // nsManifestCheck <public>
michael@0 172 //-----------------------------------------------------------------------------
michael@0 173
michael@0 174 nsresult
michael@0 175 nsManifestCheck::Begin()
michael@0 176 {
michael@0 177 nsresult rv;
michael@0 178 mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
michael@0 179 NS_ENSURE_SUCCESS(rv, rv);
michael@0 180
michael@0 181 rv = mManifestHash->Init(nsICryptoHash::MD5);
michael@0 182 NS_ENSURE_SUCCESS(rv, rv);
michael@0 183
michael@0 184 rv = NS_NewChannel(getter_AddRefs(mChannel),
michael@0 185 mURI,
michael@0 186 nullptr, nullptr, nullptr,
michael@0 187 nsIRequest::LOAD_BYPASS_CACHE);
michael@0 188 NS_ENSURE_SUCCESS(rv, rv);
michael@0 189
michael@0 190 // configure HTTP specific stuff
michael@0 191 nsCOMPtr<nsIHttpChannel> httpChannel =
michael@0 192 do_QueryInterface(mChannel);
michael@0 193 if (httpChannel) {
michael@0 194 httpChannel->SetReferrer(mReferrerURI);
michael@0 195 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
michael@0 196 NS_LITERAL_CSTRING("offline-resource"),
michael@0 197 false);
michael@0 198 }
michael@0 199
michael@0 200 rv = mChannel->AsyncOpen(this, nullptr);
michael@0 201 NS_ENSURE_SUCCESS(rv, rv);
michael@0 202
michael@0 203 return NS_OK;
michael@0 204 }
michael@0 205
michael@0 206 //-----------------------------------------------------------------------------
michael@0 207 // nsManifestCheck <public>
michael@0 208 //-----------------------------------------------------------------------------
michael@0 209
michael@0 210 /* static */
michael@0 211 NS_METHOD
michael@0 212 nsManifestCheck::ReadManifest(nsIInputStream *aInputStream,
michael@0 213 void *aClosure,
michael@0 214 const char *aFromSegment,
michael@0 215 uint32_t aOffset,
michael@0 216 uint32_t aCount,
michael@0 217 uint32_t *aBytesConsumed)
michael@0 218 {
michael@0 219 nsManifestCheck *manifestCheck =
michael@0 220 static_cast<nsManifestCheck*>(aClosure);
michael@0 221
michael@0 222 nsresult rv;
michael@0 223 *aBytesConsumed = aCount;
michael@0 224
michael@0 225 rv = manifestCheck->mManifestHash->Update(
michael@0 226 reinterpret_cast<const uint8_t *>(aFromSegment), aCount);
michael@0 227 NS_ENSURE_SUCCESS(rv, rv);
michael@0 228
michael@0 229 return NS_OK;
michael@0 230 }
michael@0 231
michael@0 232 //-----------------------------------------------------------------------------
michael@0 233 // nsManifestCheck::nsIStreamListener
michael@0 234 //-----------------------------------------------------------------------------
michael@0 235
michael@0 236 NS_IMETHODIMP
michael@0 237 nsManifestCheck::OnStartRequest(nsIRequest *aRequest,
michael@0 238 nsISupports *aContext)
michael@0 239 {
michael@0 240 return NS_OK;
michael@0 241 }
michael@0 242
michael@0 243 NS_IMETHODIMP
michael@0 244 nsManifestCheck::OnDataAvailable(nsIRequest *aRequest,
michael@0 245 nsISupports *aContext,
michael@0 246 nsIInputStream *aStream,
michael@0 247 uint64_t aOffset,
michael@0 248 uint32_t aCount)
michael@0 249 {
michael@0 250 uint32_t bytesRead;
michael@0 251 aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
michael@0 252 return NS_OK;
michael@0 253 }
michael@0 254
michael@0 255 NS_IMETHODIMP
michael@0 256 nsManifestCheck::OnStopRequest(nsIRequest *aRequest,
michael@0 257 nsISupports *aContext,
michael@0 258 nsresult aStatus)
michael@0 259 {
michael@0 260 nsAutoCString manifestHash;
michael@0 261 if (NS_SUCCEEDED(aStatus)) {
michael@0 262 mManifestHash->Finish(true, manifestHash);
michael@0 263 }
michael@0 264
michael@0 265 mUpdate->ManifestCheckCompleted(aStatus, manifestHash);
michael@0 266
michael@0 267 return NS_OK;
michael@0 268 }
michael@0 269
michael@0 270 //-----------------------------------------------------------------------------
michael@0 271 // nsManifestCheck::nsIInterfaceRequestor
michael@0 272 //-----------------------------------------------------------------------------
michael@0 273
michael@0 274 NS_IMETHODIMP
michael@0 275 nsManifestCheck::GetInterface(const nsIID &aIID, void **aResult)
michael@0 276 {
michael@0 277 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
michael@0 278 NS_ADDREF_THIS();
michael@0 279 *aResult = static_cast<nsIChannelEventSink *>(this);
michael@0 280 return NS_OK;
michael@0 281 }
michael@0 282
michael@0 283 return NS_ERROR_NO_INTERFACE;
michael@0 284 }
michael@0 285
michael@0 286 //-----------------------------------------------------------------------------
michael@0 287 // nsManifestCheck::nsIChannelEventSink
michael@0 288 //-----------------------------------------------------------------------------
michael@0 289
michael@0 290 NS_IMETHODIMP
michael@0 291 nsManifestCheck::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
michael@0 292 nsIChannel *aNewChannel,
michael@0 293 uint32_t aFlags,
michael@0 294 nsIAsyncVerifyRedirectCallback *callback)
michael@0 295 {
michael@0 296 // Redirects should cause the load (and therefore the update) to fail.
michael@0 297 if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
michael@0 298 callback->OnRedirectVerifyCallback(NS_OK);
michael@0 299 return NS_OK;
michael@0 300 }
michael@0 301
michael@0 302 LogToConsole("Manifest check failed because its response is a redirect");
michael@0 303
michael@0 304 aOldChannel->Cancel(NS_ERROR_ABORT);
michael@0 305 return NS_ERROR_ABORT;
michael@0 306 }
michael@0 307
michael@0 308 //-----------------------------------------------------------------------------
michael@0 309 // nsOfflineCacheUpdateItem::nsISupports
michael@0 310 //-----------------------------------------------------------------------------
michael@0 311
michael@0 312 NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateItem,
michael@0 313 nsIRequestObserver,
michael@0 314 nsIStreamListener,
michael@0 315 nsIRunnable,
michael@0 316 nsIInterfaceRequestor,
michael@0 317 nsIChannelEventSink)
michael@0 318
michael@0 319 //-----------------------------------------------------------------------------
michael@0 320 // nsOfflineCacheUpdateItem <public>
michael@0 321 //-----------------------------------------------------------------------------
michael@0 322
michael@0 323 nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsIURI *aURI,
michael@0 324 nsIURI *aReferrerURI,
michael@0 325 nsIApplicationCache *aApplicationCache,
michael@0 326 nsIApplicationCache *aPreviousApplicationCache,
michael@0 327 uint32_t type)
michael@0 328 : mURI(aURI)
michael@0 329 , mReferrerURI(aReferrerURI)
michael@0 330 , mApplicationCache(aApplicationCache)
michael@0 331 , mPreviousApplicationCache(aPreviousApplicationCache)
michael@0 332 , mItemType(type)
michael@0 333 , mChannel(nullptr)
michael@0 334 , mState(LoadStatus::UNINITIALIZED)
michael@0 335 , mBytesRead(0)
michael@0 336 {
michael@0 337 }
michael@0 338
michael@0 339 nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
michael@0 340 {
michael@0 341 }
michael@0 342
michael@0 343 nsresult
michael@0 344 nsOfflineCacheUpdateItem::OpenChannel(nsOfflineCacheUpdate *aUpdate)
michael@0 345 {
michael@0 346 #if defined(PR_LOGGING)
michael@0 347 if (LOG_ENABLED()) {
michael@0 348 nsAutoCString spec;
michael@0 349 mURI->GetSpec(spec);
michael@0 350 LOG(("%p: Opening channel for %s", this, spec.get()));
michael@0 351 }
michael@0 352 #endif
michael@0 353
michael@0 354 if (mUpdate) {
michael@0 355 // Holding a reference to the update means this item is already
michael@0 356 // in progress (has a channel, or is just in between OnStopRequest()
michael@0 357 // and its Run() call. We must never open channel on this item again.
michael@0 358 LOG((" %p is already running! ignoring", this));
michael@0 359 return NS_ERROR_ALREADY_OPENED;
michael@0 360 }
michael@0 361
michael@0 362 nsresult rv = nsOfflineCacheUpdate::GetCacheKey(mURI, mCacheKey);
michael@0 363 NS_ENSURE_SUCCESS(rv, rv);
michael@0 364
michael@0 365 uint32_t flags = nsIRequest::LOAD_BACKGROUND |
michael@0 366 nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
michael@0 367 nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
michael@0 368
michael@0 369 if (mApplicationCache == mPreviousApplicationCache) {
michael@0 370 // Same app cache to read from and to write to is used during
michael@0 371 // an only-update-check procedure. Here we protect the existing
michael@0 372 // cache from being modified.
michael@0 373 flags |= nsIRequest::INHIBIT_CACHING;
michael@0 374 }
michael@0 375
michael@0 376 rv = NS_NewChannel(getter_AddRefs(mChannel),
michael@0 377 mURI,
michael@0 378 nullptr, nullptr, this,
michael@0 379 flags);
michael@0 380 NS_ENSURE_SUCCESS(rv, rv);
michael@0 381
michael@0 382 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
michael@0 383 do_QueryInterface(mChannel, &rv);
michael@0 384
michael@0 385 // Support for nsIApplicationCacheChannel is required.
michael@0 386 NS_ENSURE_SUCCESS(rv, rv);
michael@0 387
michael@0 388 // Use the existing application cache as the cache to check.
michael@0 389 rv = appCacheChannel->SetApplicationCache(mPreviousApplicationCache);
michael@0 390 NS_ENSURE_SUCCESS(rv, rv);
michael@0 391
michael@0 392 // Set the new application cache as the target for write.
michael@0 393 rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
michael@0 394 NS_ENSURE_SUCCESS(rv, rv);
michael@0 395
michael@0 396 // configure HTTP specific stuff
michael@0 397 nsCOMPtr<nsIHttpChannel> httpChannel =
michael@0 398 do_QueryInterface(mChannel);
michael@0 399 if (httpChannel) {
michael@0 400 httpChannel->SetReferrer(mReferrerURI);
michael@0 401 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
michael@0 402 NS_LITERAL_CSTRING("offline-resource"),
michael@0 403 false);
michael@0 404 }
michael@0 405
michael@0 406 rv = mChannel->AsyncOpen(this, nullptr);
michael@0 407 NS_ENSURE_SUCCESS(rv, rv);
michael@0 408
michael@0 409 mUpdate = aUpdate;
michael@0 410
michael@0 411 mState = LoadStatus::REQUESTED;
michael@0 412
michael@0 413 return NS_OK;
michael@0 414 }
michael@0 415
michael@0 416 nsresult
michael@0 417 nsOfflineCacheUpdateItem::Cancel()
michael@0 418 {
michael@0 419 if (mChannel) {
michael@0 420 mChannel->Cancel(NS_ERROR_ABORT);
michael@0 421 mChannel = nullptr;
michael@0 422 }
michael@0 423
michael@0 424 mState = LoadStatus::UNINITIALIZED;
michael@0 425
michael@0 426 return NS_OK;
michael@0 427 }
michael@0 428
michael@0 429 //-----------------------------------------------------------------------------
michael@0 430 // nsOfflineCacheUpdateItem::nsIStreamListener
michael@0 431 //-----------------------------------------------------------------------------
michael@0 432
michael@0 433 NS_IMETHODIMP
michael@0 434 nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
michael@0 435 nsISupports *aContext)
michael@0 436 {
michael@0 437 mState = LoadStatus::RECEIVING;
michael@0 438
michael@0 439 return NS_OK;
michael@0 440 }
michael@0 441
michael@0 442 NS_IMETHODIMP
michael@0 443 nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
michael@0 444 nsISupports *aContext,
michael@0 445 nsIInputStream *aStream,
michael@0 446 uint64_t aOffset,
michael@0 447 uint32_t aCount)
michael@0 448 {
michael@0 449 uint32_t bytesRead = 0;
michael@0 450 aStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &bytesRead);
michael@0 451 mBytesRead += bytesRead;
michael@0 452 LOG(("loaded %u bytes into offline cache [offset=%llu]\n",
michael@0 453 bytesRead, aOffset));
michael@0 454
michael@0 455 mUpdate->OnByteProgress(bytesRead);
michael@0 456
michael@0 457 return NS_OK;
michael@0 458 }
michael@0 459
michael@0 460 NS_IMETHODIMP
michael@0 461 nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
michael@0 462 nsISupports *aContext,
michael@0 463 nsresult aStatus)
michael@0 464 {
michael@0 465 #if defined(PR_LOGGING)
michael@0 466 if (LOG_ENABLED()) {
michael@0 467 nsAutoCString spec;
michael@0 468 mURI->GetSpec(spec);
michael@0 469 LOG(("%p: Done fetching offline item %s [status=%x]\n",
michael@0 470 this, spec.get(), aStatus));
michael@0 471 }
michael@0 472 #endif
michael@0 473
michael@0 474 if (mBytesRead == 0 && aStatus == NS_OK) {
michael@0 475 // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
michael@0 476 // specified), but the object should report loadedSize as if it
michael@0 477 // did.
michael@0 478 mChannel->GetContentLength(&mBytesRead);
michael@0 479 mUpdate->OnByteProgress(mBytesRead);
michael@0 480 }
michael@0 481
michael@0 482 if (NS_FAILED(aStatus)) {
michael@0 483 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
michael@0 484 if (httpChannel) {
michael@0 485 bool isNoStore;
michael@0 486 if (NS_SUCCEEDED(httpChannel->IsNoStoreResponse(&isNoStore))
michael@0 487 && isNoStore) {
michael@0 488 LogToConsole("Offline cache manifest item has Cache-control: no-store header",
michael@0 489 this);
michael@0 490 }
michael@0 491 }
michael@0 492 }
michael@0 493
michael@0 494 // We need to notify the update that the load is complete, but we
michael@0 495 // want to give the channel a chance to close the cache entries.
michael@0 496 NS_DispatchToCurrentThread(this);
michael@0 497
michael@0 498 return NS_OK;
michael@0 499 }
michael@0 500
michael@0 501
michael@0 502 //-----------------------------------------------------------------------------
michael@0 503 // nsOfflineCacheUpdateItem::nsIRunnable
michael@0 504 //-----------------------------------------------------------------------------
michael@0 505 NS_IMETHODIMP
michael@0 506 nsOfflineCacheUpdateItem::Run()
michael@0 507 {
michael@0 508 // Set mState to LOADED here rather than in OnStopRequest to prevent
michael@0 509 // race condition when checking state of all mItems in ProcessNextURI().
michael@0 510 // If state would have been set in OnStopRequest we could mistakenly
michael@0 511 // take this item as already finished and finish the update process too
michael@0 512 // early when ProcessNextURI() would get called between OnStopRequest()
michael@0 513 // and Run() of this item. Finish() would then have been called twice.
michael@0 514 mState = LoadStatus::LOADED;
michael@0 515
michael@0 516 nsRefPtr<nsOfflineCacheUpdate> update;
michael@0 517 update.swap(mUpdate);
michael@0 518 update->LoadCompleted(this);
michael@0 519
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522
michael@0 523 //-----------------------------------------------------------------------------
michael@0 524 // nsOfflineCacheUpdateItem::nsIInterfaceRequestor
michael@0 525 //-----------------------------------------------------------------------------
michael@0 526
michael@0 527 NS_IMETHODIMP
michael@0 528 nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
michael@0 529 {
michael@0 530 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
michael@0 531 NS_ADDREF_THIS();
michael@0 532 *aResult = static_cast<nsIChannelEventSink *>(this);
michael@0 533 return NS_OK;
michael@0 534 }
michael@0 535
michael@0 536 return NS_ERROR_NO_INTERFACE;
michael@0 537 }
michael@0 538
michael@0 539 //-----------------------------------------------------------------------------
michael@0 540 // nsOfflineCacheUpdateItem::nsIChannelEventSink
michael@0 541 //-----------------------------------------------------------------------------
michael@0 542
michael@0 543 NS_IMETHODIMP
michael@0 544 nsOfflineCacheUpdateItem::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
michael@0 545 nsIChannel *aNewChannel,
michael@0 546 uint32_t aFlags,
michael@0 547 nsIAsyncVerifyRedirectCallback *cb)
michael@0 548 {
michael@0 549 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
michael@0 550 // Don't allow redirect in case of non-internal redirect and cancel
michael@0 551 // the channel to clean the cache entry.
michael@0 552 LogToConsole("Offline cache manifest failed because an item redirects", this);
michael@0 553
michael@0 554 aOldChannel->Cancel(NS_ERROR_ABORT);
michael@0 555 return NS_ERROR_ABORT;
michael@0 556 }
michael@0 557
michael@0 558 nsCOMPtr<nsIURI> newURI;
michael@0 559 nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
michael@0 560 if (NS_FAILED(rv))
michael@0 561 return rv;
michael@0 562
michael@0 563 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
michael@0 564 do_QueryInterface(aNewChannel);
michael@0 565 if (appCacheChannel) {
michael@0 566 rv = appCacheChannel->SetApplicationCacheForWrite(mApplicationCache);
michael@0 567 NS_ENSURE_SUCCESS(rv, rv);
michael@0 568 }
michael@0 569
michael@0 570 nsAutoCString oldScheme;
michael@0 571 mURI->GetScheme(oldScheme);
michael@0 572
michael@0 573 bool match;
michael@0 574 if (NS_FAILED(newURI->SchemeIs(oldScheme.get(), &match)) || !match) {
michael@0 575 LOG(("rejected: redirected to a different scheme\n"));
michael@0 576 return NS_ERROR_ABORT;
michael@0 577 }
michael@0 578
michael@0 579 // HTTP request headers are not automatically forwarded to the new channel.
michael@0 580 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
michael@0 581 NS_ENSURE_STATE(httpChannel);
michael@0 582
michael@0 583 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
michael@0 584 NS_LITERAL_CSTRING("offline-resource"),
michael@0 585 false);
michael@0 586
michael@0 587 mChannel = aNewChannel;
michael@0 588
michael@0 589 cb->OnRedirectVerifyCallback(NS_OK);
michael@0 590 return NS_OK;
michael@0 591 }
michael@0 592
michael@0 593 nsresult
michael@0 594 nsOfflineCacheUpdateItem::GetRequestSucceeded(bool * succeeded)
michael@0 595 {
michael@0 596 *succeeded = false;
michael@0 597
michael@0 598 if (!mChannel)
michael@0 599 return NS_OK;
michael@0 600
michael@0 601 nsresult rv;
michael@0 602 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
michael@0 603 NS_ENSURE_SUCCESS(rv, rv);
michael@0 604
michael@0 605 bool reqSucceeded;
michael@0 606 rv = httpChannel->GetRequestSucceeded(&reqSucceeded);
michael@0 607 if (NS_ERROR_NOT_AVAILABLE == rv)
michael@0 608 return NS_OK;
michael@0 609 NS_ENSURE_SUCCESS(rv, rv);
michael@0 610
michael@0 611 if (!reqSucceeded) {
michael@0 612 LOG(("Request failed"));
michael@0 613 return NS_OK;
michael@0 614 }
michael@0 615
michael@0 616 nsresult channelStatus;
michael@0 617 rv = httpChannel->GetStatus(&channelStatus);
michael@0 618 NS_ENSURE_SUCCESS(rv, rv);
michael@0 619
michael@0 620 if (NS_FAILED(channelStatus)) {
michael@0 621 LOG(("Channel status=0x%08x", channelStatus));
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 *succeeded = true;
michael@0 626 return NS_OK;
michael@0 627 }
michael@0 628
michael@0 629 bool
michael@0 630 nsOfflineCacheUpdateItem::IsScheduled()
michael@0 631 {
michael@0 632 return mState == LoadStatus::UNINITIALIZED;
michael@0 633 }
michael@0 634
michael@0 635 bool
michael@0 636 nsOfflineCacheUpdateItem::IsInProgress()
michael@0 637 {
michael@0 638 return mState == LoadStatus::REQUESTED ||
michael@0 639 mState == LoadStatus::RECEIVING;
michael@0 640 }
michael@0 641
michael@0 642 bool
michael@0 643 nsOfflineCacheUpdateItem::IsCompleted()
michael@0 644 {
michael@0 645 return mState == LoadStatus::LOADED;
michael@0 646 }
michael@0 647
michael@0 648 nsresult
michael@0 649 nsOfflineCacheUpdateItem::GetStatus(uint16_t *aStatus)
michael@0 650 {
michael@0 651 if (!mChannel) {
michael@0 652 *aStatus = 0;
michael@0 653 return NS_OK;
michael@0 654 }
michael@0 655
michael@0 656 nsresult rv;
michael@0 657 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
michael@0 658 NS_ENSURE_SUCCESS(rv, rv);
michael@0 659
michael@0 660 uint32_t httpStatus;
michael@0 661 rv = httpChannel->GetResponseStatus(&httpStatus);
michael@0 662 if (rv == NS_ERROR_NOT_AVAILABLE) {
michael@0 663 *aStatus = 0;
michael@0 664 return NS_OK;
michael@0 665 }
michael@0 666
michael@0 667 NS_ENSURE_SUCCESS(rv, rv);
michael@0 668 *aStatus = uint16_t(httpStatus);
michael@0 669 return NS_OK;
michael@0 670 }
michael@0 671
michael@0 672 //-----------------------------------------------------------------------------
michael@0 673 // nsOfflineManifestItem
michael@0 674 //-----------------------------------------------------------------------------
michael@0 675
michael@0 676 //-----------------------------------------------------------------------------
michael@0 677 // nsOfflineManifestItem <public>
michael@0 678 //-----------------------------------------------------------------------------
michael@0 679
michael@0 680 nsOfflineManifestItem::nsOfflineManifestItem(nsIURI *aURI,
michael@0 681 nsIURI *aReferrerURI,
michael@0 682 nsIApplicationCache *aApplicationCache,
michael@0 683 nsIApplicationCache *aPreviousApplicationCache)
michael@0 684 : nsOfflineCacheUpdateItem(aURI, aReferrerURI,
michael@0 685 aApplicationCache, aPreviousApplicationCache,
michael@0 686 nsIApplicationCache::ITEM_MANIFEST)
michael@0 687 , mParserState(PARSE_INIT)
michael@0 688 , mNeedsUpdate(true)
michael@0 689 , mManifestHashInitialized(false)
michael@0 690 {
michael@0 691 ReadStrictFileOriginPolicyPref();
michael@0 692 }
michael@0 693
michael@0 694 nsOfflineManifestItem::~nsOfflineManifestItem()
michael@0 695 {
michael@0 696 }
michael@0 697
michael@0 698 //-----------------------------------------------------------------------------
michael@0 699 // nsOfflineManifestItem <private>
michael@0 700 //-----------------------------------------------------------------------------
michael@0 701
michael@0 702 /* static */
michael@0 703 NS_METHOD
michael@0 704 nsOfflineManifestItem::ReadManifest(nsIInputStream *aInputStream,
michael@0 705 void *aClosure,
michael@0 706 const char *aFromSegment,
michael@0 707 uint32_t aOffset,
michael@0 708 uint32_t aCount,
michael@0 709 uint32_t *aBytesConsumed)
michael@0 710 {
michael@0 711 nsOfflineManifestItem *manifest =
michael@0 712 static_cast<nsOfflineManifestItem*>(aClosure);
michael@0 713
michael@0 714 nsresult rv;
michael@0 715
michael@0 716 *aBytesConsumed = aCount;
michael@0 717
michael@0 718 if (manifest->mParserState == PARSE_ERROR) {
michael@0 719 // parse already failed, ignore this
michael@0 720 return NS_OK;
michael@0 721 }
michael@0 722
michael@0 723 if (!manifest->mManifestHashInitialized) {
michael@0 724 // Avoid re-creation of crypto hash when it fails from some reason the first time
michael@0 725 manifest->mManifestHashInitialized = true;
michael@0 726
michael@0 727 manifest->mManifestHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
michael@0 728 if (NS_SUCCEEDED(rv)) {
michael@0 729 rv = manifest->mManifestHash->Init(nsICryptoHash::MD5);
michael@0 730 if (NS_FAILED(rv)) {
michael@0 731 manifest->mManifestHash = nullptr;
michael@0 732 LOG(("Could not initialize manifest hash for byte-to-byte check, rv=%08x", rv));
michael@0 733 }
michael@0 734 }
michael@0 735 }
michael@0 736
michael@0 737 if (manifest->mManifestHash) {
michael@0 738 rv = manifest->mManifestHash->Update(reinterpret_cast<const uint8_t *>(aFromSegment), aCount);
michael@0 739 if (NS_FAILED(rv)) {
michael@0 740 manifest->mManifestHash = nullptr;
michael@0 741 LOG(("Could not update manifest hash, rv=%08x", rv));
michael@0 742 }
michael@0 743 }
michael@0 744
michael@0 745 manifest->mReadBuf.Append(aFromSegment, aCount);
michael@0 746
michael@0 747 nsCString::const_iterator begin, iter, end;
michael@0 748 manifest->mReadBuf.BeginReading(begin);
michael@0 749 manifest->mReadBuf.EndReading(end);
michael@0 750
michael@0 751 for (iter = begin; iter != end; iter++) {
michael@0 752 if (*iter == '\r' || *iter == '\n') {
michael@0 753 nsresult rv = manifest->HandleManifestLine(begin, iter);
michael@0 754
michael@0 755 if (NS_FAILED(rv)) {
michael@0 756 LOG(("HandleManifestLine failed with 0x%08x", rv));
michael@0 757 *aBytesConsumed = 0; // Avoid assertion failure in stream tee
michael@0 758 return NS_ERROR_ABORT;
michael@0 759 }
michael@0 760
michael@0 761 begin = iter;
michael@0 762 begin++;
michael@0 763 }
michael@0 764 }
michael@0 765
michael@0 766 // any leftovers are saved for next time
michael@0 767 manifest->mReadBuf = Substring(begin, end);
michael@0 768
michael@0 769 return NS_OK;
michael@0 770 }
michael@0 771
michael@0 772 nsresult
michael@0 773 nsOfflineManifestItem::AddNamespace(uint32_t namespaceType,
michael@0 774 const nsCString &namespaceSpec,
michael@0 775 const nsCString &data)
michael@0 776
michael@0 777 {
michael@0 778 nsresult rv;
michael@0 779 if (!mNamespaces) {
michael@0 780 mNamespaces = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
michael@0 781 NS_ENSURE_SUCCESS(rv, rv);
michael@0 782 }
michael@0 783
michael@0 784 nsCOMPtr<nsIApplicationCacheNamespace> ns =
michael@0 785 do_CreateInstance(NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &rv);
michael@0 786 NS_ENSURE_SUCCESS(rv, rv);
michael@0 787
michael@0 788 rv = ns->Init(namespaceType, namespaceSpec, data);
michael@0 789 NS_ENSURE_SUCCESS(rv, rv);
michael@0 790
michael@0 791 rv = mNamespaces->AppendElement(ns, false);
michael@0 792 NS_ENSURE_SUCCESS(rv, rv);
michael@0 793
michael@0 794 return NS_OK;
michael@0 795 }
michael@0 796
michael@0 797 nsresult
michael@0 798 nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegin,
michael@0 799 const nsCString::const_iterator &aEnd)
michael@0 800 {
michael@0 801 nsCString::const_iterator begin = aBegin;
michael@0 802 nsCString::const_iterator end = aEnd;
michael@0 803
michael@0 804 // all lines ignore trailing spaces and tabs
michael@0 805 nsCString::const_iterator last = end;
michael@0 806 --last;
michael@0 807 while (end != begin && (*last == ' ' || *last == '\t')) {
michael@0 808 --end;
michael@0 809 --last;
michael@0 810 }
michael@0 811
michael@0 812 if (mParserState == PARSE_INIT) {
michael@0 813 // Allow a UTF-8 BOM
michael@0 814 if (begin != end && static_cast<unsigned char>(*begin) == 0xef) {
michael@0 815 if (++begin == end || static_cast<unsigned char>(*begin) != 0xbb ||
michael@0 816 ++begin == end || static_cast<unsigned char>(*begin) != 0xbf) {
michael@0 817 mParserState = PARSE_ERROR;
michael@0 818 LogToConsole("Offline cache manifest BOM error", this);
michael@0 819 return NS_OK;
michael@0 820 }
michael@0 821 ++begin;
michael@0 822 }
michael@0 823
michael@0 824 const nsCSubstring &magic = Substring(begin, end);
michael@0 825
michael@0 826 if (!magic.EqualsLiteral("CACHE MANIFEST")) {
michael@0 827 mParserState = PARSE_ERROR;
michael@0 828 LogToConsole("Offline cache manifest magic incorect", this);
michael@0 829 return NS_OK;
michael@0 830 }
michael@0 831
michael@0 832 mParserState = PARSE_CACHE_ENTRIES;
michael@0 833 return NS_OK;
michael@0 834 }
michael@0 835
michael@0 836 // lines other than the first ignore leading spaces and tabs
michael@0 837 while (begin != end && (*begin == ' ' || *begin == '\t'))
michael@0 838 begin++;
michael@0 839
michael@0 840 // ignore blank lines and comments
michael@0 841 if (begin == end || *begin == '#')
michael@0 842 return NS_OK;
michael@0 843
michael@0 844 const nsCSubstring &line = Substring(begin, end);
michael@0 845
michael@0 846 if (line.EqualsLiteral("CACHE:")) {
michael@0 847 mParserState = PARSE_CACHE_ENTRIES;
michael@0 848 return NS_OK;
michael@0 849 }
michael@0 850
michael@0 851 if (line.EqualsLiteral("FALLBACK:")) {
michael@0 852 mParserState = PARSE_FALLBACK_ENTRIES;
michael@0 853 return NS_OK;
michael@0 854 }
michael@0 855
michael@0 856 if (line.EqualsLiteral("NETWORK:")) {
michael@0 857 mParserState = PARSE_BYPASS_ENTRIES;
michael@0 858 return NS_OK;
michael@0 859 }
michael@0 860
michael@0 861 // Every other section type we don't know must be silently ignored.
michael@0 862 nsCString::const_iterator lastChar = end;
michael@0 863 if (*(--lastChar) == ':') {
michael@0 864 mParserState = PARSE_UNKNOWN_SECTION;
michael@0 865 return NS_OK;
michael@0 866 }
michael@0 867
michael@0 868 nsresult rv;
michael@0 869
michael@0 870 switch(mParserState) {
michael@0 871 case PARSE_INIT:
michael@0 872 case PARSE_ERROR: {
michael@0 873 // this should have been dealt with earlier
michael@0 874 return NS_ERROR_FAILURE;
michael@0 875 }
michael@0 876
michael@0 877 case PARSE_UNKNOWN_SECTION: {
michael@0 878 // just jump over
michael@0 879 return NS_OK;
michael@0 880 }
michael@0 881
michael@0 882 case PARSE_CACHE_ENTRIES: {
michael@0 883 nsCOMPtr<nsIURI> uri;
michael@0 884 rv = NS_NewURI(getter_AddRefs(uri), line, nullptr, mURI);
michael@0 885 if (NS_FAILED(rv))
michael@0 886 break;
michael@0 887 if (NS_FAILED(DropReferenceFromURL(uri)))
michael@0 888 break;
michael@0 889
michael@0 890 nsAutoCString scheme;
michael@0 891 uri->GetScheme(scheme);
michael@0 892
michael@0 893 // Manifest URIs must have the same scheme as the manifest.
michael@0 894 bool match;
michael@0 895 if (NS_FAILED(mURI->SchemeIs(scheme.get(), &match)) || !match)
michael@0 896 break;
michael@0 897
michael@0 898 mExplicitURIs.AppendObject(uri);
michael@0 899 break;
michael@0 900 }
michael@0 901
michael@0 902 case PARSE_FALLBACK_ENTRIES: {
michael@0 903 int32_t separator = line.FindChar(' ');
michael@0 904 if (separator == kNotFound) {
michael@0 905 separator = line.FindChar('\t');
michael@0 906 if (separator == kNotFound)
michael@0 907 break;
michael@0 908 }
michael@0 909
michael@0 910 nsCString namespaceSpec(Substring(line, 0, separator));
michael@0 911 nsCString fallbackSpec(Substring(line, separator + 1));
michael@0 912 namespaceSpec.CompressWhitespace();
michael@0 913 fallbackSpec.CompressWhitespace();
michael@0 914
michael@0 915 nsCOMPtr<nsIURI> namespaceURI;
michael@0 916 rv = NS_NewURI(getter_AddRefs(namespaceURI), namespaceSpec, nullptr, mURI);
michael@0 917 if (NS_FAILED(rv))
michael@0 918 break;
michael@0 919 if (NS_FAILED(DropReferenceFromURL(namespaceURI)))
michael@0 920 break;
michael@0 921 rv = namespaceURI->GetAsciiSpec(namespaceSpec);
michael@0 922 if (NS_FAILED(rv))
michael@0 923 break;
michael@0 924
michael@0 925
michael@0 926 nsCOMPtr<nsIURI> fallbackURI;
michael@0 927 rv = NS_NewURI(getter_AddRefs(fallbackURI), fallbackSpec, nullptr, mURI);
michael@0 928 if (NS_FAILED(rv))
michael@0 929 break;
michael@0 930 if (NS_FAILED(DropReferenceFromURL(fallbackURI)))
michael@0 931 break;
michael@0 932 rv = fallbackURI->GetAsciiSpec(fallbackSpec);
michael@0 933 if (NS_FAILED(rv))
michael@0 934 break;
michael@0 935
michael@0 936 // Manifest and namespace must be same origin
michael@0 937 if (!NS_SecurityCompareURIs(mURI, namespaceURI,
michael@0 938 mStrictFileOriginPolicy))
michael@0 939 break;
michael@0 940
michael@0 941 // Fallback and namespace must be same origin
michael@0 942 if (!NS_SecurityCompareURIs(namespaceURI, fallbackURI,
michael@0 943 mStrictFileOriginPolicy))
michael@0 944 break;
michael@0 945
michael@0 946 mFallbackURIs.AppendObject(fallbackURI);
michael@0 947
michael@0 948 AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK,
michael@0 949 namespaceSpec, fallbackSpec);
michael@0 950 break;
michael@0 951 }
michael@0 952
michael@0 953 case PARSE_BYPASS_ENTRIES: {
michael@0 954 if (line[0] == '*' && (line.Length() == 1 || line[1] == ' ' || line[1] == '\t'))
michael@0 955 {
michael@0 956 // '*' indicates to make the online whitelist wildcard flag open,
michael@0 957 // i.e. do allow load of resources not present in the offline cache
michael@0 958 // or not conforming any namespace.
michael@0 959 // We achive that simply by adding an 'empty' - i.e. universal
michael@0 960 // namespace of BYPASS type into the cache.
michael@0 961 AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
michael@0 962 EmptyCString(), EmptyCString());
michael@0 963 break;
michael@0 964 }
michael@0 965
michael@0 966 nsCOMPtr<nsIURI> bypassURI;
michael@0 967 rv = NS_NewURI(getter_AddRefs(bypassURI), line, nullptr, mURI);
michael@0 968 if (NS_FAILED(rv))
michael@0 969 break;
michael@0 970
michael@0 971 nsAutoCString scheme;
michael@0 972 bypassURI->GetScheme(scheme);
michael@0 973 bool equals;
michael@0 974 if (NS_FAILED(mURI->SchemeIs(scheme.get(), &equals)) || !equals)
michael@0 975 break;
michael@0 976 if (NS_FAILED(DropReferenceFromURL(bypassURI)))
michael@0 977 break;
michael@0 978 nsCString spec;
michael@0 979 if (NS_FAILED(bypassURI->GetAsciiSpec(spec)))
michael@0 980 break;
michael@0 981
michael@0 982 AddNamespace(nsIApplicationCacheNamespace::NAMESPACE_BYPASS,
michael@0 983 spec, EmptyCString());
michael@0 984 break;
michael@0 985 }
michael@0 986 }
michael@0 987
michael@0 988 return NS_OK;
michael@0 989 }
michael@0 990
michael@0 991 nsresult
michael@0 992 nsOfflineManifestItem::GetOldManifestContentHash(nsIRequest *aRequest)
michael@0 993 {
michael@0 994 nsresult rv;
michael@0 995
michael@0 996 nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
michael@0 997 NS_ENSURE_SUCCESS(rv, rv);
michael@0 998
michael@0 999 // load the main cache token that is actually the old offline cache token and
michael@0 1000 // read previous manifest content hash value
michael@0 1001 nsCOMPtr<nsISupports> cacheToken;
michael@0 1002 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
michael@0 1003 if (cacheToken) {
michael@0 1004 nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
michael@0 1005 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1006
michael@0 1007 rv = cacheDescriptor->GetMetaDataElement("offline-manifest-hash", getter_Copies(mOldManifestHashValue));
michael@0 1008 if (NS_FAILED(rv))
michael@0 1009 mOldManifestHashValue.Truncate();
michael@0 1010 }
michael@0 1011
michael@0 1012 return NS_OK;
michael@0 1013 }
michael@0 1014
michael@0 1015 nsresult
michael@0 1016 nsOfflineManifestItem::CheckNewManifestContentHash(nsIRequest *aRequest)
michael@0 1017 {
michael@0 1018 nsresult rv;
michael@0 1019
michael@0 1020 if (!mManifestHash) {
michael@0 1021 // Nothing to compare against...
michael@0 1022 return NS_OK;
michael@0 1023 }
michael@0 1024
michael@0 1025 nsCString newManifestHashValue;
michael@0 1026 rv = mManifestHash->Finish(true, mManifestHashValue);
michael@0 1027 mManifestHash = nullptr;
michael@0 1028
michael@0 1029 if (NS_FAILED(rv)) {
michael@0 1030 LOG(("Could not finish manifest hash, rv=%08x", rv));
michael@0 1031 // This is not critical error
michael@0 1032 return NS_OK;
michael@0 1033 }
michael@0 1034
michael@0 1035 if (!ParseSucceeded()) {
michael@0 1036 // Parsing failed, the hash is not valid
michael@0 1037 return NS_OK;
michael@0 1038 }
michael@0 1039
michael@0 1040 if (mOldManifestHashValue == mManifestHashValue) {
michael@0 1041 LOG(("Update not needed, downloaded manifest content is byte-for-byte identical"));
michael@0 1042 mNeedsUpdate = false;
michael@0 1043 }
michael@0 1044
michael@0 1045 // Store the manifest content hash value to the new
michael@0 1046 // offline cache token
michael@0 1047 nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
michael@0 1048 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1049
michael@0 1050 nsCOMPtr<nsISupports> cacheToken;
michael@0 1051 cachingChannel->GetOfflineCacheToken(getter_AddRefs(cacheToken));
michael@0 1052 if (cacheToken) {
michael@0 1053 nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
michael@0 1054 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1055
michael@0 1056 rv = cacheDescriptor->SetMetaDataElement("offline-manifest-hash", mManifestHashValue.get());
michael@0 1057 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1058 }
michael@0 1059
michael@0 1060 return NS_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 void
michael@0 1064 nsOfflineManifestItem::ReadStrictFileOriginPolicyPref()
michael@0 1065 {
michael@0 1066 mStrictFileOriginPolicy =
michael@0 1067 Preferences::GetBool("security.fileuri.strict_origin_policy", true);
michael@0 1068 }
michael@0 1069
michael@0 1070 NS_IMETHODIMP
michael@0 1071 nsOfflineManifestItem::OnStartRequest(nsIRequest *aRequest,
michael@0 1072 nsISupports *aContext)
michael@0 1073 {
michael@0 1074 nsresult rv;
michael@0 1075
michael@0 1076 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
michael@0 1077 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1078
michael@0 1079 bool succeeded;
michael@0 1080 rv = channel->GetRequestSucceeded(&succeeded);
michael@0 1081 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1082
michael@0 1083 if (!succeeded) {
michael@0 1084 LOG(("HTTP request failed"));
michael@0 1085 LogToConsole("Offline cache manifest HTTP request failed", this);
michael@0 1086 mParserState = PARSE_ERROR;
michael@0 1087 return NS_ERROR_ABORT;
michael@0 1088 }
michael@0 1089
michael@0 1090 rv = GetOldManifestContentHash(aRequest);
michael@0 1091 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1092
michael@0 1093 return nsOfflineCacheUpdateItem::OnStartRequest(aRequest, aContext);
michael@0 1094 }
michael@0 1095
michael@0 1096 NS_IMETHODIMP
michael@0 1097 nsOfflineManifestItem::OnDataAvailable(nsIRequest *aRequest,
michael@0 1098 nsISupports *aContext,
michael@0 1099 nsIInputStream *aStream,
michael@0 1100 uint64_t aOffset,
michael@0 1101 uint32_t aCount)
michael@0 1102 {
michael@0 1103 uint32_t bytesRead = 0;
michael@0 1104 aStream->ReadSegments(ReadManifest, this, aCount, &bytesRead);
michael@0 1105 mBytesRead += bytesRead;
michael@0 1106
michael@0 1107 if (mParserState == PARSE_ERROR) {
michael@0 1108 LOG(("OnDataAvailable is canceling the request due a parse error\n"));
michael@0 1109 return NS_ERROR_ABORT;
michael@0 1110 }
michael@0 1111
michael@0 1112 LOG(("loaded %u bytes into offline cache [offset=%u]\n",
michael@0 1113 bytesRead, aOffset));
michael@0 1114
michael@0 1115 // All the parent method does is read and discard, don't bother
michael@0 1116 // chaining up.
michael@0 1117
michael@0 1118 return NS_OK;
michael@0 1119 }
michael@0 1120
michael@0 1121 NS_IMETHODIMP
michael@0 1122 nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest,
michael@0 1123 nsISupports *aContext,
michael@0 1124 nsresult aStatus)
michael@0 1125 {
michael@0 1126 if (mBytesRead == 0) {
michael@0 1127 // We didn't need to read (because LOAD_ONLY_IF_MODIFIED was
michael@0 1128 // specified).
michael@0 1129 mNeedsUpdate = false;
michael@0 1130 } else {
michael@0 1131 // Handle any leftover manifest data.
michael@0 1132 nsCString::const_iterator begin, end;
michael@0 1133 mReadBuf.BeginReading(begin);
michael@0 1134 mReadBuf.EndReading(end);
michael@0 1135 nsresult rv = HandleManifestLine(begin, end);
michael@0 1136 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1137
michael@0 1138 rv = CheckNewManifestContentHash(aRequest);
michael@0 1139 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1140 }
michael@0 1141
michael@0 1142 return nsOfflineCacheUpdateItem::OnStopRequest(aRequest, aContext, aStatus);
michael@0 1143 }
michael@0 1144
michael@0 1145 //-----------------------------------------------------------------------------
michael@0 1146 // nsOfflineCacheUpdate::nsISupports
michael@0 1147 //-----------------------------------------------------------------------------
michael@0 1148
michael@0 1149 NS_IMPL_ISUPPORTS(nsOfflineCacheUpdate,
michael@0 1150 nsIOfflineCacheUpdateObserver,
michael@0 1151 nsIOfflineCacheUpdate,
michael@0 1152 nsIRunnable)
michael@0 1153
michael@0 1154 //-----------------------------------------------------------------------------
michael@0 1155 // nsOfflineCacheUpdate <public>
michael@0 1156 //-----------------------------------------------------------------------------
michael@0 1157
michael@0 1158 nsOfflineCacheUpdate::nsOfflineCacheUpdate()
michael@0 1159 : mState(STATE_UNINITIALIZED)
michael@0 1160 , mAddedItems(false)
michael@0 1161 , mPartialUpdate(false)
michael@0 1162 , mOnlyCheckUpdate(false)
michael@0 1163 , mSucceeded(true)
michael@0 1164 , mObsolete(false)
michael@0 1165 , mAppID(NECKO_NO_APP_ID)
michael@0 1166 , mInBrowser(false)
michael@0 1167 , mItemsInProgress(0)
michael@0 1168 , mRescheduleCount(0)
michael@0 1169 , mPinnedEntryRetriesCount(0)
michael@0 1170 , mPinned(false)
michael@0 1171 {
michael@0 1172 }
michael@0 1173
michael@0 1174 nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
michael@0 1175 {
michael@0 1176 LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
michael@0 1177 }
michael@0 1178
michael@0 1179 /* static */
michael@0 1180 nsresult
michael@0 1181 nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
michael@0 1182 {
michael@0 1183 aKey.Truncate();
michael@0 1184
michael@0 1185 nsCOMPtr<nsIURI> newURI;
michael@0 1186 nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(newURI));
michael@0 1187 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1188
michael@0 1189 rv = newURI->GetAsciiSpec(aKey);
michael@0 1190 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1191
michael@0 1192 return NS_OK;
michael@0 1193 }
michael@0 1194
michael@0 1195 nsresult
michael@0 1196 nsOfflineCacheUpdate::InitInternal(nsIURI *aManifestURI)
michael@0 1197 {
michael@0 1198 nsresult rv;
michael@0 1199
michael@0 1200 // Only http and https applications are supported.
michael@0 1201 bool match;
michael@0 1202 rv = aManifestURI->SchemeIs("http", &match);
michael@0 1203 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1204
michael@0 1205 if (!match) {
michael@0 1206 rv = aManifestURI->SchemeIs("https", &match);
michael@0 1207 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1208 if (!match)
michael@0 1209 return NS_ERROR_ABORT;
michael@0 1210 }
michael@0 1211
michael@0 1212 mManifestURI = aManifestURI;
michael@0 1213
michael@0 1214 rv = mManifestURI->GetAsciiHost(mUpdateDomain);
michael@0 1215 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1216
michael@0 1217 mPartialUpdate = false;
michael@0 1218
michael@0 1219 return NS_OK;
michael@0 1220 }
michael@0 1221
michael@0 1222 nsresult
michael@0 1223 nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
michael@0 1224 nsIURI *aDocumentURI,
michael@0 1225 nsIDOMDocument *aDocument,
michael@0 1226 nsIFile *aCustomProfileDir,
michael@0 1227 uint32_t aAppID,
michael@0 1228 bool aInBrowser)
michael@0 1229 {
michael@0 1230 nsresult rv;
michael@0 1231
michael@0 1232 // Make sure the service has been initialized
michael@0 1233 nsOfflineCacheUpdateService* service =
michael@0 1234 nsOfflineCacheUpdateService::EnsureService();
michael@0 1235 if (!service)
michael@0 1236 return NS_ERROR_FAILURE;
michael@0 1237
michael@0 1238 LOG(("nsOfflineCacheUpdate::Init [%p]", this));
michael@0 1239
michael@0 1240 rv = InitInternal(aManifestURI);
michael@0 1241 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1242
michael@0 1243 nsCOMPtr<nsIApplicationCacheService> cacheService =
michael@0 1244 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
michael@0 1245 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1246
michael@0 1247 mDocumentURI = aDocumentURI;
michael@0 1248
michael@0 1249 if (aCustomProfileDir) {
michael@0 1250 rv = cacheService->BuildGroupIDForApp(aManifestURI,
michael@0 1251 aAppID, aInBrowser,
michael@0 1252 mGroupID);
michael@0 1253 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1254
michael@0 1255 // Create only a new offline application cache in the custom profile
michael@0 1256 // This is a preload of a new cache.
michael@0 1257
michael@0 1258 // XXX Custom updates don't support "updating" of an existing cache
michael@0 1259 // in the custom profile at the moment. This support can be, though,
michael@0 1260 // simply added as well when needed.
michael@0 1261 mPreviousApplicationCache = nullptr;
michael@0 1262
michael@0 1263 rv = cacheService->CreateCustomApplicationCache(mGroupID,
michael@0 1264 aCustomProfileDir,
michael@0 1265 kCustomProfileQuota,
michael@0 1266 getter_AddRefs(mApplicationCache));
michael@0 1267 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1268
michael@0 1269 mCustomProfileDir = aCustomProfileDir;
michael@0 1270 }
michael@0 1271 else {
michael@0 1272 rv = cacheService->BuildGroupIDForApp(aManifestURI,
michael@0 1273 aAppID, aInBrowser,
michael@0 1274 mGroupID);
michael@0 1275 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1276
michael@0 1277 rv = cacheService->GetActiveCache(mGroupID,
michael@0 1278 getter_AddRefs(mPreviousApplicationCache));
michael@0 1279 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1280
michael@0 1281 rv = cacheService->CreateApplicationCache(mGroupID,
michael@0 1282 getter_AddRefs(mApplicationCache));
michael@0 1283 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1284 }
michael@0 1285
michael@0 1286 rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
michael@0 1287 nullptr,
michael@0 1288 &mPinned);
michael@0 1289 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1290
michael@0 1291 mAppID = aAppID;
michael@0 1292 mInBrowser = aInBrowser;
michael@0 1293
michael@0 1294 mState = STATE_INITIALIZED;
michael@0 1295 return NS_OK;
michael@0 1296 }
michael@0 1297
michael@0 1298 nsresult
michael@0 1299 nsOfflineCacheUpdate::InitForUpdateCheck(nsIURI *aManifestURI,
michael@0 1300 uint32_t aAppID,
michael@0 1301 bool aInBrowser,
michael@0 1302 nsIObserver *aObserver)
michael@0 1303 {
michael@0 1304 nsresult rv;
michael@0 1305
michael@0 1306 // Make sure the service has been initialized
michael@0 1307 nsOfflineCacheUpdateService* service =
michael@0 1308 nsOfflineCacheUpdateService::EnsureService();
michael@0 1309 if (!service)
michael@0 1310 return NS_ERROR_FAILURE;
michael@0 1311
michael@0 1312 LOG(("nsOfflineCacheUpdate::InitForUpdateCheck [%p]", this));
michael@0 1313
michael@0 1314 rv = InitInternal(aManifestURI);
michael@0 1315 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1316
michael@0 1317 nsCOMPtr<nsIApplicationCacheService> cacheService =
michael@0 1318 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
michael@0 1319 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1320
michael@0 1321 rv = cacheService->BuildGroupIDForApp(aManifestURI,
michael@0 1322 aAppID, aInBrowser,
michael@0 1323 mGroupID);
michael@0 1324 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1325
michael@0 1326 rv = cacheService->GetActiveCache(mGroupID,
michael@0 1327 getter_AddRefs(mPreviousApplicationCache));
michael@0 1328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1329
michael@0 1330 // To load the manifest properly using current app cache to satisfy and
michael@0 1331 // also to compare the cached content hash value we have to set 'some'
michael@0 1332 // app cache to write to on the channel. Otherwise the cached version will
michael@0 1333 // be used and no actual network request will be made. We use the same
michael@0 1334 // app cache here. OpenChannel prevents caching in this case using
michael@0 1335 // INHIBIT_CACHING load flag.
michael@0 1336 mApplicationCache = mPreviousApplicationCache;
michael@0 1337
michael@0 1338 rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aManifestURI,
michael@0 1339 nullptr,
michael@0 1340 &mPinned);
michael@0 1341 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1342
michael@0 1343 mUpdateAvailableObserver = aObserver;
michael@0 1344 mOnlyCheckUpdate = true;
michael@0 1345
michael@0 1346 mState = STATE_INITIALIZED;
michael@0 1347 return NS_OK;
michael@0 1348 }
michael@0 1349
michael@0 1350 nsresult
michael@0 1351 nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
michael@0 1352 const nsACString& clientID,
michael@0 1353 nsIURI *aDocumentURI)
michael@0 1354 {
michael@0 1355 nsresult rv;
michael@0 1356
michael@0 1357 // Make sure the service has been initialized
michael@0 1358 nsOfflineCacheUpdateService* service =
michael@0 1359 nsOfflineCacheUpdateService::EnsureService();
michael@0 1360 if (!service)
michael@0 1361 return NS_ERROR_FAILURE;
michael@0 1362
michael@0 1363 LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
michael@0 1364
michael@0 1365 mPartialUpdate = true;
michael@0 1366 mDocumentURI = aDocumentURI;
michael@0 1367
michael@0 1368 mManifestURI = aManifestURI;
michael@0 1369 rv = mManifestURI->GetAsciiHost(mUpdateDomain);
michael@0 1370 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1371
michael@0 1372 nsCOMPtr<nsIApplicationCacheService> cacheService =
michael@0 1373 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
michael@0 1374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1375
michael@0 1376 rv = cacheService->GetApplicationCache(clientID,
michael@0 1377 getter_AddRefs(mApplicationCache));
michael@0 1378 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1379
michael@0 1380 if (!mApplicationCache) {
michael@0 1381 nsAutoCString manifestSpec;
michael@0 1382 rv = GetCacheKey(mManifestURI, manifestSpec);
michael@0 1383 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1384
michael@0 1385 rv = cacheService->CreateApplicationCache
michael@0 1386 (manifestSpec, getter_AddRefs(mApplicationCache));
michael@0 1387 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1388 }
michael@0 1389
michael@0 1390 rv = mApplicationCache->GetManifestURI(getter_AddRefs(mManifestURI));
michael@0 1391 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1392
michael@0 1393 nsAutoCString groupID;
michael@0 1394 rv = mApplicationCache->GetGroupID(groupID);
michael@0 1395 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1396
michael@0 1397 rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(aDocumentURI,
michael@0 1398 nullptr,
michael@0 1399 &mPinned);
michael@0 1400 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1401
michael@0 1402 mState = STATE_INITIALIZED;
michael@0 1403 return NS_OK;
michael@0 1404 }
michael@0 1405
michael@0 1406 nsresult
michael@0 1407 nsOfflineCacheUpdate::HandleManifest(bool *aDoUpdate)
michael@0 1408 {
michael@0 1409 // Be pessimistic
michael@0 1410 *aDoUpdate = false;
michael@0 1411
michael@0 1412 bool succeeded;
michael@0 1413 nsresult rv = mManifestItem->GetRequestSucceeded(&succeeded);
michael@0 1414 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1415
michael@0 1416 if (!succeeded || !mManifestItem->ParseSucceeded()) {
michael@0 1417 return NS_ERROR_FAILURE;
michael@0 1418 }
michael@0 1419
michael@0 1420 if (!mManifestItem->NeedsUpdate()) {
michael@0 1421 return NS_OK;
michael@0 1422 }
michael@0 1423
michael@0 1424 // Add items requested by the manifest.
michael@0 1425 const nsCOMArray<nsIURI> &manifestURIs = mManifestItem->GetExplicitURIs();
michael@0 1426 for (int32_t i = 0; i < manifestURIs.Count(); i++) {
michael@0 1427 rv = AddURI(manifestURIs[i], nsIApplicationCache::ITEM_EXPLICIT);
michael@0 1428 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1429 }
michael@0 1430
michael@0 1431 const nsCOMArray<nsIURI> &fallbackURIs = mManifestItem->GetFallbackURIs();
michael@0 1432 for (int32_t i = 0; i < fallbackURIs.Count(); i++) {
michael@0 1433 rv = AddURI(fallbackURIs[i], nsIApplicationCache::ITEM_FALLBACK);
michael@0 1434 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1435 }
michael@0 1436
michael@0 1437 // The document that requested the manifest is implicitly included
michael@0 1438 // as part of that manifest update.
michael@0 1439 rv = AddURI(mDocumentURI, nsIApplicationCache::ITEM_IMPLICIT);
michael@0 1440 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1441
michael@0 1442 // Add items previously cached implicitly
michael@0 1443 rv = AddExistingItems(nsIApplicationCache::ITEM_IMPLICIT);
michael@0 1444 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1445
michael@0 1446 // Add items requested by the script API
michael@0 1447 rv = AddExistingItems(nsIApplicationCache::ITEM_DYNAMIC);
michael@0 1448 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1449
michael@0 1450 // Add opportunistically cached items conforming current opportunistic
michael@0 1451 // namespace list
michael@0 1452 rv = AddExistingItems(nsIApplicationCache::ITEM_OPPORTUNISTIC,
michael@0 1453 &mManifestItem->GetOpportunisticNamespaces());
michael@0 1454 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1455
michael@0 1456 *aDoUpdate = true;
michael@0 1457
michael@0 1458 return NS_OK;
michael@0 1459 }
michael@0 1460
michael@0 1461 bool
michael@0 1462 nsOfflineCacheUpdate::CheckUpdateAvailability()
michael@0 1463 {
michael@0 1464 nsresult rv;
michael@0 1465
michael@0 1466 bool succeeded;
michael@0 1467 rv = mManifestItem->GetRequestSucceeded(&succeeded);
michael@0 1468 NS_ENSURE_SUCCESS(rv, false);
michael@0 1469
michael@0 1470 if (!succeeded || !mManifestItem->ParseSucceeded()) {
michael@0 1471 return false;
michael@0 1472 }
michael@0 1473
michael@0 1474 if (!mPinned) {
michael@0 1475 uint16_t status;
michael@0 1476 rv = mManifestItem->GetStatus(&status);
michael@0 1477 NS_ENSURE_SUCCESS(rv, false);
michael@0 1478
michael@0 1479 // Treat these as there would be an update available,
michael@0 1480 // since this is indication of demand to remove this
michael@0 1481 // offline cache.
michael@0 1482 if (status == 404 || status == 410) {
michael@0 1483 return true;
michael@0 1484 }
michael@0 1485 }
michael@0 1486
michael@0 1487 return mManifestItem->NeedsUpdate();
michael@0 1488 }
michael@0 1489
michael@0 1490 void
michael@0 1491 nsOfflineCacheUpdate::LoadCompleted(nsOfflineCacheUpdateItem *aItem)
michael@0 1492 {
michael@0 1493 nsresult rv;
michael@0 1494
michael@0 1495 LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
michael@0 1496
michael@0 1497 if (mState == STATE_FINISHED) {
michael@0 1498 LOG((" after completion, ignoring"));
michael@0 1499 return;
michael@0 1500 }
michael@0 1501
michael@0 1502 // Keep the object alive through a Finish() call.
michael@0 1503 nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
michael@0 1504
michael@0 1505 if (mState == STATE_CANCELLED) {
michael@0 1506 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1507 Finish();
michael@0 1508 return;
michael@0 1509 }
michael@0 1510
michael@0 1511 if (mState == STATE_CHECKING) {
michael@0 1512 // Manifest load finished.
michael@0 1513
michael@0 1514 if (mOnlyCheckUpdate) {
michael@0 1515 Finish();
michael@0 1516 NotifyUpdateAvailability(CheckUpdateAvailability());
michael@0 1517 return;
michael@0 1518 }
michael@0 1519
michael@0 1520 NS_ASSERTION(mManifestItem,
michael@0 1521 "Must have a manifest item in STATE_CHECKING.");
michael@0 1522 NS_ASSERTION(mManifestItem == aItem,
michael@0 1523 "Unexpected aItem in nsOfflineCacheUpdate::LoadCompleted");
michael@0 1524
michael@0 1525 // A 404 or 410 is interpreted as an intentional removal of
michael@0 1526 // the manifest file, rather than a transient server error.
michael@0 1527 // Obsolete this cache group if one of these is returned.
michael@0 1528 uint16_t status;
michael@0 1529 rv = mManifestItem->GetStatus(&status);
michael@0 1530 if (status == 404 || status == 410) {
michael@0 1531 LogToConsole("Offline cache manifest removed, cache cleared", mManifestItem);
michael@0 1532 mSucceeded = false;
michael@0 1533 if (mPreviousApplicationCache) {
michael@0 1534 if (mPinned) {
michael@0 1535 // Do not obsolete a pinned application.
michael@0 1536 NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
michael@0 1537 } else {
michael@0 1538 NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE);
michael@0 1539 mObsolete = true;
michael@0 1540 }
michael@0 1541 } else {
michael@0 1542 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1543 mObsolete = true;
michael@0 1544 }
michael@0 1545 Finish();
michael@0 1546 return;
michael@0 1547 }
michael@0 1548
michael@0 1549 bool doUpdate;
michael@0 1550 if (NS_FAILED(HandleManifest(&doUpdate))) {
michael@0 1551 mSucceeded = false;
michael@0 1552 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1553 Finish();
michael@0 1554 return;
michael@0 1555 }
michael@0 1556
michael@0 1557 if (!doUpdate) {
michael@0 1558 LogToConsole("Offline cache doesn't need to update", mManifestItem);
michael@0 1559
michael@0 1560 mSucceeded = false;
michael@0 1561
michael@0 1562 AssociateDocuments(mPreviousApplicationCache);
michael@0 1563
michael@0 1564 ScheduleImplicit();
michael@0 1565
michael@0 1566 // If we didn't need an implicit update, we can
michael@0 1567 // send noupdate and end the update now.
michael@0 1568 if (!mImplicitUpdate) {
michael@0 1569 NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
michael@0 1570 Finish();
michael@0 1571 }
michael@0 1572 return;
michael@0 1573 }
michael@0 1574
michael@0 1575 rv = mApplicationCache->MarkEntry(mManifestItem->mCacheKey,
michael@0 1576 mManifestItem->mItemType);
michael@0 1577 if (NS_FAILED(rv)) {
michael@0 1578 mSucceeded = false;
michael@0 1579 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1580 Finish();
michael@0 1581 return;
michael@0 1582 }
michael@0 1583
michael@0 1584 mState = STATE_DOWNLOADING;
michael@0 1585 NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
michael@0 1586
michael@0 1587 // Start fetching resources.
michael@0 1588 ProcessNextURI();
michael@0 1589
michael@0 1590 return;
michael@0 1591 }
michael@0 1592
michael@0 1593 // Normal load finished.
michael@0 1594 if (mItemsInProgress) // Just to be safe here!
michael@0 1595 --mItemsInProgress;
michael@0 1596
michael@0 1597 bool succeeded;
michael@0 1598 rv = aItem->GetRequestSucceeded(&succeeded);
michael@0 1599
michael@0 1600 if (mPinned && NS_SUCCEEDED(rv) && succeeded) {
michael@0 1601 uint32_t dummy_cache_type;
michael@0 1602 rv = mApplicationCache->GetTypes(aItem->mCacheKey, &dummy_cache_type);
michael@0 1603 bool item_doomed = NS_FAILED(rv); // can not find it? -> doomed
michael@0 1604
michael@0 1605 if (item_doomed &&
michael@0 1606 mPinnedEntryRetriesCount < kPinnedEntryRetriesLimit &&
michael@0 1607 (aItem->mItemType & (nsIApplicationCache::ITEM_EXPLICIT |
michael@0 1608 nsIApplicationCache::ITEM_FALLBACK))) {
michael@0 1609 rv = EvictOneNonPinned();
michael@0 1610 if (NS_FAILED(rv)) {
michael@0 1611 mSucceeded = false;
michael@0 1612 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1613 Finish();
michael@0 1614 return;
michael@0 1615 }
michael@0 1616
michael@0 1617 // This reverts the item state to UNINITIALIZED that makes it to
michael@0 1618 // be scheduled for download again.
michael@0 1619 rv = aItem->Cancel();
michael@0 1620 if (NS_FAILED(rv)) {
michael@0 1621 mSucceeded = false;
michael@0 1622 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1623 Finish();
michael@0 1624 return;
michael@0 1625 }
michael@0 1626
michael@0 1627 mPinnedEntryRetriesCount++;
michael@0 1628
michael@0 1629 LogToConsole("An unpinned offline cache deleted");
michael@0 1630
michael@0 1631 // Retry this item.
michael@0 1632 ProcessNextURI();
michael@0 1633 return;
michael@0 1634 }
michael@0 1635 }
michael@0 1636
michael@0 1637 // According to parallelism this may imply more pinned retries count,
michael@0 1638 // but that is not critical, since at one moment the algoritm will
michael@0 1639 // stop anyway. Also, this code may soon be completely removed
michael@0 1640 // after we have a separate storage for pinned apps.
michael@0 1641 mPinnedEntryRetriesCount = 0;
michael@0 1642
michael@0 1643 // Check for failures. 3XX, 4XX and 5XX errors on items explicitly
michael@0 1644 // listed in the manifest will cause the update to fail.
michael@0 1645 if (NS_FAILED(rv) || !succeeded) {
michael@0 1646 if (aItem->mItemType &
michael@0 1647 (nsIApplicationCache::ITEM_EXPLICIT |
michael@0 1648 nsIApplicationCache::ITEM_FALLBACK)) {
michael@0 1649 LogToConsole("Offline cache manifest item failed to load", aItem);
michael@0 1650 mSucceeded = false;
michael@0 1651 }
michael@0 1652 } else {
michael@0 1653 rv = mApplicationCache->MarkEntry(aItem->mCacheKey, aItem->mItemType);
michael@0 1654 if (NS_FAILED(rv)) {
michael@0 1655 mSucceeded = false;
michael@0 1656 }
michael@0 1657 }
michael@0 1658
michael@0 1659 if (!mSucceeded) {
michael@0 1660 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1661 Finish();
michael@0 1662 return;
michael@0 1663 }
michael@0 1664
michael@0 1665 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED);
michael@0 1666
michael@0 1667 ProcessNextURI();
michael@0 1668 }
michael@0 1669
michael@0 1670 void
michael@0 1671 nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus,
michael@0 1672 const nsCString &aManifestHash)
michael@0 1673 {
michael@0 1674 // Keep the object alive through a Finish() call.
michael@0 1675 nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
michael@0 1676
michael@0 1677 if (NS_SUCCEEDED(aStatus)) {
michael@0 1678 nsAutoCString firstManifestHash;
michael@0 1679 mManifestItem->GetManifestHash(firstManifestHash);
michael@0 1680 if (aManifestHash != firstManifestHash) {
michael@0 1681 LOG(("Manifest has changed during cache items download [%p]", this));
michael@0 1682 LogToConsole("Offline cache manifest changed during update", mManifestItem);
michael@0 1683 aStatus = NS_ERROR_FAILURE;
michael@0 1684 }
michael@0 1685 }
michael@0 1686
michael@0 1687 if (NS_FAILED(aStatus)) {
michael@0 1688 mSucceeded = false;
michael@0 1689 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1690 }
michael@0 1691
michael@0 1692 if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) {
michael@0 1693 // Do the final stuff but prevent notification of STATE_FINISHED.
michael@0 1694 // That would disconnect listeners that are responsible for document
michael@0 1695 // association after a successful update. Forwarding notifications
michael@0 1696 // from a new update through this dead update to them is absolutely
michael@0 1697 // correct.
michael@0 1698 FinishNoNotify();
michael@0 1699
michael@0 1700 nsRefPtr<nsOfflineCacheUpdate> newUpdate =
michael@0 1701 new nsOfflineCacheUpdate();
michael@0 1702 // Leave aDocument argument null. Only glues and children keep
michael@0 1703 // document instances.
michael@0 1704 newUpdate->Init(mManifestURI, mDocumentURI, nullptr,
michael@0 1705 mCustomProfileDir, mAppID, mInBrowser);
michael@0 1706
michael@0 1707 // In a rare case the manifest will not be modified on the next refetch
michael@0 1708 // transfer all master document URIs to the new update to ensure that
michael@0 1709 // all documents refering it will be properly cached.
michael@0 1710 for (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
michael@0 1711 newUpdate->StickDocument(mDocumentURIs[i]);
michael@0 1712 }
michael@0 1713
michael@0 1714 newUpdate->mRescheduleCount = mRescheduleCount + 1;
michael@0 1715 newUpdate->AddObserver(this, false);
michael@0 1716 newUpdate->Schedule();
michael@0 1717 }
michael@0 1718 else {
michael@0 1719 LogToConsole("Offline cache update done", mManifestItem);
michael@0 1720 Finish();
michael@0 1721 }
michael@0 1722 }
michael@0 1723
michael@0 1724 nsresult
michael@0 1725 nsOfflineCacheUpdate::Begin()
michael@0 1726 {
michael@0 1727 LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
michael@0 1728
michael@0 1729 // Keep the object alive through a ProcessNextURI()/Finish() call.
michael@0 1730 nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
michael@0 1731
michael@0 1732 mItemsInProgress = 0;
michael@0 1733
michael@0 1734 if (mState == STATE_CANCELLED) {
michael@0 1735 nsRefPtr<nsRunnableMethod<nsOfflineCacheUpdate> > errorNotification =
michael@0 1736 NS_NewRunnableMethod(this,
michael@0 1737 &nsOfflineCacheUpdate::AsyncFinishWithError);
michael@0 1738 nsresult rv = NS_DispatchToMainThread(errorNotification);
michael@0 1739 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1740
michael@0 1741 return NS_OK;
michael@0 1742 }
michael@0 1743
michael@0 1744 if (mPartialUpdate) {
michael@0 1745 mState = STATE_DOWNLOADING;
michael@0 1746 NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING);
michael@0 1747 ProcessNextURI();
michael@0 1748 return NS_OK;
michael@0 1749 }
michael@0 1750
michael@0 1751 // Start checking the manifest.
michael@0 1752 nsCOMPtr<nsIURI> uri;
michael@0 1753
michael@0 1754 mManifestItem = new nsOfflineManifestItem(mManifestURI,
michael@0 1755 mDocumentURI,
michael@0 1756 mApplicationCache,
michael@0 1757 mPreviousApplicationCache);
michael@0 1758 if (!mManifestItem) {
michael@0 1759 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1760 }
michael@0 1761
michael@0 1762 mState = STATE_CHECKING;
michael@0 1763 mByteProgress = 0;
michael@0 1764 NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING);
michael@0 1765
michael@0 1766 nsresult rv = mManifestItem->OpenChannel(this);
michael@0 1767 if (NS_FAILED(rv)) {
michael@0 1768 LoadCompleted(mManifestItem);
michael@0 1769 }
michael@0 1770
michael@0 1771 return NS_OK;
michael@0 1772 }
michael@0 1773
michael@0 1774 //-----------------------------------------------------------------------------
michael@0 1775 // nsOfflineCacheUpdate <private>
michael@0 1776 //-----------------------------------------------------------------------------
michael@0 1777
michael@0 1778 nsresult
michael@0 1779 nsOfflineCacheUpdate::AddExistingItems(uint32_t aType,
michael@0 1780 nsTArray<nsCString>* namespaceFilter)
michael@0 1781 {
michael@0 1782 if (!mPreviousApplicationCache) {
michael@0 1783 return NS_OK;
michael@0 1784 }
michael@0 1785
michael@0 1786 if (namespaceFilter && namespaceFilter->Length() == 0) {
michael@0 1787 // Don't bother to walk entries when there are no namespaces
michael@0 1788 // defined.
michael@0 1789 return NS_OK;
michael@0 1790 }
michael@0 1791
michael@0 1792 uint32_t count = 0;
michael@0 1793 char **keys = nullptr;
michael@0 1794 nsresult rv = mPreviousApplicationCache->GatherEntries(aType,
michael@0 1795 &count, &keys);
michael@0 1796 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1797
michael@0 1798 AutoFreeArray autoFree(count, keys);
michael@0 1799
michael@0 1800 for (uint32_t i = 0; i < count; i++) {
michael@0 1801 if (namespaceFilter) {
michael@0 1802 bool found = false;
michael@0 1803 for (uint32_t j = 0; j < namespaceFilter->Length() && !found; j++) {
michael@0 1804 found = StringBeginsWith(nsDependentCString(keys[i]),
michael@0 1805 namespaceFilter->ElementAt(j));
michael@0 1806 }
michael@0 1807
michael@0 1808 if (!found)
michael@0 1809 continue;
michael@0 1810 }
michael@0 1811
michael@0 1812 nsCOMPtr<nsIURI> uri;
michael@0 1813 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
michael@0 1814 rv = AddURI(uri, aType);
michael@0 1815 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1816 }
michael@0 1817 }
michael@0 1818
michael@0 1819 return NS_OK;
michael@0 1820 }
michael@0 1821
michael@0 1822 nsresult
michael@0 1823 nsOfflineCacheUpdate::ProcessNextURI()
michael@0 1824 {
michael@0 1825 // Keep the object alive through a Finish() call.
michael@0 1826 nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
michael@0 1827
michael@0 1828 LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, inprogress=%d, numItems=%d]",
michael@0 1829 this, mItemsInProgress, mItems.Length()));
michael@0 1830
michael@0 1831 if (mState != STATE_DOWNLOADING) {
michael@0 1832 LOG((" should only be called from the DOWNLOADING state, ignoring"));
michael@0 1833 return NS_ERROR_UNEXPECTED;
michael@0 1834 }
michael@0 1835
michael@0 1836 nsOfflineCacheUpdateItem * runItem = nullptr;
michael@0 1837 uint32_t completedItems = 0;
michael@0 1838 for (uint32_t i = 0; i < mItems.Length(); ++i) {
michael@0 1839 nsOfflineCacheUpdateItem * item = mItems[i];
michael@0 1840
michael@0 1841 if (item->IsScheduled()) {
michael@0 1842 runItem = item;
michael@0 1843 break;
michael@0 1844 }
michael@0 1845
michael@0 1846 if (item->IsCompleted())
michael@0 1847 ++completedItems;
michael@0 1848 }
michael@0 1849
michael@0 1850 if (completedItems == mItems.Length()) {
michael@0 1851 LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]: all items loaded", this));
michael@0 1852
michael@0 1853 if (mPartialUpdate) {
michael@0 1854 return Finish();
michael@0 1855 } else {
michael@0 1856 // Verify that the manifest wasn't changed during the
michael@0 1857 // update, to prevent capturing a cache while the server
michael@0 1858 // is being updated. The check will call
michael@0 1859 // ManifestCheckCompleted() when it's done.
michael@0 1860 nsRefPtr<nsManifestCheck> manifestCheck =
michael@0 1861 new nsManifestCheck(this, mManifestURI, mDocumentURI);
michael@0 1862 if (NS_FAILED(manifestCheck->Begin())) {
michael@0 1863 mSucceeded = false;
michael@0 1864 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 1865 return Finish();
michael@0 1866 }
michael@0 1867
michael@0 1868 return NS_OK;
michael@0 1869 }
michael@0 1870 }
michael@0 1871
michael@0 1872 if (!runItem) {
michael@0 1873 LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
michael@0 1874 " No more items to include in parallel load", this));
michael@0 1875 return NS_OK;
michael@0 1876 }
michael@0 1877
michael@0 1878 #if defined(PR_LOGGING)
michael@0 1879 if (LOG_ENABLED()) {
michael@0 1880 nsAutoCString spec;
michael@0 1881 runItem->mURI->GetSpec(spec);
michael@0 1882 LOG(("%p: Opening channel for %s", this, spec.get()));
michael@0 1883 }
michael@0 1884 #endif
michael@0 1885
michael@0 1886 ++mItemsInProgress;
michael@0 1887 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED);
michael@0 1888
michael@0 1889 nsresult rv = runItem->OpenChannel(this);
michael@0 1890 if (NS_FAILED(rv)) {
michael@0 1891 LoadCompleted(runItem);
michael@0 1892 return rv;
michael@0 1893 }
michael@0 1894
michael@0 1895 if (mItemsInProgress >= kParallelLoadLimit) {
michael@0 1896 LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p]:"
michael@0 1897 " At parallel load limit", this));
michael@0 1898 return NS_OK;
michael@0 1899 }
michael@0 1900
michael@0 1901 // This calls this method again via a post triggering
michael@0 1902 // a parallel item load
michael@0 1903 return NS_DispatchToCurrentThread(this);
michael@0 1904 }
michael@0 1905
michael@0 1906 void
michael@0 1907 nsOfflineCacheUpdate::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
michael@0 1908 {
michael@0 1909 for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
michael@0 1910 nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
michael@0 1911 do_QueryReferent(mWeakObservers[i]);
michael@0 1912 if (observer)
michael@0 1913 aObservers.AppendObject(observer);
michael@0 1914 else
michael@0 1915 mWeakObservers.RemoveObjectAt(i--);
michael@0 1916 }
michael@0 1917
michael@0 1918 for (int32_t i = 0; i < mObservers.Count(); i++) {
michael@0 1919 aObservers.AppendObject(mObservers[i]);
michael@0 1920 }
michael@0 1921 }
michael@0 1922
michael@0 1923 void
michael@0 1924 nsOfflineCacheUpdate::NotifyState(uint32_t state)
michael@0 1925 {
michael@0 1926 LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state));
michael@0 1927
michael@0 1928 if (state == STATE_ERROR) {
michael@0 1929 LogToConsole("Offline cache update error", mManifestItem);
michael@0 1930 }
michael@0 1931
michael@0 1932 nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
michael@0 1933 GatherObservers(observers);
michael@0 1934
michael@0 1935 for (int32_t i = 0; i < observers.Count(); i++) {
michael@0 1936 observers[i]->UpdateStateChanged(this, state);
michael@0 1937 }
michael@0 1938 }
michael@0 1939
michael@0 1940 void
michael@0 1941 nsOfflineCacheUpdate::NotifyUpdateAvailability(bool updateAvailable)
michael@0 1942 {
michael@0 1943 if (!mUpdateAvailableObserver)
michael@0 1944 return;
michael@0 1945
michael@0 1946 LOG(("nsOfflineCacheUpdate::NotifyUpdateAvailability [this=%p, avail=%d]",
michael@0 1947 this, updateAvailable));
michael@0 1948
michael@0 1949 const char* topic = updateAvailable
michael@0 1950 ? "offline-cache-update-available"
michael@0 1951 : "offline-cache-update-unavailable";
michael@0 1952
michael@0 1953 nsCOMPtr<nsIObserver> observer;
michael@0 1954 observer.swap(mUpdateAvailableObserver);
michael@0 1955 observer->Observe(mManifestURI, topic, nullptr);
michael@0 1956 }
michael@0 1957
michael@0 1958 void
michael@0 1959 nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache)
michael@0 1960 {
michael@0 1961 if (!cache) {
michael@0 1962 LOG(("nsOfflineCacheUpdate::AssociateDocuments bypassed"
michael@0 1963 ", no cache provided [this=%p]", this));
michael@0 1964 return;
michael@0 1965 }
michael@0 1966
michael@0 1967 nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
michael@0 1968 GatherObservers(observers);
michael@0 1969
michael@0 1970 for (int32_t i = 0; i < observers.Count(); i++) {
michael@0 1971 observers[i]->ApplicationCacheAvailable(cache);
michael@0 1972 }
michael@0 1973 }
michael@0 1974
michael@0 1975 void
michael@0 1976 nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI)
michael@0 1977 {
michael@0 1978 if (!aDocumentURI)
michael@0 1979 return;
michael@0 1980
michael@0 1981 mDocumentURIs.AppendObject(aDocumentURI);
michael@0 1982 }
michael@0 1983
michael@0 1984 void
michael@0 1985 nsOfflineCacheUpdate::SetOwner(nsOfflineCacheUpdateOwner *aOwner)
michael@0 1986 {
michael@0 1987 NS_ASSERTION(!mOwner, "Tried to set cache update owner twice.");
michael@0 1988 mOwner = aOwner->asWeakPtr();
michael@0 1989 }
michael@0 1990
michael@0 1991 bool
michael@0 1992 nsOfflineCacheUpdate::IsForGroupID(const nsCSubstring &groupID)
michael@0 1993 {
michael@0 1994 return mGroupID == groupID;
michael@0 1995 }
michael@0 1996
michael@0 1997 nsresult
michael@0 1998 nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
michael@0 1999 {
michael@0 2000 // Keep the object alive through a Finish() call.
michael@0 2001 nsCOMPtr<nsIOfflineCacheUpdate> kungFuDeathGrip(this);
michael@0 2002
michael@0 2003 mImplicitUpdate = nullptr;
michael@0 2004
michael@0 2005 NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE);
michael@0 2006 Finish();
michael@0 2007
michael@0 2008 return NS_OK;
michael@0 2009 }
michael@0 2010
michael@0 2011 void
michael@0 2012 nsOfflineCacheUpdate::OnByteProgress(uint64_t byteIncrement)
michael@0 2013 {
michael@0 2014 mByteProgress += byteIncrement;
michael@0 2015 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMPROGRESS);
michael@0 2016 }
michael@0 2017
michael@0 2018 nsresult
michael@0 2019 nsOfflineCacheUpdate::ScheduleImplicit()
michael@0 2020 {
michael@0 2021 if (mDocumentURIs.Count() == 0)
michael@0 2022 return NS_OK;
michael@0 2023
michael@0 2024 nsresult rv;
michael@0 2025
michael@0 2026 nsRefPtr<nsOfflineCacheUpdate> update = new nsOfflineCacheUpdate();
michael@0 2027 NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY);
michael@0 2028
michael@0 2029 nsAutoCString clientID;
michael@0 2030 if (mPreviousApplicationCache) {
michael@0 2031 rv = mPreviousApplicationCache->GetClientID(clientID);
michael@0 2032 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2033 }
michael@0 2034 else if (mApplicationCache) {
michael@0 2035 rv = mApplicationCache->GetClientID(clientID);
michael@0 2036 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2037 }
michael@0 2038 else {
michael@0 2039 NS_ERROR("Offline cache update not having set mApplicationCache?");
michael@0 2040 }
michael@0 2041
michael@0 2042 rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
michael@0 2043 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2044
michael@0 2045 for (int32_t i = 0; i < mDocumentURIs.Count(); i++) {
michael@0 2046 rv = update->AddURI(mDocumentURIs[i],
michael@0 2047 nsIApplicationCache::ITEM_IMPLICIT);
michael@0 2048 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2049 }
michael@0 2050
michael@0 2051 update->SetOwner(this);
michael@0 2052 rv = update->Begin();
michael@0 2053 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2054
michael@0 2055 mImplicitUpdate = update;
michael@0 2056
michael@0 2057 return NS_OK;
michael@0 2058 }
michael@0 2059
michael@0 2060 nsresult
michael@0 2061 nsOfflineCacheUpdate::FinishNoNotify()
michael@0 2062 {
michael@0 2063 LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
michael@0 2064
michael@0 2065 mState = STATE_FINISHED;
michael@0 2066
michael@0 2067 if (!mPartialUpdate && !mOnlyCheckUpdate) {
michael@0 2068 if (mSucceeded) {
michael@0 2069 nsIArray *namespaces = mManifestItem->GetNamespaces();
michael@0 2070 nsresult rv = mApplicationCache->AddNamespaces(namespaces);
michael@0 2071 if (NS_FAILED(rv)) {
michael@0 2072 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 2073 mSucceeded = false;
michael@0 2074 }
michael@0 2075
michael@0 2076 rv = mApplicationCache->Activate();
michael@0 2077 if (NS_FAILED(rv)) {
michael@0 2078 NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR);
michael@0 2079 mSucceeded = false;
michael@0 2080 }
michael@0 2081
michael@0 2082 AssociateDocuments(mApplicationCache);
michael@0 2083 }
michael@0 2084
michael@0 2085 if (mObsolete) {
michael@0 2086 nsCOMPtr<nsIApplicationCacheService> appCacheService =
michael@0 2087 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
michael@0 2088 if (appCacheService) {
michael@0 2089 nsAutoCString groupID;
michael@0 2090 mApplicationCache->GetGroupID(groupID);
michael@0 2091 appCacheService->DeactivateGroup(groupID);
michael@0 2092 }
michael@0 2093 }
michael@0 2094
michael@0 2095 if (!mSucceeded) {
michael@0 2096 // Update was not merged, mark all the loads as failures
michael@0 2097 for (uint32_t i = 0; i < mItems.Length(); i++) {
michael@0 2098 mItems[i]->Cancel();
michael@0 2099 }
michael@0 2100
michael@0 2101 mApplicationCache->Discard();
michael@0 2102 }
michael@0 2103 }
michael@0 2104
michael@0 2105 nsresult rv = NS_OK;
michael@0 2106
michael@0 2107 if (mOwner) {
michael@0 2108 rv = mOwner->UpdateFinished(this);
michael@0 2109 // mozilla::WeakPtr is missing some key features, like setting it to
michael@0 2110 // null explicitly.
michael@0 2111 mOwner = mozilla::WeakPtr<nsOfflineCacheUpdateOwner>();
michael@0 2112 }
michael@0 2113
michael@0 2114 return rv;
michael@0 2115 }
michael@0 2116
michael@0 2117 nsresult
michael@0 2118 nsOfflineCacheUpdate::Finish()
michael@0 2119 {
michael@0 2120 nsresult rv = FinishNoNotify();
michael@0 2121
michael@0 2122 NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED);
michael@0 2123
michael@0 2124 return rv;
michael@0 2125 }
michael@0 2126
michael@0 2127 void
michael@0 2128 nsOfflineCacheUpdate::AsyncFinishWithError()
michael@0 2129 {
michael@0 2130 NotifyState(nsOfflineCacheUpdate::STATE_ERROR);
michael@0 2131 Finish();
michael@0 2132 }
michael@0 2133
michael@0 2134 static nsresult
michael@0 2135 EvictOneOfCacheGroups(nsIApplicationCacheService *cacheService,
michael@0 2136 uint32_t count, const char * const *groups)
michael@0 2137 {
michael@0 2138 nsresult rv;
michael@0 2139 unsigned int i;
michael@0 2140
michael@0 2141 for (i = 0; i < count; i++) {
michael@0 2142 nsCOMPtr<nsIURI> uri;
michael@0 2143 rv = NS_NewURI(getter_AddRefs(uri), groups[i]);
michael@0 2144 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2145
michael@0 2146 nsDependentCString group_name(groups[i]);
michael@0 2147 nsCOMPtr<nsIApplicationCache> cache;
michael@0 2148 rv = cacheService->GetActiveCache(group_name, getter_AddRefs(cache));
michael@0 2149 // Maybe someone in another thread or process have deleted it.
michael@0 2150 if (NS_FAILED(rv) || !cache)
michael@0 2151 continue;
michael@0 2152
michael@0 2153 bool pinned;
michael@0 2154 rv = nsOfflineCacheUpdateService::OfflineAppPinnedForURI(uri,
michael@0 2155 nullptr,
michael@0 2156 &pinned);
michael@0 2157 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2158
michael@0 2159 if (!pinned) {
michael@0 2160 rv = cache->Discard();
michael@0 2161 return NS_OK;
michael@0 2162 }
michael@0 2163 }
michael@0 2164
michael@0 2165 return NS_ERROR_FILE_NOT_FOUND;
michael@0 2166 }
michael@0 2167
michael@0 2168 nsresult
michael@0 2169 nsOfflineCacheUpdate::EvictOneNonPinned()
michael@0 2170 {
michael@0 2171 nsresult rv;
michael@0 2172
michael@0 2173 nsCOMPtr<nsIApplicationCacheService> cacheService =
michael@0 2174 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
michael@0 2175 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2176
michael@0 2177 uint32_t count;
michael@0 2178 char **groups;
michael@0 2179 rv = cacheService->GetGroupsTimeOrdered(&count, &groups);
michael@0 2180 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2181
michael@0 2182 rv = EvictOneOfCacheGroups(cacheService, count, groups);
michael@0 2183
michael@0 2184 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, groups);
michael@0 2185 return rv;
michael@0 2186 }
michael@0 2187
michael@0 2188 //-----------------------------------------------------------------------------
michael@0 2189 // nsOfflineCacheUpdate::nsIOfflineCacheUpdate
michael@0 2190 //-----------------------------------------------------------------------------
michael@0 2191
michael@0 2192 NS_IMETHODIMP
michael@0 2193 nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
michael@0 2194 {
michael@0 2195 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2196
michael@0 2197 aUpdateDomain = mUpdateDomain;
michael@0 2198 return NS_OK;
michael@0 2199 }
michael@0 2200
michael@0 2201 NS_IMETHODIMP
michael@0 2202 nsOfflineCacheUpdate::GetStatus(uint16_t *aStatus)
michael@0 2203 {
michael@0 2204 switch (mState) {
michael@0 2205 case STATE_CHECKING :
michael@0 2206 *aStatus = nsIDOMOfflineResourceList::CHECKING;
michael@0 2207 return NS_OK;
michael@0 2208 case STATE_DOWNLOADING :
michael@0 2209 *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
michael@0 2210 return NS_OK;
michael@0 2211 default :
michael@0 2212 *aStatus = nsIDOMOfflineResourceList::IDLE;
michael@0 2213 return NS_OK;
michael@0 2214 }
michael@0 2215
michael@0 2216 return NS_ERROR_FAILURE;
michael@0 2217 }
michael@0 2218
michael@0 2219 NS_IMETHODIMP
michael@0 2220 nsOfflineCacheUpdate::GetPartial(bool *aPartial)
michael@0 2221 {
michael@0 2222 *aPartial = mPartialUpdate || mOnlyCheckUpdate;
michael@0 2223 return NS_OK;
michael@0 2224 }
michael@0 2225
michael@0 2226 NS_IMETHODIMP
michael@0 2227 nsOfflineCacheUpdate::GetManifestURI(nsIURI **aManifestURI)
michael@0 2228 {
michael@0 2229 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2230
michael@0 2231 NS_IF_ADDREF(*aManifestURI = mManifestURI);
michael@0 2232 return NS_OK;
michael@0 2233 }
michael@0 2234
michael@0 2235 NS_IMETHODIMP
michael@0 2236 nsOfflineCacheUpdate::GetSucceeded(bool *aSucceeded)
michael@0 2237 {
michael@0 2238 NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
michael@0 2239
michael@0 2240 *aSucceeded = mSucceeded;
michael@0 2241
michael@0 2242 return NS_OK;
michael@0 2243 }
michael@0 2244
michael@0 2245 NS_IMETHODIMP
michael@0 2246 nsOfflineCacheUpdate::GetIsUpgrade(bool *aIsUpgrade)
michael@0 2247 {
michael@0 2248 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2249
michael@0 2250 *aIsUpgrade = (mPreviousApplicationCache != nullptr);
michael@0 2251
michael@0 2252 return NS_OK;
michael@0 2253 }
michael@0 2254
michael@0 2255 nsresult
michael@0 2256 nsOfflineCacheUpdate::AddURI(nsIURI *aURI, uint32_t aType)
michael@0 2257 {
michael@0 2258 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2259
michael@0 2260 if (mState >= STATE_DOWNLOADING)
michael@0 2261 return NS_ERROR_NOT_AVAILABLE;
michael@0 2262
michael@0 2263 // Resource URIs must have the same scheme as the manifest.
michael@0 2264 nsAutoCString scheme;
michael@0 2265 aURI->GetScheme(scheme);
michael@0 2266
michael@0 2267 bool match;
michael@0 2268 if (NS_FAILED(mManifestURI->SchemeIs(scheme.get(), &match)) || !match)
michael@0 2269 return NS_ERROR_FAILURE;
michael@0 2270
michael@0 2271 // Don't fetch the same URI twice.
michael@0 2272 for (uint32_t i = 0; i < mItems.Length(); i++) {
michael@0 2273 bool equals;
michael@0 2274 if (NS_SUCCEEDED(mItems[i]->mURI->Equals(aURI, &equals)) && equals) {
michael@0 2275 // retain both types.
michael@0 2276 mItems[i]->mItemType |= aType;
michael@0 2277 return NS_OK;
michael@0 2278 }
michael@0 2279 }
michael@0 2280
michael@0 2281 nsRefPtr<nsOfflineCacheUpdateItem> item =
michael@0 2282 new nsOfflineCacheUpdateItem(aURI,
michael@0 2283 mDocumentURI,
michael@0 2284 mApplicationCache,
michael@0 2285 mPreviousApplicationCache,
michael@0 2286 aType);
michael@0 2287 if (!item) return NS_ERROR_OUT_OF_MEMORY;
michael@0 2288
michael@0 2289 mItems.AppendElement(item);
michael@0 2290 mAddedItems = true;
michael@0 2291
michael@0 2292 return NS_OK;
michael@0 2293 }
michael@0 2294
michael@0 2295 NS_IMETHODIMP
michael@0 2296 nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI)
michael@0 2297 {
michael@0 2298 if (GeckoProcessType_Default != XRE_GetProcessType())
michael@0 2299 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2300
michael@0 2301 // If this is a partial update and the resource is already in the
michael@0 2302 // cache, we should only mark the entry, not fetch it again.
michael@0 2303 if (mPartialUpdate) {
michael@0 2304 nsAutoCString key;
michael@0 2305 GetCacheKey(aURI, key);
michael@0 2306
michael@0 2307 uint32_t types;
michael@0 2308 nsresult rv = mApplicationCache->GetTypes(key, &types);
michael@0 2309 if (NS_SUCCEEDED(rv)) {
michael@0 2310 if (!(types & nsIApplicationCache::ITEM_DYNAMIC)) {
michael@0 2311 mApplicationCache->MarkEntry
michael@0 2312 (key, nsIApplicationCache::ITEM_DYNAMIC);
michael@0 2313 }
michael@0 2314 return NS_OK;
michael@0 2315 }
michael@0 2316 }
michael@0 2317
michael@0 2318 return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC);
michael@0 2319 }
michael@0 2320
michael@0 2321 NS_IMETHODIMP
michael@0 2322 nsOfflineCacheUpdate::Cancel()
michael@0 2323 {
michael@0 2324 LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
michael@0 2325
michael@0 2326 if ((mState == STATE_FINISHED) || (mState == STATE_CANCELLED)) {
michael@0 2327 return NS_ERROR_NOT_AVAILABLE;
michael@0 2328 }
michael@0 2329
michael@0 2330 mState = STATE_CANCELLED;
michael@0 2331 mSucceeded = false;
michael@0 2332
michael@0 2333 // Cancel all running downloads
michael@0 2334 for (uint32_t i = 0; i < mItems.Length(); ++i) {
michael@0 2335 nsOfflineCacheUpdateItem * item = mItems[i];
michael@0 2336
michael@0 2337 if (item->IsInProgress())
michael@0 2338 item->Cancel();
michael@0 2339 }
michael@0 2340
michael@0 2341 return NS_OK;
michael@0 2342 }
michael@0 2343
michael@0 2344 NS_IMETHODIMP
michael@0 2345 nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
michael@0 2346 bool aHoldWeak)
michael@0 2347 {
michael@0 2348 LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this));
michael@0 2349
michael@0 2350 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2351
michael@0 2352 if (aHoldWeak) {
michael@0 2353 nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
michael@0 2354 mWeakObservers.AppendObject(weakRef);
michael@0 2355 } else {
michael@0 2356 mObservers.AppendObject(aObserver);
michael@0 2357 }
michael@0 2358
michael@0 2359 return NS_OK;
michael@0 2360 }
michael@0 2361
michael@0 2362 NS_IMETHODIMP
michael@0 2363 nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
michael@0 2364 {
michael@0 2365 LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this));
michael@0 2366
michael@0 2367 NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
michael@0 2368
michael@0 2369 for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
michael@0 2370 nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
michael@0 2371 do_QueryReferent(mWeakObservers[i]);
michael@0 2372 if (observer == aObserver) {
michael@0 2373 mWeakObservers.RemoveObjectAt(i);
michael@0 2374 return NS_OK;
michael@0 2375 }
michael@0 2376 }
michael@0 2377
michael@0 2378 for (int32_t i = 0; i < mObservers.Count(); i++) {
michael@0 2379 if (mObservers[i] == aObserver) {
michael@0 2380 mObservers.RemoveObjectAt(i);
michael@0 2381 return NS_OK;
michael@0 2382 }
michael@0 2383 }
michael@0 2384
michael@0 2385 return NS_OK;
michael@0 2386 }
michael@0 2387
michael@0 2388 NS_IMETHODIMP
michael@0 2389 nsOfflineCacheUpdate::GetByteProgress(uint64_t * _result)
michael@0 2390 {
michael@0 2391 NS_ENSURE_ARG(_result);
michael@0 2392
michael@0 2393 *_result = mByteProgress;
michael@0 2394 return NS_OK;
michael@0 2395 }
michael@0 2396
michael@0 2397 NS_IMETHODIMP
michael@0 2398 nsOfflineCacheUpdate::Schedule()
michael@0 2399 {
michael@0 2400 LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
michael@0 2401
michael@0 2402 nsOfflineCacheUpdateService* service =
michael@0 2403 nsOfflineCacheUpdateService::EnsureService();
michael@0 2404
michael@0 2405 if (!service) {
michael@0 2406 return NS_ERROR_FAILURE;
michael@0 2407 }
michael@0 2408
michael@0 2409 return service->ScheduleUpdate(this);
michael@0 2410 }
michael@0 2411
michael@0 2412 NS_IMETHODIMP
michael@0 2413 nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
michael@0 2414 uint32_t aState)
michael@0 2415 {
michael@0 2416 if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) {
michael@0 2417 // Take the mSucceeded flag from the underlying update, we will be
michael@0 2418 // queried for it soon. mSucceeded of this update is false (manifest
michael@0 2419 // check failed) but the subsequent re-fetch update might succeed
michael@0 2420 bool succeeded;
michael@0 2421 aUpdate->GetSucceeded(&succeeded);
michael@0 2422 mSucceeded = succeeded;
michael@0 2423 }
michael@0 2424
michael@0 2425 NotifyState(aState);
michael@0 2426 if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED)
michael@0 2427 aUpdate->RemoveObserver(this);
michael@0 2428
michael@0 2429 return NS_OK;
michael@0 2430 }
michael@0 2431
michael@0 2432 NS_IMETHODIMP
michael@0 2433 nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache)
michael@0 2434 {
michael@0 2435 AssociateDocuments(applicationCache);
michael@0 2436 return NS_OK;
michael@0 2437 }
michael@0 2438
michael@0 2439 //-----------------------------------------------------------------------------
michael@0 2440 // nsOfflineCacheUpdate::nsIRunable
michael@0 2441 //-----------------------------------------------------------------------------
michael@0 2442
michael@0 2443 NS_IMETHODIMP
michael@0 2444 nsOfflineCacheUpdate::Run()
michael@0 2445 {
michael@0 2446 ProcessNextURI();
michael@0 2447 return NS_OK;
michael@0 2448 }

mercurial