1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Corporation
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Dave Camp <dcamp@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsDOMOfflineResourceList.h"
40 : #include "nsDOMClassInfoID.h"
41 : #include "nsIScriptSecurityManager.h"
42 : #include "nsDOMError.h"
43 : #include "nsDOMLists.h"
44 : #include "nsIPrefetchService.h"
45 : #include "nsCPrefetchService.h"
46 : #include "nsNetUtil.h"
47 : #include "nsNetCID.h"
48 : #include "nsICacheSession.h"
49 : #include "nsICacheService.h"
50 : #include "nsIOfflineCacheUpdate.h"
51 : #include "nsIDOMLoadStatus.h"
52 : #include "nsAutoPtr.h"
53 : #include "nsContentUtils.h"
54 : #include "nsIJSContextStack.h"
55 : #include "nsEventDispatcher.h"
56 : #include "nsIPrivateDOMEvent.h"
57 : #include "nsIObserverService.h"
58 : #include "nsIScriptGlobalObject.h"
59 : #include "nsIWebNavigation.h"
60 : #include "mozilla/Preferences.h"
61 :
62 : #include "nsXULAppAPI.h"
63 : #define IS_CHILD_PROCESS() \
64 : (GeckoProcessType_Default != XRE_GetProcessType())
65 :
66 : using namespace mozilla;
67 :
68 : // Event names
69 :
70 : #define CHECKING_STR "checking"
71 : #define ERROR_STR "error"
72 : #define NOUPDATE_STR "noupdate"
73 : #define DOWNLOADING_STR "downloading"
74 : #define PROGRESS_STR "progress"
75 : #define CACHED_STR "cached"
76 : #define UPDATEREADY_STR "updateready"
77 : #define OBSOLETE_STR "obsolete"
78 :
79 : // To prevent abuse of the resource list for data storage, the number
80 : // of offline urls and their length are limited.
81 :
82 : static const char kMaxEntriesPref[] = "offline.max_site_resources";
83 : #define DEFAULT_MAX_ENTRIES 100
84 : #define MAX_URI_LENGTH 2048
85 :
86 : //
87 : // nsDOMOfflineResourceList
88 : //
89 :
90 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMOfflineResourceList)
91 :
92 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMOfflineResourceList,
93 : nsDOMEventTargetHelper)
94 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCacheUpdate)
95 :
96 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCheckingListener)
97 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
98 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnNoUpdateListener)
99 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnDownloadingListener)
100 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
101 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCachedListener)
102 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUpdateReadyListener)
103 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnObsoleteListener)
104 :
105 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingEvents);
106 :
107 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
108 :
109 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMOfflineResourceList,
110 : nsDOMEventTargetHelper)
111 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCacheUpdate)
112 :
113 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCheckingListener)
114 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
115 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnNoUpdateListener)
116 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnDownloadingListener)
117 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
118 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCachedListener)
119 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUpdateReadyListener)
120 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnObsoleteListener)
121 :
122 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingEvents)
123 :
124 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
125 :
126 : DOMCI_DATA(OfflineResourceList, nsDOMOfflineResourceList)
127 :
128 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMOfflineResourceList)
129 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMOfflineResourceList)
130 0 : NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
131 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
132 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
133 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(OfflineResourceList)
134 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
135 :
136 0 : NS_IMPL_ADDREF_INHERITED(nsDOMOfflineResourceList, nsDOMEventTargetHelper)
137 0 : NS_IMPL_RELEASE_INHERITED(nsDOMOfflineResourceList, nsDOMEventTargetHelper)
138 :
139 0 : nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI,
140 : nsIURI *aDocumentURI,
141 : nsPIDOMWindow *aWindow)
142 : : mInitialized(false)
143 : , mManifestURI(aManifestURI)
144 : , mDocumentURI(aDocumentURI)
145 : , mExposeCacheUpdateStatus(true)
146 : , mStatus(nsIDOMOfflineResourceList::IDLE)
147 : , mCachedKeys(nsnull)
148 0 : , mCachedKeysCount(0)
149 : {
150 0 : BindToOwner(aWindow);
151 0 : }
152 :
153 0 : nsDOMOfflineResourceList::~nsDOMOfflineResourceList()
154 : {
155 0 : ClearCachedKeys();
156 0 : }
157 :
158 : nsresult
159 0 : nsDOMOfflineResourceList::Init()
160 : {
161 0 : if (mInitialized) {
162 0 : return NS_OK;
163 : }
164 :
165 0 : if (!mManifestURI) {
166 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
167 : }
168 :
169 0 : mManifestURI->GetAsciiSpec(mManifestSpec);
170 :
171 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
172 0 : CheckSameOriginURI(mManifestURI, mDocumentURI, true);
173 0 : NS_ENSURE_SUCCESS(rv, rv);
174 :
175 : // Dynamically-managed resources are stored as a separate ownership list
176 : // from the manifest.
177 0 : nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
178 0 : if (!innerURI)
179 0 : return NS_ERROR_FAILURE;
180 :
181 0 : if (!IS_CHILD_PROCESS())
182 : {
183 : mApplicationCacheService =
184 0 : do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
185 0 : NS_ENSURE_SUCCESS(rv, rv);
186 :
187 : // Check for in-progress cache updates
188 : nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
189 0 : do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
190 0 : NS_ENSURE_SUCCESS(rv, rv);
191 :
192 : PRUint32 numUpdates;
193 0 : rv = cacheUpdateService->GetNumUpdates(&numUpdates);
194 0 : NS_ENSURE_SUCCESS(rv, rv);
195 :
196 0 : for (PRUint32 i = 0; i < numUpdates; i++) {
197 0 : nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
198 0 : rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
199 0 : NS_ENSURE_SUCCESS(rv, rv);
200 :
201 0 : UpdateAdded(cacheUpdate);
202 0 : NS_ENSURE_SUCCESS(rv, rv);
203 : }
204 : }
205 :
206 : // watch for new offline cache updates
207 : nsCOMPtr<nsIObserverService> observerService =
208 0 : mozilla::services::GetObserverService();
209 0 : if (!observerService)
210 0 : return NS_ERROR_FAILURE;
211 :
212 0 : rv = observerService->AddObserver(this, "offline-cache-update-added", true);
213 0 : NS_ENSURE_SUCCESS(rv, rv);
214 0 : rv = observerService->AddObserver(this, "offline-cache-update-completed", true);
215 0 : NS_ENSURE_SUCCESS(rv, rv);
216 :
217 0 : mInitialized = true;
218 :
219 0 : return NS_OK;
220 : }
221 :
222 : void
223 0 : nsDOMOfflineResourceList::Disconnect()
224 : {
225 0 : mOnCheckingListener = nsnull;
226 0 : mOnErrorListener = nsnull;
227 0 : mOnNoUpdateListener = nsnull;
228 0 : mOnDownloadingListener = nsnull;
229 0 : mOnProgressListener = nsnull;
230 0 : mOnCachedListener = nsnull;
231 0 : mOnUpdateReadyListener = nsnull;
232 0 : mOnObsoleteListener = nsnull;
233 :
234 0 : mPendingEvents.Clear();
235 :
236 0 : if (mListenerManager) {
237 0 : mListenerManager->Disconnect();
238 0 : mListenerManager = nsnull;
239 : }
240 0 : }
241 :
242 : //
243 : // nsDOMOfflineResourceList::nsIDOMOfflineResourceList
244 : //
245 :
246 : NS_IMETHODIMP
247 0 : nsDOMOfflineResourceList::GetMozItems(nsIDOMDOMStringList **aItems)
248 : {
249 0 : if (IS_CHILD_PROCESS())
250 0 : return NS_ERROR_NOT_IMPLEMENTED;
251 :
252 0 : *aItems = nsnull;
253 :
254 0 : nsRefPtr<nsDOMStringList> items = new nsDOMStringList();
255 0 : NS_ENSURE_TRUE(items, NS_ERROR_OUT_OF_MEMORY);
256 :
257 : // If we are not associated with an application cache, return an
258 : // empty list.
259 0 : nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
260 0 : if (!appCache) {
261 0 : NS_ADDREF(*aItems = items);
262 0 : return NS_OK;
263 : }
264 :
265 0 : nsresult rv = Init();
266 0 : NS_ENSURE_SUCCESS(rv, rv);
267 :
268 : PRUint32 length;
269 : char **keys;
270 0 : rv = appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
271 0 : &length, &keys);
272 0 : NS_ENSURE_SUCCESS(rv, rv);
273 :
274 0 : for (PRUint32 i = 0; i < length; i++) {
275 0 : items->Add(NS_ConvertUTF8toUTF16(keys[i]));
276 : }
277 :
278 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(length, keys);
279 :
280 0 : NS_ADDREF(*aItems = items);
281 0 : return NS_OK;
282 : }
283 :
284 : NS_IMETHODIMP
285 0 : nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, bool* aExists)
286 : {
287 0 : if (IS_CHILD_PROCESS())
288 0 : return NS_ERROR_NOT_IMPLEMENTED;
289 :
290 0 : nsresult rv = Init();
291 0 : NS_ENSURE_SUCCESS(rv, rv);
292 :
293 0 : nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
294 0 : if (!appCache) {
295 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
296 : }
297 :
298 0 : nsCAutoString key;
299 0 : rv = GetCacheKey(aURI, key);
300 0 : NS_ENSURE_SUCCESS(rv, rv);
301 :
302 : PRUint32 types;
303 0 : rv = appCache->GetTypes(key, &types);
304 0 : if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
305 0 : *aExists = false;
306 0 : return NS_OK;
307 : }
308 0 : NS_ENSURE_SUCCESS(rv, rv);
309 :
310 0 : *aExists = ((types & nsIApplicationCache::ITEM_DYNAMIC) != 0);
311 0 : return NS_OK;
312 : }
313 :
314 : NS_IMETHODIMP
315 0 : nsDOMOfflineResourceList::GetMozLength(PRUint32 *aLength)
316 : {
317 0 : if (IS_CHILD_PROCESS())
318 0 : return NS_ERROR_NOT_IMPLEMENTED;
319 :
320 0 : if (!mManifestURI) {
321 0 : *aLength = 0;
322 0 : return NS_OK;
323 : }
324 :
325 0 : nsresult rv = Init();
326 0 : NS_ENSURE_SUCCESS(rv, rv);
327 :
328 0 : rv = CacheKeys();
329 0 : NS_ENSURE_SUCCESS(rv, rv);
330 :
331 0 : *aLength = mCachedKeysCount;
332 0 : return NS_OK;
333 : }
334 :
335 : NS_IMETHODIMP
336 0 : nsDOMOfflineResourceList::MozItem(PRUint32 aIndex, nsAString& aURI)
337 : {
338 0 : if (IS_CHILD_PROCESS())
339 0 : return NS_ERROR_NOT_IMPLEMENTED;
340 :
341 0 : nsresult rv = Init();
342 0 : NS_ENSURE_SUCCESS(rv, rv);
343 :
344 0 : SetDOMStringToNull(aURI);
345 :
346 0 : rv = CacheKeys();
347 0 : NS_ENSURE_SUCCESS(rv, rv);
348 :
349 0 : if (aIndex >= mCachedKeysCount)
350 0 : return NS_ERROR_NOT_AVAILABLE;
351 :
352 0 : CopyUTF8toUTF16(mCachedKeys[aIndex], aURI);
353 :
354 0 : return NS_OK;
355 : }
356 :
357 : NS_IMETHODIMP
358 0 : nsDOMOfflineResourceList::MozAdd(const nsAString& aURI)
359 : {
360 0 : if (IS_CHILD_PROCESS())
361 0 : return NS_ERROR_NOT_IMPLEMENTED;
362 :
363 0 : nsresult rv = Init();
364 0 : NS_ENSURE_SUCCESS(rv, rv);
365 :
366 0 : if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
367 0 : return NS_ERROR_DOM_SECURITY_ERR;
368 : }
369 :
370 0 : nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
371 0 : if (!appCache) {
372 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
373 : }
374 :
375 0 : if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI;
376 :
377 : // this will fail if the URI is not absolute
378 0 : nsCOMPtr<nsIURI> requestedURI;
379 0 : rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
380 0 : NS_ENSURE_SUCCESS(rv, rv);
381 :
382 0 : nsCAutoString scheme;
383 0 : rv = requestedURI->GetScheme(scheme);
384 0 : NS_ENSURE_SUCCESS(rv, rv);
385 :
386 : bool match;
387 0 : rv = mManifestURI->SchemeIs(scheme.get(), &match);
388 0 : NS_ENSURE_SUCCESS(rv, rv);
389 :
390 0 : if (!match) {
391 0 : return NS_ERROR_DOM_SECURITY_ERR;
392 : }
393 :
394 : PRUint32 length;
395 0 : rv = GetMozLength(&length);
396 0 : NS_ENSURE_SUCCESS(rv, rv);
397 : PRUint32 maxEntries =
398 0 : Preferences::GetUint(kMaxEntriesPref, DEFAULT_MAX_ENTRIES);
399 :
400 0 : if (length > maxEntries) return NS_ERROR_NOT_AVAILABLE;
401 :
402 0 : ClearCachedKeys();
403 :
404 : nsCOMPtr<nsIOfflineCacheUpdate> update =
405 0 : do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
406 0 : NS_ENSURE_SUCCESS(rv, rv);
407 :
408 0 : nsCAutoString clientID;
409 0 : rv = appCache->GetClientID(clientID);
410 0 : NS_ENSURE_SUCCESS(rv, rv);
411 :
412 0 : rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
413 0 : NS_ENSURE_SUCCESS(rv, rv);
414 :
415 0 : rv = update->AddDynamicURI(requestedURI);
416 0 : NS_ENSURE_SUCCESS(rv, rv);
417 :
418 0 : rv = update->Schedule();
419 0 : NS_ENSURE_SUCCESS(rv, rv);
420 :
421 0 : return NS_OK;
422 : }
423 :
424 : NS_IMETHODIMP
425 0 : nsDOMOfflineResourceList::MozRemove(const nsAString& aURI)
426 : {
427 0 : if (IS_CHILD_PROCESS())
428 0 : return NS_ERROR_NOT_IMPLEMENTED;
429 :
430 0 : nsresult rv = Init();
431 0 : NS_ENSURE_SUCCESS(rv, rv);
432 :
433 0 : if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
434 0 : return NS_ERROR_DOM_SECURITY_ERR;
435 : }
436 :
437 0 : nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
438 0 : if (!appCache) {
439 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
440 : }
441 :
442 0 : nsCAutoString key;
443 0 : rv = GetCacheKey(aURI, key);
444 0 : NS_ENSURE_SUCCESS(rv, rv);
445 :
446 0 : ClearCachedKeys();
447 :
448 : // XXX: This is a race condition. remove() is specced to remove
449 : // from the currently associated application cache, but if this
450 : // happens during an update (or after an update, if we haven't
451 : // swapped yet), that remove() will be lost when the next update is
452 : // finished. Need to bring this issue up.
453 :
454 0 : rv = appCache->UnmarkEntry(key, nsIApplicationCache::ITEM_DYNAMIC);
455 0 : NS_ENSURE_SUCCESS(rv, rv);
456 :
457 0 : return NS_OK;
458 : }
459 :
460 : NS_IMETHODIMP
461 0 : nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus)
462 : {
463 0 : nsresult rv = Init();
464 :
465 : // Init may fail with INVALID_STATE_ERR if there is no manifest URI.
466 : // The status attribute should not throw that exception, convert it
467 : // to an UNCACHED.
468 0 : if (rv == NS_ERROR_DOM_INVALID_STATE_ERR ||
469 0 : !nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
470 0 : *aStatus = nsIDOMOfflineResourceList::UNCACHED;
471 0 : return NS_OK;
472 : }
473 :
474 0 : NS_ENSURE_SUCCESS(rv, rv);
475 :
476 : // If this object is not associated with a cache, return UNCACHED
477 0 : nsCOMPtr<nsIApplicationCache> appCache = GetDocumentAppCache();
478 0 : if (!appCache) {
479 0 : *aStatus = nsIDOMOfflineResourceList::UNCACHED;
480 0 : return NS_OK;
481 : }
482 :
483 :
484 : // If there is an update in process, use its status.
485 0 : if (mCacheUpdate && mExposeCacheUpdateStatus) {
486 0 : rv = mCacheUpdate->GetStatus(aStatus);
487 0 : if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) {
488 0 : return NS_OK;
489 : }
490 : }
491 :
492 0 : *aStatus = mStatus;
493 0 : return NS_OK;
494 : }
495 :
496 : NS_IMETHODIMP
497 0 : nsDOMOfflineResourceList::Update()
498 : {
499 0 : nsresult rv = Init();
500 0 : NS_ENSURE_SUCCESS(rv, rv);
501 :
502 0 : if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
503 0 : return NS_ERROR_DOM_SECURITY_ERR;
504 : }
505 :
506 : nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
507 0 : do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
508 0 : NS_ENSURE_SUCCESS(rv, rv);
509 :
510 : nsCOMPtr<nsIDOMWindow> window =
511 0 : do_QueryInterface(GetOwner());
512 :
513 0 : nsCOMPtr<nsIOfflineCacheUpdate> update;
514 0 : rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI,
515 0 : window, getter_AddRefs(update));
516 0 : NS_ENSURE_SUCCESS(rv, rv);
517 :
518 0 : return NS_OK;
519 : }
520 :
521 : NS_IMETHODIMP
522 0 : nsDOMOfflineResourceList::SwapCache()
523 : {
524 0 : nsresult rv = Init();
525 0 : NS_ENSURE_SUCCESS(rv, rv);
526 :
527 0 : if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
528 0 : return NS_ERROR_DOM_SECURITY_ERR;
529 : }
530 :
531 0 : nsCOMPtr<nsIApplicationCache> currentAppCache = GetDocumentAppCache();
532 0 : if (!currentAppCache) {
533 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
534 : }
535 :
536 : // Check the current and potentially newly available cache are not identical.
537 0 : if (mAvailableApplicationCache == currentAppCache) {
538 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
539 : }
540 :
541 0 : if (mAvailableApplicationCache) {
542 0 : nsCString currClientId, availClientId;
543 0 : currentAppCache->GetClientID(currClientId);
544 0 : mAvailableApplicationCache->GetClientID(availClientId);
545 0 : if (availClientId == currClientId)
546 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
547 : }
548 :
549 0 : ClearCachedKeys();
550 :
551 : nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
552 0 : GetDocumentAppCacheContainer();
553 :
554 : // In the case of an obsolete cache group, newAppCache might be null.
555 : // We will disassociate from the cache in that case.
556 0 : if (appCacheContainer) {
557 0 : rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache);
558 0 : NS_ENSURE_SUCCESS(rv, rv);
559 : }
560 :
561 0 : mAvailableApplicationCache = nsnull;
562 0 : mStatus = nsIDOMOfflineResourceList::IDLE;
563 :
564 0 : return NS_OK;
565 : }
566 :
567 : //
568 : // nsDOMOfflineResourceList::nsIDOMEventTarget
569 : //
570 :
571 : NS_IMETHODIMP
572 0 : nsDOMOfflineResourceList::GetOnchecking(nsIDOMEventListener **aOnchecking)
573 : {
574 0 : return GetInnerEventListener(mOnCheckingListener, aOnchecking);
575 : }
576 :
577 : NS_IMETHODIMP
578 0 : nsDOMOfflineResourceList::SetOnchecking(nsIDOMEventListener *aOnchecking)
579 : {
580 0 : return RemoveAddEventListener(NS_LITERAL_STRING(CHECKING_STR),
581 0 : mOnCheckingListener, aOnchecking);
582 : }
583 :
584 : NS_IMETHODIMP
585 0 : nsDOMOfflineResourceList::GetOnerror(nsIDOMEventListener **aOnerror)
586 : {
587 0 : return GetInnerEventListener(mOnErrorListener, aOnerror);
588 : }
589 :
590 : NS_IMETHODIMP
591 0 : nsDOMOfflineResourceList::SetOnerror(nsIDOMEventListener *aOnerror)
592 : {
593 0 : return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_STR), mOnErrorListener,
594 0 : aOnerror);
595 : }
596 :
597 : NS_IMETHODIMP
598 0 : nsDOMOfflineResourceList::GetOnnoupdate(nsIDOMEventListener **aOnnoupdate)
599 : {
600 0 : return GetInnerEventListener(mOnNoUpdateListener, aOnnoupdate);
601 : }
602 :
603 : NS_IMETHODIMP
604 0 : nsDOMOfflineResourceList::SetOnnoupdate(nsIDOMEventListener *aOnnoupdate)
605 : {
606 0 : return RemoveAddEventListener(NS_LITERAL_STRING(NOUPDATE_STR),
607 0 : mOnNoUpdateListener, aOnnoupdate);
608 : }
609 :
610 : NS_IMETHODIMP
611 0 : nsDOMOfflineResourceList::GetOndownloading(nsIDOMEventListener **aOndownloading)
612 : {
613 0 : return GetInnerEventListener(mOnDownloadingListener, aOndownloading);
614 : }
615 :
616 : NS_IMETHODIMP
617 0 : nsDOMOfflineResourceList::SetOndownloading(nsIDOMEventListener *aOndownloading)
618 : {
619 0 : return RemoveAddEventListener(NS_LITERAL_STRING(DOWNLOADING_STR),
620 0 : mOnDownloadingListener, aOndownloading);
621 : }
622 :
623 : NS_IMETHODIMP
624 0 : nsDOMOfflineResourceList::GetOnprogress(nsIDOMEventListener **aOnprogress)
625 : {
626 0 : return GetInnerEventListener(mOnProgressListener, aOnprogress);
627 : }
628 :
629 : NS_IMETHODIMP
630 0 : nsDOMOfflineResourceList::SetOnprogress(nsIDOMEventListener *aOnprogress)
631 : {
632 0 : return RemoveAddEventListener(NS_LITERAL_STRING(PROGRESS_STR),
633 0 : mOnProgressListener, aOnprogress);
634 : }
635 :
636 :
637 : NS_IMETHODIMP
638 0 : nsDOMOfflineResourceList::GetOnupdateready(nsIDOMEventListener **aOnupdateready)
639 : {
640 0 : return GetInnerEventListener(mOnUpdateReadyListener, aOnupdateready);
641 : }
642 :
643 : NS_IMETHODIMP
644 0 : nsDOMOfflineResourceList::SetOncached(nsIDOMEventListener *aOncached)
645 : {
646 0 : return RemoveAddEventListener(NS_LITERAL_STRING(CACHED_STR),
647 0 : mOnCachedListener, aOncached);
648 : }
649 :
650 : NS_IMETHODIMP
651 0 : nsDOMOfflineResourceList::GetOncached(nsIDOMEventListener **aOncached)
652 : {
653 0 : return GetInnerEventListener(mOnCachedListener, aOncached);
654 : }
655 :
656 : NS_IMETHODIMP
657 0 : nsDOMOfflineResourceList::SetOnupdateready(nsIDOMEventListener *aOnupdateready)
658 : {
659 0 : return RemoveAddEventListener(NS_LITERAL_STRING(UPDATEREADY_STR),
660 0 : mOnUpdateReadyListener, aOnupdateready);
661 : }
662 :
663 : NS_IMETHODIMP
664 0 : nsDOMOfflineResourceList::GetOnobsolete(nsIDOMEventListener **aOnobsolete)
665 : {
666 0 : return GetInnerEventListener(mOnObsoleteListener, aOnobsolete);
667 : }
668 :
669 : NS_IMETHODIMP
670 0 : nsDOMOfflineResourceList::SetOnobsolete(nsIDOMEventListener *aOnobsolete)
671 : {
672 0 : return RemoveAddEventListener(NS_LITERAL_STRING(OBSOLETE_STR),
673 0 : mOnObsoleteListener, aOnobsolete);
674 : }
675 :
676 : void
677 0 : nsDOMOfflineResourceList::FirePendingEvents()
678 : {
679 0 : for (PRInt32 i = 0; i < mPendingEvents.Count(); ++i) {
680 : bool dummy;
681 0 : nsCOMPtr<nsIDOMEvent> event = mPendingEvents[i];
682 0 : DispatchEvent(event, &dummy);
683 : }
684 0 : mPendingEvents.Clear();
685 0 : }
686 :
687 : nsresult
688 0 : nsDOMOfflineResourceList::SendEvent(const nsAString &aEventName)
689 : {
690 : // Don't send events to closed windows
691 0 : if (!GetOwner()) {
692 0 : return NS_OK;
693 : }
694 :
695 0 : if (!GetOwner()->GetDocShell()) {
696 0 : return NS_OK;
697 : }
698 :
699 0 : nsCOMPtr<nsIDOMEvent> event;
700 : nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
701 0 : NS_LITERAL_STRING("Events"),
702 0 : getter_AddRefs(event));
703 0 : NS_ENSURE_SUCCESS(rv, rv);
704 :
705 0 : nsCOMPtr<nsIPrivateDOMEvent> privevent = do_QueryInterface(event);
706 0 : if (!privevent) {
707 0 : return NS_ERROR_FAILURE;
708 : }
709 :
710 0 : event->InitEvent(aEventName, false, true);
711 :
712 : // We assume anyone that managed to call SendEvent is trusted
713 0 : privevent->SetTrusted(true);
714 :
715 : // If the window is frozen or we're still catching up on events that were
716 : // queued while frozen, save the event for later.
717 0 : if (GetOwner()->IsFrozen() || mPendingEvents.Count() > 0) {
718 0 : mPendingEvents.AppendObject(event);
719 0 : return NS_OK;
720 : }
721 :
722 : bool dummy;
723 0 : DispatchEvent(event, &dummy);
724 :
725 0 : return NS_OK;
726 : }
727 :
728 :
729 : //
730 : // nsDOMOfflineResourceList::nsIObserver
731 : //
732 : NS_IMETHODIMP
733 0 : nsDOMOfflineResourceList::Observe(nsISupports *aSubject,
734 : const char *aTopic,
735 : const PRUnichar *aData)
736 : {
737 0 : if (!strcmp(aTopic, "offline-cache-update-added")) {
738 0 : nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
739 0 : if (update) {
740 0 : UpdateAdded(update);
741 : }
742 0 : } else if (!strcmp(aTopic, "offline-cache-update-completed")) {
743 0 : nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
744 0 : if (update) {
745 0 : UpdateCompleted(update);
746 : }
747 : }
748 :
749 0 : return NS_OK;
750 : }
751 :
752 : //
753 : // nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver
754 : //
755 : NS_IMETHODIMP
756 0 : nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate,
757 : PRUint32 event)
758 : {
759 : mExposeCacheUpdateStatus =
760 : (event == STATE_CHECKING) ||
761 : (event == STATE_DOWNLOADING) ||
762 : (event == STATE_ITEMSTARTED) ||
763 : (event == STATE_ITEMCOMPLETED) ||
764 : // During notification of "obsolete" we must expose state of the update
765 0 : (event == STATE_OBSOLETE);
766 :
767 0 : switch (event) {
768 : case STATE_ERROR:
769 0 : SendEvent(NS_LITERAL_STRING(ERROR_STR));
770 0 : break;
771 : case STATE_CHECKING:
772 0 : SendEvent(NS_LITERAL_STRING(CHECKING_STR));
773 0 : break;
774 : case STATE_NOUPDATE:
775 0 : SendEvent(NS_LITERAL_STRING(NOUPDATE_STR));
776 0 : break;
777 : case STATE_OBSOLETE:
778 0 : mStatus = nsIDOMOfflineResourceList::OBSOLETE;
779 0 : mAvailableApplicationCache = nsnull;
780 0 : SendEvent(NS_LITERAL_STRING(OBSOLETE_STR));
781 0 : break;
782 : case STATE_DOWNLOADING:
783 0 : SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR));
784 0 : break;
785 : case STATE_ITEMSTARTED:
786 0 : SendEvent(NS_LITERAL_STRING(PROGRESS_STR));
787 0 : break;
788 : case STATE_ITEMCOMPLETED:
789 : // Nothing to do here...
790 0 : break;
791 : }
792 :
793 0 : return NS_OK;
794 : }
795 :
796 : NS_IMETHODIMP
797 0 : nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache)
798 : {
799 0 : mAvailableApplicationCache = aApplicationCache;
800 0 : return NS_OK;
801 : }
802 :
803 : nsresult
804 0 : nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
805 : {
806 0 : nsCOMPtr<nsIURI> requestedURI;
807 0 : nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
808 0 : NS_ENSURE_SUCCESS(rv, rv);
809 :
810 0 : return GetCacheKey(requestedURI, aKey);
811 : }
812 :
813 : nsresult
814 0 : nsDOMOfflineResourceList::UpdateAdded(nsIOfflineCacheUpdate *aUpdate)
815 : {
816 : // Ignore partial updates.
817 : bool partial;
818 0 : nsresult rv = aUpdate->GetPartial(&partial);
819 0 : NS_ENSURE_SUCCESS(rv, rv);
820 :
821 0 : if (partial) {
822 0 : return NS_OK;
823 : }
824 :
825 0 : nsCOMPtr<nsIURI> updateURI;
826 0 : rv = aUpdate->GetManifestURI(getter_AddRefs(updateURI));
827 0 : NS_ENSURE_SUCCESS(rv, rv);
828 :
829 : bool equals;
830 0 : rv = updateURI->Equals(mManifestURI, &equals);
831 0 : NS_ENSURE_SUCCESS(rv, rv);
832 :
833 0 : if (!equals) {
834 : // This update doesn't belong to us
835 0 : return NS_OK;
836 : }
837 :
838 0 : NS_ENSURE_TRUE(!mCacheUpdate, NS_ERROR_FAILURE);
839 :
840 : // We don't need to emit signals here. Updates are either added
841 : // when they are scheduled (in which case they are always IDLE) or
842 : // they are added when the applicationCache object is initialized, so there
843 : // are no listeners to accept signals anyway.
844 :
845 0 : mCacheUpdate = aUpdate;
846 0 : mCacheUpdate->AddObserver(this, true);
847 :
848 0 : return NS_OK;
849 : }
850 :
851 : already_AddRefed<nsIApplicationCacheContainer>
852 0 : nsDOMOfflineResourceList::GetDocumentAppCacheContainer()
853 : {
854 0 : nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(GetOwner());
855 0 : if (!webnav) {
856 0 : return nsnull;
857 : }
858 :
859 : nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
860 0 : do_GetInterface(webnav);
861 0 : return appCacheContainer.forget();
862 : }
863 :
864 : already_AddRefed<nsIApplicationCache>
865 0 : nsDOMOfflineResourceList::GetDocumentAppCache()
866 : {
867 : nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer =
868 0 : GetDocumentAppCacheContainer();
869 :
870 0 : if (appCacheContainer) {
871 0 : nsCOMPtr<nsIApplicationCache> applicationCache;
872 0 : appCacheContainer->GetApplicationCache(
873 0 : getter_AddRefs(applicationCache));
874 0 : return applicationCache.forget();
875 : }
876 :
877 0 : return nsnull;
878 : }
879 :
880 : nsresult
881 0 : nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate)
882 : {
883 0 : if (aUpdate != mCacheUpdate) {
884 : // This isn't the update we're watching.
885 0 : return NS_OK;
886 : }
887 :
888 : bool partial;
889 0 : mCacheUpdate->GetPartial(&partial);
890 : bool isUpgrade;
891 0 : mCacheUpdate->GetIsUpgrade(&isUpgrade);
892 :
893 : bool succeeded;
894 0 : nsresult rv = mCacheUpdate->GetSucceeded(&succeeded);
895 :
896 0 : mCacheUpdate->RemoveObserver(this);
897 0 : mCacheUpdate = nsnull;
898 :
899 0 : if (NS_SUCCEEDED(rv) && succeeded && !partial) {
900 0 : if (isUpgrade) {
901 0 : mStatus = nsIDOMOfflineResourceList::UPDATEREADY;
902 0 : SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR));
903 : } else {
904 0 : mStatus = nsIDOMOfflineResourceList::IDLE;
905 0 : SendEvent(NS_LITERAL_STRING(CACHED_STR));
906 : }
907 : }
908 :
909 0 : return NS_OK;
910 : }
911 :
912 : nsresult
913 0 : nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey)
914 : {
915 0 : nsresult rv = aURI->GetAsciiSpec(aKey);
916 0 : NS_ENSURE_SUCCESS(rv, rv);
917 :
918 : // url fragments aren't used in cache keys
919 0 : nsCAutoString::const_iterator specStart, specEnd;
920 0 : aKey.BeginReading(specStart);
921 0 : aKey.EndReading(specEnd);
922 0 : if (FindCharInReadable('#', specStart, specEnd)) {
923 0 : aKey.BeginReading(specEnd);
924 0 : aKey = Substring(specEnd, specStart);
925 : }
926 :
927 0 : return NS_OK;
928 : }
929 :
930 : nsresult
931 0 : nsDOMOfflineResourceList::CacheKeys()
932 : {
933 0 : if (IS_CHILD_PROCESS())
934 0 : return NS_ERROR_NOT_IMPLEMENTED;
935 :
936 0 : if (mCachedKeys)
937 0 : return NS_OK;
938 :
939 0 : nsCOMPtr<nsIApplicationCache> appCache;
940 0 : mApplicationCacheService->GetActiveCache(mManifestSpec,
941 0 : getter_AddRefs(appCache));
942 :
943 0 : if (!appCache) {
944 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
945 : }
946 :
947 0 : return appCache->GatherEntries(nsIApplicationCache::ITEM_DYNAMIC,
948 0 : &mCachedKeysCount, &mCachedKeys);
949 : }
950 :
951 : void
952 0 : nsDOMOfflineResourceList::ClearCachedKeys()
953 : {
954 0 : if (mCachedKeys) {
955 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCachedKeysCount, mCachedKeys);
956 0 : mCachedKeys = nsnull;
957 0 : mCachedKeysCount = 0;
958 : }
959 4392 : }
|