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