|
1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 #if defined(MOZ_LOGGING) |
|
7 #define FORCE_PR_LOG |
|
8 #endif |
|
9 |
|
10 #include "OfflineCacheUpdateChild.h" |
|
11 #include "OfflineCacheUpdateParent.h" |
|
12 #include "nsXULAppAPI.h" |
|
13 #include "OfflineCacheUpdateGlue.h" |
|
14 #include "nsOfflineCacheUpdate.h" |
|
15 |
|
16 #include "nsCPrefetchService.h" |
|
17 #include "nsCURILoader.h" |
|
18 #include "nsIApplicationCacheContainer.h" |
|
19 #include "nsIApplicationCacheChannel.h" |
|
20 #include "nsIApplicationCacheService.h" |
|
21 #include "nsICache.h" |
|
22 #include "nsICacheService.h" |
|
23 #include "nsICacheSession.h" |
|
24 #include "nsICachingChannel.h" |
|
25 #include "nsIContent.h" |
|
26 #include "nsIDocShell.h" |
|
27 #include "nsIDocumentLoader.h" |
|
28 #include "nsIDOMElement.h" |
|
29 #include "nsIDOMWindow.h" |
|
30 #include "nsIDOMOfflineResourceList.h" |
|
31 #include "nsIDocument.h" |
|
32 #include "nsIObserverService.h" |
|
33 #include "nsIURL.h" |
|
34 #include "nsIWebProgress.h" |
|
35 #include "nsIWebNavigation.h" |
|
36 #include "nsICryptoHash.h" |
|
37 #include "nsICacheEntryDescriptor.h" |
|
38 #include "nsIPermissionManager.h" |
|
39 #include "nsIPrincipal.h" |
|
40 #include "nsIScriptSecurityManager.h" |
|
41 #include "nsNetCID.h" |
|
42 #include "nsNetUtil.h" |
|
43 #include "nsServiceManagerUtils.h" |
|
44 #include "nsStreamUtils.h" |
|
45 #include "nsThreadUtils.h" |
|
46 #include "nsProxyRelease.h" |
|
47 #include "prlog.h" |
|
48 #include "nsIAsyncVerifyRedirectCallback.h" |
|
49 #include "mozilla/Preferences.h" |
|
50 #include "mozilla/Attributes.h" |
|
51 #include "mozilla/unused.h" |
|
52 #include "nsIDiskSpaceWatcher.h" |
|
53 #include "nsIDocShell.h" |
|
54 #include "nsIDocShellTreeItem.h" |
|
55 #include "nsIDocShellTreeOwner.h" |
|
56 #include "mozilla/dom/TabChild.h" |
|
57 #include "mozilla/dom/PermissionMessageUtils.h" |
|
58 #include "nsContentUtils.h" |
|
59 #include "mozilla/unused.h" |
|
60 |
|
61 using namespace mozilla; |
|
62 using namespace mozilla::dom; |
|
63 |
|
64 static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr; |
|
65 |
|
66 nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr; |
|
67 |
|
68 nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains() |
|
69 { |
|
70 if (!mAllowedDomains) |
|
71 mAllowedDomains = new nsTHashtable<nsCStringHashKey>(); |
|
72 |
|
73 return mAllowedDomains; |
|
74 } |
|
75 |
|
76 |
|
77 typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent; |
|
78 typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild; |
|
79 typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue; |
|
80 |
|
81 #if defined(PR_LOGGING) |
|
82 // |
|
83 // To enable logging (see prlog.h for full details): |
|
84 // |
|
85 // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 |
|
86 // set NSPR_LOG_FILE=offlineupdate.log |
|
87 // |
|
88 // this enables PR_LOG_ALWAYS level information and places all output in |
|
89 // the file offlineupdate.log |
|
90 // |
|
91 PRLogModuleInfo *gOfflineCacheUpdateLog; |
|
92 #endif |
|
93 |
|
94 #undef LOG |
|
95 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) |
|
96 |
|
97 #undef LOG_ENABLED |
|
98 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) |
|
99 |
|
100 namespace { // anon |
|
101 |
|
102 nsresult |
|
103 GetAppIDAndInBrowserFromWindow(nsIDOMWindow *aWindow, |
|
104 uint32_t *aAppId, |
|
105 bool *aInBrowser) |
|
106 { |
|
107 *aAppId = NECKO_NO_APP_ID; |
|
108 *aInBrowser = false; |
|
109 |
|
110 if (!aWindow) { |
|
111 return NS_OK; |
|
112 } |
|
113 |
|
114 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aWindow); |
|
115 if (!loadContext) { |
|
116 return NS_OK; |
|
117 } |
|
118 |
|
119 nsresult rv; |
|
120 |
|
121 rv = loadContext->GetAppId(aAppId); |
|
122 NS_ENSURE_SUCCESS(rv, rv); |
|
123 |
|
124 rv = loadContext->GetIsInBrowserElement(aInBrowser); |
|
125 NS_ENSURE_SUCCESS(rv, rv); |
|
126 |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 } // anon |
|
131 |
|
132 //----------------------------------------------------------------------------- |
|
133 // nsOfflineCachePendingUpdate |
|
134 //----------------------------------------------------------------------------- |
|
135 |
|
136 class nsOfflineCachePendingUpdate MOZ_FINAL : public nsIWebProgressListener |
|
137 , public nsSupportsWeakReference |
|
138 { |
|
139 public: |
|
140 NS_DECL_ISUPPORTS |
|
141 NS_DECL_NSIWEBPROGRESSLISTENER |
|
142 |
|
143 nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService, |
|
144 nsIURI *aManifestURI, |
|
145 nsIURI *aDocumentURI, |
|
146 nsIDOMDocument *aDocument) |
|
147 : mService(aService) |
|
148 , mManifestURI(aManifestURI) |
|
149 , mDocumentURI(aDocumentURI) |
|
150 , mDidReleaseThis(false) |
|
151 { |
|
152 mDocument = do_GetWeakReference(aDocument); |
|
153 } |
|
154 |
|
155 private: |
|
156 nsRefPtr<nsOfflineCacheUpdateService> mService; |
|
157 nsCOMPtr<nsIURI> mManifestURI; |
|
158 nsCOMPtr<nsIURI> mDocumentURI; |
|
159 nsCOMPtr<nsIWeakReference> mDocument; |
|
160 bool mDidReleaseThis; |
|
161 }; |
|
162 |
|
163 NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate, |
|
164 nsIWebProgressListener, |
|
165 nsISupportsWeakReference) |
|
166 |
|
167 //----------------------------------------------------------------------------- |
|
168 // nsOfflineCacheUpdateService::nsIWebProgressListener |
|
169 //----------------------------------------------------------------------------- |
|
170 |
|
171 NS_IMETHODIMP |
|
172 nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress, |
|
173 nsIRequest *aRequest, |
|
174 int32_t curSelfProgress, |
|
175 int32_t maxSelfProgress, |
|
176 int32_t curTotalProgress, |
|
177 int32_t maxTotalProgress) |
|
178 { |
|
179 NS_NOTREACHED("notification excluded in AddProgressListener(...)"); |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 NS_IMETHODIMP |
|
184 nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress, |
|
185 nsIRequest *aRequest, |
|
186 uint32_t progressStateFlags, |
|
187 nsresult aStatus) |
|
188 { |
|
189 if (mDidReleaseThis) { |
|
190 return NS_OK; |
|
191 } |
|
192 nsCOMPtr<nsIDOMDocument> updateDoc = do_QueryReferent(mDocument); |
|
193 if (!updateDoc) { |
|
194 // The document that scheduled this update has gone away, |
|
195 // we don't need to listen anymore. |
|
196 aWebProgress->RemoveProgressListener(this); |
|
197 MOZ_ASSERT(!mDidReleaseThis); |
|
198 mDidReleaseThis = true; |
|
199 NS_RELEASE_THIS(); |
|
200 return NS_OK; |
|
201 } |
|
202 |
|
203 if (!(progressStateFlags & STATE_STOP)) { |
|
204 return NS_OK; |
|
205 } |
|
206 |
|
207 nsCOMPtr<nsIDOMWindow> window; |
|
208 aWebProgress->GetDOMWindow(getter_AddRefs(window)); |
|
209 if (!window) return NS_OK; |
|
210 |
|
211 nsCOMPtr<nsIDOMDocument> progressDoc; |
|
212 window->GetDocument(getter_AddRefs(progressDoc)); |
|
213 if (!progressDoc) return NS_OK; |
|
214 |
|
215 if (!SameCOMIdentity(progressDoc, updateDoc)) { |
|
216 return NS_OK; |
|
217 } |
|
218 |
|
219 LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]", |
|
220 this, progressDoc.get())); |
|
221 |
|
222 // Only schedule the update if the document loaded successfully |
|
223 if (NS_SUCCEEDED(aStatus)) { |
|
224 // Get extended origin attributes |
|
225 uint32_t appId; |
|
226 bool isInBrowserElement; |
|
227 nsresult rv = GetAppIDAndInBrowserFromWindow(window, &appId, &isInBrowserElement); |
|
228 NS_ENSURE_SUCCESS(rv, rv); |
|
229 |
|
230 nsCOMPtr<nsIOfflineCacheUpdate> update; |
|
231 mService->Schedule(mManifestURI, mDocumentURI, |
|
232 updateDoc, window, nullptr, |
|
233 appId, isInBrowserElement, getter_AddRefs(update)); |
|
234 if (mDidReleaseThis) { |
|
235 return NS_OK; |
|
236 } |
|
237 } |
|
238 |
|
239 aWebProgress->RemoveProgressListener(this); |
|
240 MOZ_ASSERT(!mDidReleaseThis); |
|
241 mDidReleaseThis = true; |
|
242 NS_RELEASE_THIS(); |
|
243 |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 NS_IMETHODIMP |
|
248 nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress, |
|
249 nsIRequest* aRequest, |
|
250 nsIURI *location, |
|
251 uint32_t aFlags) |
|
252 { |
|
253 NS_NOTREACHED("notification excluded in AddProgressListener(...)"); |
|
254 return NS_OK; |
|
255 } |
|
256 |
|
257 NS_IMETHODIMP |
|
258 nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress, |
|
259 nsIRequest* aRequest, |
|
260 nsresult aStatus, |
|
261 const char16_t* aMessage) |
|
262 { |
|
263 NS_NOTREACHED("notification excluded in AddProgressListener(...)"); |
|
264 return NS_OK; |
|
265 } |
|
266 |
|
267 NS_IMETHODIMP |
|
268 nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress, |
|
269 nsIRequest *aRequest, |
|
270 uint32_t state) |
|
271 { |
|
272 NS_NOTREACHED("notification excluded in AddProgressListener(...)"); |
|
273 return NS_OK; |
|
274 } |
|
275 |
|
276 //----------------------------------------------------------------------------- |
|
277 // nsOfflineCacheUpdateService::nsISupports |
|
278 //----------------------------------------------------------------------------- |
|
279 |
|
280 NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService, |
|
281 nsIOfflineCacheUpdateService, |
|
282 nsIObserver, |
|
283 nsISupportsWeakReference) |
|
284 |
|
285 //----------------------------------------------------------------------------- |
|
286 // nsOfflineCacheUpdateService <public> |
|
287 //----------------------------------------------------------------------------- |
|
288 |
|
289 nsOfflineCacheUpdateService::nsOfflineCacheUpdateService() |
|
290 : mDisabled(false) |
|
291 , mUpdateRunning(false) |
|
292 , mLowFreeSpace(false) |
|
293 { |
|
294 } |
|
295 |
|
296 nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() |
|
297 { |
|
298 gOfflineCacheUpdateService = nullptr; |
|
299 } |
|
300 |
|
301 nsresult |
|
302 nsOfflineCacheUpdateService::Init() |
|
303 { |
|
304 #if defined(PR_LOGGING) |
|
305 if (!gOfflineCacheUpdateLog) |
|
306 gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate"); |
|
307 #endif |
|
308 |
|
309 // Observe xpcom-shutdown event |
|
310 nsCOMPtr<nsIObserverService> observerService = |
|
311 mozilla::services::GetObserverService(); |
|
312 if (!observerService) |
|
313 return NS_ERROR_FAILURE; |
|
314 |
|
315 nsresult rv = observerService->AddObserver(this, |
|
316 NS_XPCOM_SHUTDOWN_OBSERVER_ID, |
|
317 true); |
|
318 NS_ENSURE_SUCCESS(rv, rv); |
|
319 |
|
320 // Get the current status of the disk in terms of free space and observe |
|
321 // low device storage notifications. |
|
322 nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcherService = |
|
323 do_GetService("@mozilla.org/toolkit/disk-space-watcher;1"); |
|
324 if (diskSpaceWatcherService) { |
|
325 diskSpaceWatcherService->GetIsDiskFull(&mLowFreeSpace); |
|
326 } else { |
|
327 NS_WARNING("Could not get disk status from nsIDiskSpaceWatcher"); |
|
328 } |
|
329 |
|
330 rv = observerService->AddObserver(this, "disk-space-watcher", false); |
|
331 NS_ENSURE_SUCCESS(rv, rv); |
|
332 |
|
333 gOfflineCacheUpdateService = this; |
|
334 |
|
335 return NS_OK; |
|
336 } |
|
337 |
|
338 /* static */ |
|
339 nsOfflineCacheUpdateService * |
|
340 nsOfflineCacheUpdateService::GetInstance() |
|
341 { |
|
342 if (!gOfflineCacheUpdateService) { |
|
343 gOfflineCacheUpdateService = new nsOfflineCacheUpdateService(); |
|
344 if (!gOfflineCacheUpdateService) |
|
345 return nullptr; |
|
346 NS_ADDREF(gOfflineCacheUpdateService); |
|
347 nsresult rv = gOfflineCacheUpdateService->Init(); |
|
348 if (NS_FAILED(rv)) { |
|
349 NS_RELEASE(gOfflineCacheUpdateService); |
|
350 return nullptr; |
|
351 } |
|
352 return gOfflineCacheUpdateService; |
|
353 } |
|
354 |
|
355 NS_ADDREF(gOfflineCacheUpdateService); |
|
356 |
|
357 return gOfflineCacheUpdateService; |
|
358 } |
|
359 |
|
360 /* static */ |
|
361 nsOfflineCacheUpdateService * |
|
362 nsOfflineCacheUpdateService::EnsureService() |
|
363 { |
|
364 if (!gOfflineCacheUpdateService) { |
|
365 // Make the service manager hold a long-lived reference to the service |
|
366 nsCOMPtr<nsIOfflineCacheUpdateService> service = |
|
367 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); |
|
368 } |
|
369 |
|
370 return gOfflineCacheUpdateService; |
|
371 } |
|
372 |
|
373 nsresult |
|
374 nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate) |
|
375 { |
|
376 LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]", |
|
377 this, aUpdate)); |
|
378 |
|
379 aUpdate->SetOwner(this); |
|
380 |
|
381 mUpdates.AppendElement(aUpdate); |
|
382 ProcessNextUpdate(); |
|
383 |
|
384 return NS_OK; |
|
385 } |
|
386 |
|
387 NS_IMETHODIMP |
|
388 nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI, |
|
389 nsIURI *aDocumentURI, |
|
390 nsIDOMDocument *aDocument) |
|
391 { |
|
392 LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]", |
|
393 this, aManifestURI, aDocumentURI, aDocument)); |
|
394 |
|
395 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument); |
|
396 nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(doc->GetContainer()); |
|
397 NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG); |
|
398 |
|
399 // Proceed with cache update |
|
400 nsRefPtr<nsOfflineCachePendingUpdate> update = |
|
401 new nsOfflineCachePendingUpdate(this, aManifestURI, |
|
402 aDocumentURI, aDocument); |
|
403 NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); |
|
404 |
|
405 nsresult rv = progress->AddProgressListener |
|
406 (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT); |
|
407 NS_ENSURE_SUCCESS(rv, rv); |
|
408 |
|
409 // The update will release when it has scheduled itself. |
|
410 unused << update.forget(); |
|
411 |
|
412 return NS_OK; |
|
413 } |
|
414 |
|
415 nsresult |
|
416 nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate) |
|
417 { |
|
418 LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]", |
|
419 this, aUpdate)); |
|
420 |
|
421 NS_ASSERTION(mUpdates.Length() > 0 && |
|
422 mUpdates[0] == aUpdate, "Unknown update completed"); |
|
423 |
|
424 // keep this item alive until we're done notifying observers |
|
425 nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0]; |
|
426 mUpdates.RemoveElementAt(0); |
|
427 mUpdateRunning = false; |
|
428 |
|
429 ProcessNextUpdate(); |
|
430 |
|
431 return NS_OK; |
|
432 } |
|
433 |
|
434 //----------------------------------------------------------------------------- |
|
435 // nsOfflineCacheUpdateService <private> |
|
436 //----------------------------------------------------------------------------- |
|
437 |
|
438 nsresult |
|
439 nsOfflineCacheUpdateService::ProcessNextUpdate() |
|
440 { |
|
441 LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]", |
|
442 this, mUpdates.Length())); |
|
443 |
|
444 if (mDisabled) |
|
445 return NS_ERROR_ABORT; |
|
446 |
|
447 if (mUpdateRunning) |
|
448 return NS_OK; |
|
449 |
|
450 if (mUpdates.Length() > 0) { |
|
451 mUpdateRunning = true; |
|
452 // Canceling the update before Begin() call will make the update |
|
453 // asynchronously finish with an error. |
|
454 if (mLowFreeSpace) { |
|
455 mUpdates[0]->Cancel(); |
|
456 } |
|
457 return mUpdates[0]->Begin(); |
|
458 } |
|
459 |
|
460 return NS_OK; |
|
461 } |
|
462 |
|
463 //----------------------------------------------------------------------------- |
|
464 // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService |
|
465 //----------------------------------------------------------------------------- |
|
466 |
|
467 NS_IMETHODIMP |
|
468 nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates) |
|
469 { |
|
470 LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this)); |
|
471 |
|
472 *aNumUpdates = mUpdates.Length(); |
|
473 return NS_OK; |
|
474 } |
|
475 |
|
476 NS_IMETHODIMP |
|
477 nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex, |
|
478 nsIOfflineCacheUpdate **aUpdate) |
|
479 { |
|
480 LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex)); |
|
481 |
|
482 if (aIndex < mUpdates.Length()) { |
|
483 NS_ADDREF(*aUpdate = mUpdates[aIndex]); |
|
484 } else { |
|
485 *aUpdate = nullptr; |
|
486 } |
|
487 |
|
488 return NS_OK; |
|
489 } |
|
490 |
|
491 nsresult |
|
492 nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI, |
|
493 uint32_t aAppID, |
|
494 bool aInBrowser, |
|
495 nsOfflineCacheUpdate **aUpdate) |
|
496 { |
|
497 nsresult rv; |
|
498 |
|
499 nsCOMPtr<nsIApplicationCacheService> cacheService = |
|
500 do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); |
|
501 NS_ENSURE_SUCCESS(rv, rv); |
|
502 |
|
503 nsAutoCString groupID; |
|
504 rv = cacheService->BuildGroupIDForApp(aManifestURI, |
|
505 aAppID, aInBrowser, |
|
506 groupID); |
|
507 NS_ENSURE_SUCCESS(rv, rv); |
|
508 |
|
509 nsRefPtr<nsOfflineCacheUpdate> update; |
|
510 for (uint32_t i = 0; i < mUpdates.Length(); i++) { |
|
511 update = mUpdates[i]; |
|
512 |
|
513 bool partial; |
|
514 rv = update->GetPartial(&partial); |
|
515 NS_ENSURE_SUCCESS(rv, rv); |
|
516 |
|
517 if (partial) { |
|
518 // Partial updates aren't considered |
|
519 continue; |
|
520 } |
|
521 |
|
522 if (update->IsForGroupID(groupID)) { |
|
523 update.swap(*aUpdate); |
|
524 return NS_OK; |
|
525 } |
|
526 } |
|
527 |
|
528 return NS_ERROR_NOT_AVAILABLE; |
|
529 } |
|
530 |
|
531 nsresult |
|
532 nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI, |
|
533 nsIURI *aDocumentURI, |
|
534 nsIDOMDocument *aDocument, |
|
535 nsIDOMWindow* aWindow, |
|
536 nsIFile* aCustomProfileDir, |
|
537 uint32_t aAppID, |
|
538 bool aInBrowser, |
|
539 nsIOfflineCacheUpdate **aUpdate) |
|
540 { |
|
541 nsCOMPtr<nsIOfflineCacheUpdate> update; |
|
542 if (GeckoProcessType_Default != XRE_GetProcessType()) { |
|
543 update = new OfflineCacheUpdateChild(aWindow); |
|
544 } |
|
545 else { |
|
546 update = new OfflineCacheUpdateGlue(); |
|
547 } |
|
548 |
|
549 nsresult rv; |
|
550 |
|
551 if (aWindow) { |
|
552 // Ensure there is window.applicationCache object that is |
|
553 // responsible for association of the new applicationCache |
|
554 // with the corresponding document. Just ignore the result. |
|
555 nsCOMPtr<nsIDOMOfflineResourceList> appCacheWindowObject; |
|
556 aWindow->GetApplicationCache(getter_AddRefs(appCacheWindowObject)); |
|
557 } |
|
558 |
|
559 rv = update->Init(aManifestURI, aDocumentURI, aDocument, |
|
560 aCustomProfileDir, aAppID, aInBrowser); |
|
561 NS_ENSURE_SUCCESS(rv, rv); |
|
562 |
|
563 rv = update->Schedule(); |
|
564 NS_ENSURE_SUCCESS(rv, rv); |
|
565 |
|
566 NS_ADDREF(*aUpdate = update); |
|
567 |
|
568 return NS_OK; |
|
569 } |
|
570 |
|
571 NS_IMETHODIMP |
|
572 nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI, |
|
573 nsIURI *aDocumentURI, |
|
574 nsIDOMWindow *aWindow, |
|
575 nsIOfflineCacheUpdate **aUpdate) |
|
576 { |
|
577 // Get extended origin attributes |
|
578 uint32_t appId; |
|
579 bool isInBrowser; |
|
580 nsresult rv = GetAppIDAndInBrowserFromWindow(aWindow, &appId, &isInBrowser); |
|
581 NS_ENSURE_SUCCESS(rv, rv); |
|
582 |
|
583 return Schedule(aManifestURI, aDocumentURI, nullptr, aWindow, nullptr, |
|
584 appId, isInBrowser, aUpdate); |
|
585 } |
|
586 |
|
587 NS_IMETHODIMP |
|
588 nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI, |
|
589 nsIURI *aDocumentURI, |
|
590 uint32_t aAppID, bool aInBrowser, |
|
591 nsIFile *aProfileDir, |
|
592 nsIOfflineCacheUpdate **aUpdate) |
|
593 { |
|
594 return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, aProfileDir, |
|
595 aAppID, aInBrowser, aUpdate); |
|
596 } |
|
597 |
|
598 NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI, |
|
599 uint32_t aAppID, |
|
600 bool aInBrowser, |
|
601 nsIObserver *aObserver) |
|
602 { |
|
603 if (GeckoProcessType_Default != XRE_GetProcessType()) { |
|
604 // Not intended to support this on child processes |
|
605 return NS_ERROR_NOT_IMPLEMENTED; |
|
606 } |
|
607 |
|
608 nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue(); |
|
609 |
|
610 nsresult rv; |
|
611 |
|
612 rv = update->InitForUpdateCheck(aManifestURI, aAppID, aInBrowser, aObserver); |
|
613 NS_ENSURE_SUCCESS(rv, rv); |
|
614 |
|
615 rv = update->Schedule(); |
|
616 NS_ENSURE_SUCCESS(rv, rv); |
|
617 |
|
618 return NS_OK; |
|
619 } |
|
620 |
|
621 //----------------------------------------------------------------------------- |
|
622 // nsOfflineCacheUpdateService::nsIObserver |
|
623 //----------------------------------------------------------------------------- |
|
624 |
|
625 NS_IMETHODIMP |
|
626 nsOfflineCacheUpdateService::Observe(nsISupports *aSubject, |
|
627 const char *aTopic, |
|
628 const char16_t *aData) |
|
629 { |
|
630 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { |
|
631 if (mUpdates.Length() > 0) |
|
632 mUpdates[0]->Cancel(); |
|
633 mDisabled = true; |
|
634 } |
|
635 |
|
636 if (!strcmp(aTopic, "disk-space-watcher")) { |
|
637 if (NS_LITERAL_STRING("full").Equals(aData)) { |
|
638 mLowFreeSpace = true; |
|
639 for (uint32_t i = 0; i < mUpdates.Length(); i++) { |
|
640 mUpdates[i]->Cancel(); |
|
641 } |
|
642 } else if (NS_LITERAL_STRING("free").Equals(aData)) { |
|
643 mLowFreeSpace = false; |
|
644 } |
|
645 } |
|
646 |
|
647 return NS_OK; |
|
648 } |
|
649 |
|
650 //----------------------------------------------------------------------------- |
|
651 // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService |
|
652 //----------------------------------------------------------------------------- |
|
653 |
|
654 static nsresult |
|
655 OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal, |
|
656 nsIPrefBranch *aPrefBranch, |
|
657 bool pinned, |
|
658 bool *aAllowed) |
|
659 { |
|
660 *aAllowed = false; |
|
661 |
|
662 if (!aPrincipal) |
|
663 return NS_ERROR_INVALID_ARG; |
|
664 |
|
665 nsCOMPtr<nsIURI> uri; |
|
666 aPrincipal->GetURI(getter_AddRefs(uri)); |
|
667 |
|
668 if (!uri) |
|
669 return NS_OK; |
|
670 |
|
671 nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri); |
|
672 if (!innerURI) |
|
673 return NS_OK; |
|
674 |
|
675 // only http and https applications can use offline APIs. |
|
676 bool match; |
|
677 nsresult rv = innerURI->SchemeIs("http", &match); |
|
678 NS_ENSURE_SUCCESS(rv, rv); |
|
679 |
|
680 if (!match) { |
|
681 rv = innerURI->SchemeIs("https", &match); |
|
682 NS_ENSURE_SUCCESS(rv, rv); |
|
683 if (!match) { |
|
684 return NS_OK; |
|
685 } |
|
686 } |
|
687 |
|
688 nsAutoCString domain; |
|
689 rv = innerURI->GetAsciiHost(domain); |
|
690 NS_ENSURE_SUCCESS(rv, rv); |
|
691 |
|
692 if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) { |
|
693 *aAllowed = true; |
|
694 return NS_OK; |
|
695 } |
|
696 |
|
697 nsCOMPtr<nsIPermissionManager> permissionManager = |
|
698 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
|
699 if (!permissionManager) { |
|
700 return NS_OK; |
|
701 } |
|
702 |
|
703 uint32_t perm; |
|
704 const char *permName = pinned ? "pin-app" : "offline-app"; |
|
705 permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm); |
|
706 |
|
707 if (perm == nsIPermissionManager::ALLOW_ACTION || |
|
708 perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) { |
|
709 *aAllowed = true; |
|
710 } |
|
711 |
|
712 // offline-apps.allow_by_default is now effective at the cache selection |
|
713 // algorithm code (nsContentSink). |
|
714 |
|
715 return NS_OK; |
|
716 } |
|
717 |
|
718 NS_IMETHODIMP |
|
719 nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal, |
|
720 nsIPrefBranch *aPrefBranch, |
|
721 bool *aAllowed) |
|
722 { |
|
723 return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed); |
|
724 } |
|
725 |
|
726 NS_IMETHODIMP |
|
727 nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI, |
|
728 nsIPrefBranch *aPrefBranch, |
|
729 bool *aAllowed) |
|
730 { |
|
731 nsCOMPtr<nsIPrincipal> principal; |
|
732 nsContentUtils::GetSecurityManager()-> |
|
733 GetNoAppCodebasePrincipal(aURI, getter_AddRefs(principal)); |
|
734 return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed); |
|
735 } |
|
736 |
|
737 nsresult |
|
738 nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI, |
|
739 nsIPrefBranch *aPrefBranch, |
|
740 bool *aPinned) |
|
741 { |
|
742 nsCOMPtr<nsIPrincipal> principal; |
|
743 nsContentUtils::GetSecurityManager()-> |
|
744 GetNoAppCodebasePrincipal(aDocumentURI, getter_AddRefs(principal)); |
|
745 return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned); |
|
746 } |
|
747 |
|
748 NS_IMETHODIMP |
|
749 nsOfflineCacheUpdateService::AllowOfflineApp(nsIDOMWindow *aWindow, |
|
750 nsIPrincipal *aPrincipal) |
|
751 { |
|
752 nsresult rv; |
|
753 |
|
754 if (GeckoProcessType_Default != XRE_GetProcessType()) { |
|
755 TabChild* child = TabChild::GetFrom(aWindow); |
|
756 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); |
|
757 |
|
758 if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) { |
|
759 return NS_ERROR_FAILURE; |
|
760 } |
|
761 |
|
762 nsAutoCString domain; |
|
763 rv = aPrincipal->GetBaseDomain(domain); |
|
764 NS_ENSURE_SUCCESS(rv, rv); |
|
765 |
|
766 nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain); |
|
767 } |
|
768 else { |
|
769 nsCOMPtr<nsIPermissionManager> permissionManager = |
|
770 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
|
771 if (!permissionManager) |
|
772 return NS_ERROR_NOT_AVAILABLE; |
|
773 |
|
774 rv = permissionManager->AddFromPrincipal( |
|
775 aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION, |
|
776 nsIPermissionManager::EXPIRE_NEVER, 0); |
|
777 NS_ENSURE_SUCCESS(rv, rv); |
|
778 } |
|
779 |
|
780 return NS_OK; |
|
781 } |