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