nsDOMOfflineResourceList.cpp 22 KB

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