dom/src/offline/nsDOMOfflineResourceList.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #include "nsDOMOfflineResourceList.h"
michael@0 7 #include "nsIDOMEvent.h"
michael@0 8 #include "nsIScriptSecurityManager.h"
michael@0 9 #include "nsError.h"
michael@0 10 #include "mozilla/dom/DOMStringList.h"
michael@0 11 #include "nsIPrefetchService.h"
michael@0 12 #include "nsCPrefetchService.h"
michael@0 13 #include "nsNetUtil.h"
michael@0 14 #include "nsNetCID.h"
michael@0 15 #include "nsICacheSession.h"
michael@0 16 #include "nsICacheService.h"
michael@0 17 #include "nsIOfflineCacheUpdate.h"
michael@0 18 #include "nsAutoPtr.h"
michael@0 19 #include "nsContentUtils.h"
michael@0 20 #include "nsIObserverService.h"
michael@0 21 #include "nsIScriptGlobalObject.h"
michael@0 22 #include "nsIWebNavigation.h"
michael@0 23 #include "mozilla/dom/OfflineResourceListBinding.h"
michael@0 24 #include "mozilla/EventDispatcher.h"
michael@0 25 #include "mozilla/Preferences.h"
michael@0 26
michael@0 27 #include "nsXULAppAPI.h"
michael@0 28 #define IS_CHILD_PROCESS() \
michael@0 29 (GeckoProcessType_Default != XRE_GetProcessType())
michael@0 30
michael@0 31 using namespace mozilla;
michael@0 32 using namespace mozilla::dom;
michael@0 33
michael@0 34 // Event names
michael@0 35
michael@0 36 #define CHECKING_STR "checking"
michael@0 37 #define ERROR_STR "error"
michael@0 38 #define NOUPDATE_STR "noupdate"
michael@0 39 #define DOWNLOADING_STR "downloading"
michael@0 40 #define PROGRESS_STR "progress"
michael@0 41 #define CACHED_STR "cached"
michael@0 42 #define UPDATEREADY_STR "updateready"
michael@0 43 #define OBSOLETE_STR "obsolete"
michael@0 44
michael@0 45 // To prevent abuse of the resource list for data storage, the number
michael@0 46 // of offline urls and their length are limited.
michael@0 47
michael@0 48 static const char kMaxEntriesPref[] = "offline.max_site_resources";
michael@0 49 #define DEFAULT_MAX_ENTRIES 100
michael@0 50 #define MAX_URI_LENGTH 2048
michael@0 51
michael@0 52 //
michael@0 53 // nsDOMOfflineResourceList
michael@0 54 //
michael@0 55
michael@0 56 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList,
michael@0 57 DOMEventTargetHelper,
michael@0 58 mCacheUpdate,
michael@0 59 mPendingEvents)
michael@0 60
michael@0 61 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList)
michael@0 62 NS_INTERFACE_MAP_ENTRY(nsIDOMOfflineResourceList)
michael@0 63 NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
michael@0 64 NS_INTERFACE_MAP_ENTRY(nsIObserver)
michael@0 65 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 66 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 67
michael@0 68 NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
michael@0 69 NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, DOMEventTargetHelper)
michael@0 70
michael@0 71 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, checking)
michael@0 72 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, error)
michael@0 73 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, noupdate)
michael@0 74 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, downloading)
michael@0 75 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, progress)
michael@0 76 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, cached)
michael@0 77 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, updateready)
michael@0 78 NS_IMPL_EVENT_HANDLER(nsDOMOfflineResourceList, obsolete)
michael@0 79
michael@0 80 nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI,
michael@0 81 nsIURI *aDocumentURI,
michael@0 82 nsPIDOMWindow *aWindow)
michael@0 83 : DOMEventTargetHelper(aWindow)
michael@0 84 , mInitialized(false)
michael@0 85 , mManifestURI(aManifestURI)
michael@0 86 , mDocumentURI(aDocumentURI)
michael@0 87 , mExposeCacheUpdateStatus(true)
michael@0 88 , mStatus(nsIDOMOfflineResourceList::IDLE)
michael@0 89 , mCachedKeys(nullptr)
michael@0 90 , mCachedKeysCount(0)
michael@0 91 {
michael@0 92 }
michael@0 93
michael@0 94 nsDOMOfflineResourceList::~nsDOMOfflineResourceList()
michael@0 95 {
michael@0 96 ClearCachedKeys();
michael@0 97 }
michael@0 98
michael@0 99 JSObject*
michael@0 100 nsDOMOfflineResourceList::WrapObject(JSContext* aCx)
michael@0 101 {
michael@0 102 return OfflineResourceListBinding::Wrap(aCx, this);
michael@0 103 }
michael@0 104
michael@0 105 nsresult
michael@0 106 nsDOMOfflineResourceList::Init()
michael@0 107 {
michael@0 108 if (mInitialized) {
michael@0 109 return NS_OK;
michael@0 110 }
michael@0 111
michael@0 112 if (!mManifestURI) {
michael@0 113 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 114 }
michael@0 115
michael@0 116 mManifestURI->GetAsciiSpec(mManifestSpec);
michael@0 117
michael@0 118 nsresult rv = nsContentUtils::GetSecurityManager()->
michael@0 119 CheckSameOriginURI(mManifestURI, mDocumentURI, true);
michael@0 120 NS_ENSURE_SUCCESS(rv, rv);
michael@0 121
michael@0 122 // Dynamically-managed resources are stored as a separate ownership list
michael@0 123 // from the manifest.
michael@0 124 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
michael@0 125 if (!innerURI)
michael@0 126 return NS_ERROR_FAILURE;
michael@0 127
michael@0 128 if (!IS_CHILD_PROCESS())
michael@0 129 {
michael@0 130 mApplicationCacheService =
michael@0 131 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
michael@0 132 NS_ENSURE_SUCCESS(rv, rv);
michael@0 133
michael@0 134 // Check for in-progress cache updates
michael@0 135 nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
michael@0 136 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
michael@0 137 NS_ENSURE_SUCCESS(rv, rv);
michael@0 138
michael@0 139 uint32_t numUpdates;
michael@0 140 rv = cacheUpdateService->GetNumUpdates(&numUpdates);
michael@0 141 NS_ENSURE_SUCCESS(rv, rv);
michael@0 142
michael@0 143 for (uint32_t i = 0; i < numUpdates; i++) {
michael@0 144 nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
michael@0 145 rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
michael@0 146 NS_ENSURE_SUCCESS(rv, rv);
michael@0 147
michael@0 148 UpdateAdded(cacheUpdate);
michael@0 149 NS_ENSURE_SUCCESS(rv, rv);
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 // watch for new offline cache updates
michael@0 154 nsCOMPtr<nsIObserverService> observerService =
michael@0 155 mozilla::services::GetObserverService();
michael@0 156 if (!observerService)
michael@0 157 return NS_ERROR_FAILURE;
michael@0 158
michael@0 159 rv = observerService->AddObserver(this, "offline-cache-update-added", true);
michael@0 160 NS_ENSURE_SUCCESS(rv, rv);
michael@0 161 rv = observerService->AddObserver(this, "offline-cache-update-completed", true);
michael@0 162 NS_ENSURE_SUCCESS(rv, rv);
michael@0 163
michael@0 164 mInitialized = true;
michael@0 165
michael@0 166 return NS_OK;
michael@0 167 }
michael@0 168
michael@0 169 void
michael@0 170 nsDOMOfflineResourceList::Disconnect()
michael@0 171 {
michael@0 172 mPendingEvents.Clear();
michael@0 173
michael@0 174 if (mListenerManager) {
michael@0 175 mListenerManager->Disconnect();
michael@0 176 mListenerManager = nullptr;
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 //
michael@0 181 // nsDOMOfflineResourceList::nsIDOMOfflineResourceList
michael@0 182 //
michael@0 183
michael@0 184 already_AddRefed<DOMStringList>
michael@0 185 nsDOMOfflineResourceList::GetMozItems(ErrorResult& aRv)
michael@0 186 {
michael@0 187 if (IS_CHILD_PROCESS()) {
michael@0 188 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
michael@0 189 return nullptr;
michael@0 190 }
michael@0 191
michael@0 192 nsRefPtr<DOMStringList> items = new DOMStringList();
michael@0 193
michael@0 194 // If we are not associated with an application cache, return an
michael@0 195 // empty list.
michael@0 196 nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
michael@0 197 if (!appCache) {
michael@0 198 return items.forget();
michael@0 199 }
michael@0 200
michael@0 201 aRv = Init();
michael@0 202 if (aRv.Failed()) {
michael@0 203 return nullptr;
michael@0 204 }
michael@0 205
michael@0 206 uint32_t length;
michael@0 207 char **keys;
michael@0 208 aRv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
michael@0 209 &length, &keys);
michael@0 210 if (aRv.Failed()) {
michael@0 211 return nullptr;
michael@0 212 }
michael@0 213
michael@0 214 for (uint32_t i = 0; i < length; i++) {
michael@0 215 items->Add(NS_ConvertUTF8toUTF16(keys[i]));
michael@0 216 }
michael@0 217
michael@0 218 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, keys);
michael@0 219
michael@0 220 return items.forget();
michael@0 221 }
michael@0 222
michael@0 223 NS_IMETHODIMP
michael@0 224 nsDOMOfflineResourceList::GetMozItems(nsISupports** aItems)
michael@0 225 {
michael@0 226 ErrorResult rv;
michael@0 227 nsRefPtr<DOMStringList> items = GetMozItems(rv);
michael@0 228 items.forget(aItems);
michael@0 229 return rv.ErrorCode();
michael@0 230 }
michael@0 231
michael@0 232 NS_IMETHODIMP
michael@0 233 nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, bool* aExists)
michael@0 234 {
michael@0 235 if (IS_CHILD_PROCESS())
michael@0 236 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 237
michael@0 238 nsresult rv = Init();
michael@0 239 NS_ENSURE_SUCCESS(rv, rv);
michael@0 240
michael@0 241 nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
michael@0 242 if (!appCache) {
michael@0 243 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 244 }
michael@0 245
michael@0 246 nsAutoCString key;
michael@0 247 rv = GetCacheKey(aURI, key);
michael@0 248 NS_ENSURE_SUCCESS(rv, rv);
michael@0 249
michael@0 250 uint32_t types;
michael@0 251 rv = appCache->GetTypes(key, &types);
michael@0 252 if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
michael@0 253 *aExists = false;
michael@0 254 return NS_OK;
michael@0 255 }
michael@0 256 NS_ENSURE_SUCCESS(rv, rv);
michael@0 257
michael@0 258 *aExists = ((types & nsIApplicationCache::ITEM_DYNAMIC) != 0);
michael@0 259 return NS_OK;
michael@0 260 }
michael@0 261
michael@0 262 NS_IMETHODIMP
michael@0 263 nsDOMOfflineResourceList::GetMozLength(uint32_t *aLength)
michael@0 264 {
michael@0 265 if (IS_CHILD_PROCESS())
michael@0 266 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 267
michael@0 268 if (!mManifestURI) {
michael@0 269 *aLength = 0;
michael@0 270 return NS_OK;
michael@0 271 }
michael@0 272
michael@0 273 nsresult rv = Init();
michael@0 274 NS_ENSURE_SUCCESS(rv, rv);
michael@0 275
michael@0 276 rv = CacheKeys();
michael@0 277 NS_ENSURE_SUCCESS(rv, rv);
michael@0 278
michael@0 279 *aLength = mCachedKeysCount;
michael@0 280 return NS_OK;
michael@0 281 }
michael@0 282
michael@0 283 NS_IMETHODIMP
michael@0 284 nsDOMOfflineResourceList::MozItem(uint32_t aIndex, nsAString& aURI)
michael@0 285 {
michael@0 286 if (IS_CHILD_PROCESS())
michael@0 287 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 288
michael@0 289 nsresult rv = Init();
michael@0 290 NS_ENSURE_SUCCESS(rv, rv);
michael@0 291
michael@0 292 SetDOMStringToNull(aURI);
michael@0 293
michael@0 294 rv = CacheKeys();
michael@0 295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 296
michael@0 297 if (aIndex >= mCachedKeysCount)
michael@0 298 return NS_ERROR_NOT_AVAILABLE;
michael@0 299
michael@0 300 CopyUTF8toUTF16(mCachedKeys[aIndex], aURI);
michael@0 301
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304
michael@0 305 NS_IMETHODIMP
michael@0 306 nsDOMOfflineResourceList::MozAdd(const nsAString& aURI)
michael@0 307 {
michael@0 308 if (IS_CHILD_PROCESS())
michael@0 309 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 310
michael@0 311 nsresult rv = Init();
michael@0 312 NS_ENSURE_SUCCESS(rv, rv);
michael@0 313
michael@0 314 if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
michael@0 315 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 316 }
michael@0 317
michael@0 318 nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
michael@0 319 if (!appCache) {
michael@0 320 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 321 }
michael@0 322
michael@0 323 if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI;
michael@0 324
michael@0 325 // this will fail if the URI is not absolute
michael@0 326 nsCOMPtr<nsIURI> requestedURI;
michael@0 327 rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
michael@0 328 NS_ENSURE_SUCCESS(rv, rv);
michael@0 329
michael@0 330 nsAutoCString scheme;
michael@0 331 rv = requestedURI->GetScheme(scheme);
michael@0 332 NS_ENSURE_SUCCESS(rv, rv);
michael@0 333
michael@0 334 bool match;
michael@0 335 rv = mManifestURI->SchemeIs(scheme.get(), &match);
michael@0 336 NS_ENSURE_SUCCESS(rv, rv);
michael@0 337
michael@0 338 if (!match) {
michael@0 339 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 340 }
michael@0 341
michael@0 342 uint32_t length;
michael@0 343 rv = GetMozLength(&length);
michael@0 344 NS_ENSURE_SUCCESS(rv, rv);
michael@0 345 uint32_t maxEntries =
michael@0 346 Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES);
michael@0 347
michael@0 348 if (length > maxEntries) return NS_ERROR_NOT_AVAILABLE;
michael@0 349
michael@0 350 ClearCachedKeys();
michael@0 351
michael@0 352 nsCOMPtr<nsIOfflineCacheUpdate> update =
michael@0 353 do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
michael@0 354 NS_ENSURE_SUCCESS(rv, rv);
michael@0 355
michael@0 356 nsAutoCString clientID;
michael@0 357 rv = appCache->GetClientID(clientID);
michael@0 358 NS_ENSURE_SUCCESS(rv, rv);
michael@0 359
michael@0 360 rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
michael@0 361 NS_ENSURE_SUCCESS(rv, rv);
michael@0 362
michael@0 363 rv = update->AddDynamicURI(requestedURI);
michael@0 364 NS_ENSURE_SUCCESS(rv, rv);
michael@0 365
michael@0 366 rv = update->Schedule();
michael@0 367 NS_ENSURE_SUCCESS(rv, rv);
michael@0 368
michael@0 369 return NS_OK;
michael@0 370 }
michael@0 371
michael@0 372 NS_IMETHODIMP
michael@0 373 nsDOMOfflineResourceList::MozRemove(const nsAString& aURI)
michael@0 374 {
michael@0 375 if (IS_CHILD_PROCESS())
michael@0 376 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 377
michael@0 378 nsresult rv = Init();
michael@0 379 NS_ENSURE_SUCCESS(rv, rv);
michael@0 380
michael@0 381 if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
michael@0 382 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 383 }
michael@0 384
michael@0 385 nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
michael@0 386 if (!appCache) {
michael@0 387 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 388 }
michael@0 389
michael@0 390 nsAutoCString key;
michael@0 391 rv = GetCacheKey(aURI, key);
michael@0 392 NS_ENSURE_SUCCESS(rv, rv);
michael@0 393
michael@0 394 ClearCachedKeys();
michael@0 395
michael@0 396 // XXX: This is a race condition. remove() is specced to remove
michael@0 397 // from the currently associated application cache, but if this
michael@0 398 // happens during an update (or after an update, if we haven't
michael@0 399 // swapped yet), that remove() will be lost when the next update is
michael@0 400 // finished. Need to bring this issue up.
michael@0 401
michael@0 402 rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC);
michael@0 403 NS_ENSURE_SUCCESS(rv, rv);
michael@0 404
michael@0 405 return NS_OK;
michael@0 406 }
michael@0 407
michael@0 408 NS_IMETHODIMP
michael@0 409 nsDOMOfflineResourceList::GetStatus(uint16_t *aStatus)
michael@0 410 {
michael@0 411 nsresult rv = Init();
michael@0 412
michael@0 413 // Init may fail with INVALID_STATE_ERR if there is no manifest URI.
michael@0 414 // The status attribute should not throw that exception, convert it
michael@0 415 // to an UNCACHED.
michael@0 416 if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
michael@0 417 !nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
michael@0 418 *aStatus = nsIDOMOfflineResourceList::UNCACHED;
michael@0 419 return NS_OK;
michael@0 420 }
michael@0 421
michael@0 422 NS_ENSURE_SUCCESS(rv, rv);
michael@0 423
michael@0 424 // If this object is not associated with a cache, return UNCACHED
michael@0 425 nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
michael@0 426 if (!appCache) {
michael@0 427 *aStatus = nsIDOMOfflineResourceList::UNCACHED;
michael@0 428 return NS_OK;
michael@0 429 }
michael@0 430
michael@0 431
michael@0 432 // If there is an update in process, use its status.
michael@0 433 if (mCacheUpdate && mExposeCacheUpdateStatus) {
michael@0 434 rv = mCacheUpdate->GetStatus(aStatus);
michael@0 435 if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) {
michael@0 436 return NS_OK;
michael@0 437 }
michael@0 438 }
michael@0 439
michael@0 440 if (mAvailableApplicationCache) {
michael@0 441 *aStatus = nsIDOMOfflineResourceList::UPDATEREADY;
michael@0 442 return NS_OK;
michael@0 443 }
michael@0 444
michael@0 445 *aStatus = mStatus;
michael@0 446 return NS_OK;
michael@0 447 }
michael@0 448
michael@0 449 NS_IMETHODIMP
michael@0 450 nsDOMOfflineResourceList::Update()
michael@0 451 {
michael@0 452 nsresult rv = Init();
michael@0 453 NS_ENSURE_SUCCESS(rv, rv);
michael@0 454
michael@0 455 if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
michael@0 456 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 457 }
michael@0 458
michael@0 459 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
michael@0 460 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
michael@0 461 NS_ENSURE_SUCCESS(rv, rv);
michael@0 462
michael@0 463 nsCOMPtr<nsIDOMWindow> window =
michael@0 464 do_QueryInterface(GetOwner());
michael@0 465
michael@0 466 nsCOMPtr<nsIOfflineCacheUpdate> update;
michael@0 467 rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI,
michael@0 468 window, getter_AddRefs(update));
michael@0 469 NS_ENSURE_SUCCESS(rv, rv);
michael@0 470
michael@0 471 return NS_OK;
michael@0 472 }
michael@0 473
michael@0 474 NS_IMETHODIMP
michael@0 475 nsDOMOfflineResourceList::SwapCache()
michael@0 476 {
michael@0 477 nsresult rv = Init();
michael@0 478 NS_ENSURE_SUCCESS(rv, rv);
michael@0 479
michael@0 480 if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
michael@0 481 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 482 }
michael@0 483
michael@0 484 nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
michael@0 485 if (!currentAppCache) {
michael@0 486 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 487 }
michael@0 488
michael@0 489 // Check the current and potentially newly available cache are not identical.
michael@0 490 if (mAvailableApplicationCache == currentAppCache) {
michael@0 491 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 492 }
michael@0 493
michael@0 494 if (mAvailableApplicationCache) {
michael@0 495 nsCString currClientId, availClientId;
michael@0 496 currentAppCache->GetClientID(currClientId);
michael@0 497 mAvailableApplicationCache->GetClientID(availClientId);
michael@0 498 if (availClientId == currClientId)
michael@0 499 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 500 } else if (mStatus != OBSOLETE) {
michael@0 501 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 502 }
michael@0 503
michael@0 504 ClearCachedKeys();
michael@0 505
michael@0 506 nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
michael@0 507 GetDocumentAppCacheContainer();
michael@0 508
michael@0 509 // In the case of an obsolete cache group, newAppCache might be null.
michael@0 510 // We will disassociate from the cache in that case.
michael@0 511 if (appCacheContainer) {
michael@0 512 rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache);
michael@0 513 NS_ENSURE_SUCCESS(rv, rv);
michael@0 514 }
michael@0 515
michael@0 516 mAvailableApplicationCache = nullptr;
michael@0 517 mStatus = nsIDOMOfflineResourceList::IDLE;
michael@0 518
michael@0 519 return NS_OK;
michael@0 520 }
michael@0 521
michael@0 522 //
michael@0 523 // nsDOMOfflineResourceList::nsIDOMEventTarget
michael@0 524 //
michael@0 525
michael@0 526 void
michael@0 527 nsDOMOfflineResourceList::FirePendingEvents()
michael@0 528 {
michael@0 529 for (int32_t i = 0; i < mPendingEvents.Count(); ++i) {
michael@0 530 bool dummy;
michael@0 531 nsCOMPtr<nsIDOMEvent> event = mPendingEvents[i];
michael@0 532 DispatchEvent(event, &dummy);
michael@0 533 }
michael@0 534 mPendingEvents.Clear();
michael@0 535 }
michael@0 536
michael@0 537 nsresult
michael@0 538 nsDOMOfflineResourceList::SendEvent(const nsAString &aEventName)
michael@0 539 {
michael@0 540 // Don't send events to closed windows
michael@0 541 if (!GetOwner()) {
michael@0 542 return NS_OK;
michael@0 543 }
michael@0 544
michael@0 545 if (!GetOwner()->GetDocShell()) {
michael@0 546 return NS_OK;
michael@0 547 }
michael@0 548
michael@0 549 nsCOMPtr<nsIDOMEvent> event;
michael@0 550 nsresult rv = EventDispatcher::CreateEvent(this, nullptr, nullptr,
michael@0 551 NS_LITERAL_STRING("Events"),
michael@0 552 getter_AddRefs(event));
michael@0 553 NS_ENSURE_SUCCESS(rv, rv);
michael@0 554 event->InitEvent(aEventName, false, true);
michael@0 555
michael@0 556 // We assume anyone that managed to call SendEvent is trusted
michael@0 557 event->SetTrusted(true);
michael@0 558
michael@0 559 // If the window is frozen or we're still catching up on events that were
michael@0 560 // queued while frozen, save the event for later.
michael@0 561 if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) {
michael@0 562 mPendingEvents.AppendObject(event);
michael@0 563 return NS_OK;
michael@0 564 }
michael@0 565
michael@0 566 bool dummy;
michael@0 567 DispatchEvent(event, &dummy);
michael@0 568
michael@0 569 return NS_OK;
michael@0 570 }
michael@0 571
michael@0 572
michael@0 573 //
michael@0 574 // nsDOMOfflineResourceList::nsIObserver
michael@0 575 //
michael@0 576 NS_IMETHODIMP
michael@0 577 nsDOMOfflineResourceList::Observe(nsISupports *aSubject,
michael@0 578 const char *aTopic,
michael@0 579 const char16_t *aData)
michael@0 580 {
michael@0 581 if (!strcmp(aTopic, "offline-cache-update-added")) {
michael@0 582 nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
michael@0 583 if (update) {
michael@0 584 UpdateAdded(update);
michael@0 585 }
michael@0 586 } else if (!strcmp(aTopic, "offline-cache-update-completed")) {
michael@0 587 nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
michael@0 588 if (update) {
michael@0 589 UpdateCompleted(update);
michael@0 590 }
michael@0 591 }
michael@0 592
michael@0 593 return NS_OK;
michael@0 594 }
michael@0 595
michael@0 596 //
michael@0 597 // nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver
michael@0 598 //
michael@0 599 NS_IMETHODIMP
michael@0 600 nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
michael@0 601 uint32_t event)
michael@0 602 {
michael@0 603 mExposeCacheUpdateStatus =
michael@0 604 (event == STATE_CHECKING) ||
michael@0 605 (event == STATE_DOWNLOADING) ||
michael@0 606 (event == STATE_ITEMSTARTED) ||
michael@0 607 (event == STATE_ITEMCOMPLETED) ||
michael@0 608 // During notification of "obsolete" we must expose state of the update
michael@0 609 (event == STATE_OBSOLETE);
michael@0 610
michael@0 611 switch (event) {
michael@0 612 case STATE_ERROR:
michael@0 613 SendEvent(NS_LITERAL_STRING(ERROR_STR));
michael@0 614 break;
michael@0 615 case STATE_CHECKING:
michael@0 616 SendEvent(NS_LITERAL_STRING(CHECKING_STR));
michael@0 617 break;
michael@0 618 case STATE_NOUPDATE:
michael@0 619 SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
michael@0 620 break;
michael@0 621 case STATE_OBSOLETE:
michael@0 622 mStatus = nsIDOMOfflineResourceList::OBSOLETE;
michael@0 623 mAvailableApplicationCache = nullptr;
michael@0 624 SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
michael@0 625 break;
michael@0 626 case STATE_DOWNLOADING:
michael@0 627 SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
michael@0 628 break;
michael@0 629 case STATE_ITEMSTARTED:
michael@0 630 SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
michael@0 631 break;
michael@0 632 case STATE_ITEMCOMPLETED:
michael@0 633 // Nothing to do here...
michael@0 634 break;
michael@0 635 }
michael@0 636
michael@0 637 return NS_OK;
michael@0 638 }
michael@0 639
michael@0 640 NS_IMETHODIMP
michael@0 641 nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
michael@0 642 {
michael@0 643 nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
michael@0 644 if (currentAppCache) {
michael@0 645 // Document already has a cache, we cannot override it. swapCache is
michael@0 646 // here to do it on demand.
michael@0 647
michael@0 648 // If the newly available cache is identical to the current cache, then
michael@0 649 // just ignore this event.
michael@0 650 if (aApplicationCache == currentAppCache) {
michael@0 651 return NS_OK;
michael@0 652 }
michael@0 653
michael@0 654 nsCString currClientId, availClientId;
michael@0 655 currentAppCache->GetClientID(currClientId);
michael@0 656 aApplicationCache->GetClientID(availClientId);
michael@0 657 if (availClientId == currClientId) {
michael@0 658 return NS_OK;
michael@0 659 }
michael@0 660
michael@0 661 mAvailableApplicationCache = aApplicationCache;
michael@0 662 return NS_OK;
michael@0 663 }
michael@0 664
michael@0 665 nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
michael@0 666 GetDocumentAppCacheContainer();
michael@0 667
michael@0 668 if (appCacheContainer) {
michael@0 669 appCacheContainer->SetApplicationCache(aApplicationCache);
michael@0 670 }
michael@0 671
michael@0 672 mAvailableApplicationCache = nullptr;
michael@0 673 return NS_OK;
michael@0 674 }
michael@0 675
michael@0 676 nsresult
michael@0 677 nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
michael@0 678 {
michael@0 679 nsCOMPtr<nsIURI> requestedURI;
michael@0 680 nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
michael@0 681 NS_ENSURE_SUCCESS(rv, rv);
michael@0 682
michael@0 683 return GetCacheKey(requestedURI, aKey);
michael@0 684 }
michael@0 685
michael@0 686 nsresult
michael@0 687 nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate)
michael@0 688 {
michael@0 689 // Ignore partial updates.
michael@0 690 bool partial;
michael@0 691 nsresult rv = aUpdate->GetPartial(&partial);
michael@0 692 NS_ENSURE_SUCCESS(rv, rv);
michael@0 693
michael@0 694 if (partial) {
michael@0 695 return NS_OK;
michael@0 696 }
michael@0 697
michael@0 698 nsCOMPtr<nsIURI> updateURI;
michael@0 699 rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI));
michael@0 700 NS_ENSURE_SUCCESS(rv, rv);
michael@0 701
michael@0 702 bool equals;
michael@0 703 rv = updateURI->Equals(mManifestURI, &equals);
michael@0 704 NS_ENSURE_SUCCESS(rv, rv);
michael@0 705
michael@0 706 if (!equals) {
michael@0 707 // This update doesn't belong to us
michael@0 708 return NS_OK;
michael@0 709 }
michael@0 710
michael@0 711 NS_ENSURE_TRUE(!mCacheUpdate, NS_ERROR_FAILURE);
michael@0 712
michael@0 713 // We don't need to emit signals here. Updates are either added
michael@0 714 // when they are scheduled (in which case they are always IDLE) or
michael@0 715 // they are added when the applicationCache object is initialized, so there
michael@0 716 // are no listeners to accept signals anyway.
michael@0 717
michael@0 718 mCacheUpdate = aUpdate;
michael@0 719 mCacheUpdate->AddObserver(this, true);
michael@0 720
michael@0 721 return NS_OK;
michael@0 722 }
michael@0 723
michael@0 724 already_AddRefed<nsIApplicationCacheContainer>
michael@0 725 nsDOMOfflineResourceList::GetDocumentAppCacheContainer()
michael@0 726 {
michael@0 727 nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner());
michael@0 728 if (!webnav) {
michael@0 729 return nullptr;
michael@0 730 }
michael@0 731
michael@0 732 nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
michael@0 733 do_GetInterface(webnav);
michael@0 734 return appCacheContainer.forget();
michael@0 735 }
michael@0 736
michael@0 737 already_AddRefed<nsIApplicationCache>
michael@0 738 nsDOMOfflineResourceList::GetDocumentAppCache()
michael@0 739 {
michael@0 740 nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
michael@0 741 GetDocumentAppCacheContainer();
michael@0 742
michael@0 743 if (appCacheContainer) {
michael@0 744 nsCOMPtr<nsIApplicationCache> applicationCache;
michael@0 745 appCacheContainer->GetApplicationCache(
michael@0 746 getter_AddRefs(applicationCache));
michael@0 747 return applicationCache.forget();
michael@0 748 }
michael@0 749
michael@0 750 return nullptr;
michael@0 751 }
michael@0 752
michael@0 753 nsresult
michael@0 754 nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
michael@0 755 {
michael@0 756 if (aUpdate != mCacheUpdate) {
michael@0 757 // This isn't the update we're watching.
michael@0 758 return NS_OK;
michael@0 759 }
michael@0 760
michael@0 761 bool partial;
michael@0 762 mCacheUpdate->GetPartial(&partial);
michael@0 763 bool isUpgrade;
michael@0 764 mCacheUpdate->GetIsUpgrade(&isUpgrade);
michael@0 765
michael@0 766 bool succeeded;
michael@0 767 nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
michael@0 768
michael@0 769 mCacheUpdate->RemoveObserver(this);
michael@0 770 mCacheUpdate = nullptr;
michael@0 771
michael@0 772 if (NS_SUCCEEDED(rv) && succeeded && !partial) {
michael@0 773 mStatus = nsIDOMOfflineResourceList::IDLE;
michael@0 774 if (isUpgrade) {
michael@0 775 SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR));
michael@0 776 } else {
michael@0 777 SendEvent(NS_LITERAL_STRING(CACHED_STR));
michael@0 778 }
michael@0 779 }
michael@0 780
michael@0 781 return NS_OK;
michael@0 782 }
michael@0 783
michael@0 784 nsresult
michael@0 785 nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
michael@0 786 {
michael@0 787 nsresult rv = aURI->GetAsciiSpec(aKey);
michael@0 788 NS_ENSURE_SUCCESS(rv, rv);
michael@0 789
michael@0 790 // url fragments aren't used in cache keys
michael@0 791 nsAutoCString::const_iterator specStart, specEnd;
michael@0 792 aKey.BeginReading(specStart);
michael@0 793 aKey.EndReading(specEnd);
michael@0 794 if (FindCharInReadable('#', specStart, specEnd)) {
michael@0 795 aKey.BeginReading(specEnd);
michael@0 796 aKey = Substring(specEnd, specStart);
michael@0 797 }
michael@0 798
michael@0 799 return NS_OK;
michael@0 800 }
michael@0 801
michael@0 802 nsresult
michael@0 803 nsDOMOfflineResourceList::CacheKeys()
michael@0 804 {
michael@0 805 if (IS_CHILD_PROCESS())
michael@0 806 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 807
michael@0 808 if (mCachedKeys)
michael@0 809 return NS_OK;
michael@0 810
michael@0 811 nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
michael@0 812 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
michael@0 813 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
michael@0 814
michael@0 815 uint32_t appId = 0;
michael@0 816 bool inBrowser = false;
michael@0 817 if (loadContext) {
michael@0 818 loadContext->GetAppId(&appId);
michael@0 819 loadContext->GetIsInBrowserElement(&inBrowser);
michael@0 820 }
michael@0 821
michael@0 822 nsAutoCString groupID;
michael@0 823 mApplicationCacheService->BuildGroupIDForApp(
michael@0 824 mManifestURI, appId, inBrowser, groupID);
michael@0 825
michael@0 826 nsCOMPtr<nsIApplicationCache> appCache;
michael@0 827 mApplicationCacheService->GetActiveCache(groupID,
michael@0 828 getter_AddRefs(appCache));
michael@0 829
michael@0 830 if (!appCache) {
michael@0 831 return NS_ERROR_DOM_INVALID_STATE_ERR;
michael@0 832 }
michael@0 833
michael@0 834 return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
michael@0 835 &mCachedKeysCount, &mCachedKeys);
michael@0 836 }
michael@0 837
michael@0 838 void
michael@0 839 nsDOMOfflineResourceList::ClearCachedKeys()
michael@0 840 {
michael@0 841 if (mCachedKeys) {
michael@0 842 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCachedKeysCount, mCachedKeys);
michael@0 843 mCachedKeys = nullptr;
michael@0 844 mCachedKeysCount = 0;
michael@0 845 }
michael@0 846 }

mercurial