1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,535 @@ 1.4 +/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "OfflineCacheUpdateChild.h" 1.10 +#include "nsOfflineCacheUpdate.h" 1.11 +#include "mozilla/dom/ContentChild.h" 1.12 +#include "mozilla/dom/TabChild.h" 1.13 +#include "mozilla/ipc/URIUtils.h" 1.14 +#include "mozilla/net/NeckoCommon.h" 1.15 + 1.16 +#include "nsIApplicationCacheContainer.h" 1.17 +#include "nsIApplicationCacheChannel.h" 1.18 +#include "nsIApplicationCacheService.h" 1.19 +#include "nsIDocShell.h" 1.20 +#include "nsIDocShellTreeItem.h" 1.21 +#include "nsIDocShellTreeOwner.h" 1.22 +#include "nsIDOMWindow.h" 1.23 +#include "nsIDOMOfflineResourceList.h" 1.24 +#include "nsIDocument.h" 1.25 +#include "nsIObserverService.h" 1.26 +#include "nsIURL.h" 1.27 +#include "nsITabChild.h" 1.28 +#include "nsNetCID.h" 1.29 +#include "nsNetUtil.h" 1.30 +#include "nsServiceManagerUtils.h" 1.31 +#include "nsStreamUtils.h" 1.32 +#include "nsThreadUtils.h" 1.33 +#include "nsProxyRelease.h" 1.34 +#include "prlog.h" 1.35 +#include "nsIAsyncVerifyRedirectCallback.h" 1.36 + 1.37 +using namespace mozilla::ipc; 1.38 +using namespace mozilla::net; 1.39 +using mozilla::dom::TabChild; 1.40 + 1.41 +#if defined(PR_LOGGING) 1.42 +// 1.43 +// To enable logging (see prlog.h for full details): 1.44 +// 1.45 +// set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 1.46 +// set NSPR_LOG_FILE=offlineupdate.log 1.47 +// 1.48 +// this enables PR_LOG_ALWAYS level information and places all output in 1.49 +// the file offlineupdate.log 1.50 +// 1.51 +extern PRLogModuleInfo *gOfflineCacheUpdateLog; 1.52 +#endif 1.53 + 1.54 +#undef LOG 1.55 +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) 1.56 + 1.57 +#undef LOG_ENABLED 1.58 +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) 1.59 + 1.60 +namespace mozilla { 1.61 +namespace docshell { 1.62 + 1.63 +//----------------------------------------------------------------------------- 1.64 +// OfflineCacheUpdateChild::nsISupports 1.65 +//----------------------------------------------------------------------------- 1.66 + 1.67 +NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild) 1.68 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.69 + NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate) 1.70 +NS_INTERFACE_MAP_END 1.71 + 1.72 +NS_IMPL_ADDREF(OfflineCacheUpdateChild) 1.73 +NS_IMPL_RELEASE(OfflineCacheUpdateChild) 1.74 + 1.75 +//----------------------------------------------------------------------------- 1.76 +// OfflineCacheUpdateChild <public> 1.77 +//----------------------------------------------------------------------------- 1.78 + 1.79 +OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow) 1.80 + : mState(STATE_UNINITIALIZED) 1.81 + , mIsUpgrade(false) 1.82 + , mAppID(NECKO_NO_APP_ID) 1.83 + , mInBrowser(false) 1.84 + , mWindow(aWindow) 1.85 + , mByteProgress(0) 1.86 +{ 1.87 +} 1.88 + 1.89 +OfflineCacheUpdateChild::~OfflineCacheUpdateChild() 1.90 +{ 1.91 + LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this)); 1.92 +} 1.93 + 1.94 +void 1.95 +OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers) 1.96 +{ 1.97 + for (int32_t i = 0; i < mWeakObservers.Count(); i++) { 1.98 + nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = 1.99 + do_QueryReferent(mWeakObservers[i]); 1.100 + if (observer) 1.101 + aObservers.AppendObject(observer); 1.102 + else 1.103 + mWeakObservers.RemoveObjectAt(i--); 1.104 + } 1.105 + 1.106 + for (int32_t i = 0; i < mObservers.Count(); i++) { 1.107 + aObservers.AppendObject(mObservers[i]); 1.108 + } 1.109 +} 1.110 + 1.111 +void 1.112 +OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument) 1.113 +{ 1.114 + // The design is one document for one cache update on the content process. 1.115 + NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update"); 1.116 + 1.117 + LOG(("Document %p added to update child %p", aDocument, this)); 1.118 + 1.119 + // Add document only if it was not loaded from an offline cache. 1.120 + // If it were loaded from an offline cache then it has already 1.121 + // been associated with it and must not be again cached as 1.122 + // implicit (which are the reasons we collect documents here). 1.123 + nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument); 1.124 + if (!document) 1.125 + return; 1.126 + 1.127 + nsIChannel* channel = document->GetChannel(); 1.128 + nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = 1.129 + do_QueryInterface(channel); 1.130 + if (!appCacheChannel) 1.131 + return; 1.132 + 1.133 + bool loadedFromAppCache; 1.134 + appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); 1.135 + if (loadedFromAppCache) 1.136 + return; 1.137 + 1.138 + mDocument = aDocument; 1.139 +} 1.140 + 1.141 +nsresult 1.142 +OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument, 1.143 + nsIApplicationCache *aApplicationCache) 1.144 +{ 1.145 + // Check that the document that requested this update was 1.146 + // previously associated with an application cache. If not, it 1.147 + // should be associated with the new one. 1.148 + nsCOMPtr<nsIApplicationCacheContainer> container = 1.149 + do_QueryInterface(aDocument); 1.150 + if (!container) 1.151 + return NS_OK; 1.152 + 1.153 + nsCOMPtr<nsIApplicationCache> existingCache; 1.154 + nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); 1.155 + NS_ENSURE_SUCCESS(rv, rv); 1.156 + 1.157 + if (!existingCache) { 1.158 +#if defined(PR_LOGGING) 1.159 + if (LOG_ENABLED()) { 1.160 + nsAutoCString clientID; 1.161 + if (aApplicationCache) { 1.162 + aApplicationCache->GetClientID(clientID); 1.163 + } 1.164 + LOG(("Update %p: associating app cache %s to document %p", 1.165 + this, clientID.get(), aDocument)); 1.166 + } 1.167 +#endif 1.168 + 1.169 + rv = container->SetApplicationCache(aApplicationCache); 1.170 + NS_ENSURE_SUCCESS(rv, rv); 1.171 + } 1.172 + 1.173 + return NS_OK; 1.174 +} 1.175 + 1.176 +//----------------------------------------------------------------------------- 1.177 +// OfflineCacheUpdateChild::nsIOfflineCacheUpdate 1.178 +//----------------------------------------------------------------------------- 1.179 + 1.180 +NS_IMETHODIMP 1.181 +OfflineCacheUpdateChild::Init(nsIURI *aManifestURI, 1.182 + nsIURI *aDocumentURI, 1.183 + nsIDOMDocument *aDocument, 1.184 + nsIFile *aCustomProfileDir, 1.185 + uint32_t aAppID, 1.186 + bool aInBrowser) 1.187 +{ 1.188 + nsresult rv; 1.189 + 1.190 + // Make sure the service has been initialized 1.191 + nsOfflineCacheUpdateService* service = 1.192 + nsOfflineCacheUpdateService::EnsureService(); 1.193 + if (!service) 1.194 + return NS_ERROR_FAILURE; 1.195 + 1.196 + if (aCustomProfileDir) { 1.197 + NS_ERROR("Custom Offline Cache Update not supported on child process"); 1.198 + return NS_ERROR_NOT_IMPLEMENTED; 1.199 + } 1.200 + 1.201 + LOG(("OfflineCacheUpdateChild::Init [%p]", this)); 1.202 + 1.203 + // Only http and https applications are supported. 1.204 + bool match; 1.205 + rv = aManifestURI->SchemeIs("http", &match); 1.206 + NS_ENSURE_SUCCESS(rv, rv); 1.207 + 1.208 + if (!match) { 1.209 + rv = aManifestURI->SchemeIs("https", &match); 1.210 + NS_ENSURE_SUCCESS(rv, rv); 1.211 + if (!match) 1.212 + return NS_ERROR_ABORT; 1.213 + } 1.214 + 1.215 + mManifestURI = aManifestURI; 1.216 + 1.217 + rv = mManifestURI->GetAsciiHost(mUpdateDomain); 1.218 + NS_ENSURE_SUCCESS(rv, rv); 1.219 + 1.220 + mDocumentURI = aDocumentURI; 1.221 + 1.222 + mState = STATE_INITIALIZED; 1.223 + 1.224 + if (aDocument) 1.225 + SetDocument(aDocument); 1.226 + 1.227 + mAppID = aAppID; 1.228 + mInBrowser = aInBrowser; 1.229 + 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +NS_IMETHODIMP 1.234 +OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI, 1.235 + const nsACString& clientID, 1.236 + nsIURI *aDocumentURI) 1.237 +{ 1.238 + NS_NOTREACHED("Not expected to do partial offline cache updates" 1.239 + " on the child process"); 1.240 + // For now leaving this method, we may discover we need it. 1.241 + return NS_ERROR_NOT_IMPLEMENTED; 1.242 +} 1.243 + 1.244 +NS_IMETHODIMP 1.245 +OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI, 1.246 + uint32_t aAppID, 1.247 + bool aInBrowser, 1.248 + nsIObserver *aObserver) 1.249 +{ 1.250 + NS_NOTREACHED("Not expected to do only update checks" 1.251 + " from the child process"); 1.252 + return NS_ERROR_NOT_IMPLEMENTED; 1.253 +} 1.254 + 1.255 +NS_IMETHODIMP 1.256 +OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain) 1.257 +{ 1.258 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.259 + 1.260 + aUpdateDomain = mUpdateDomain; 1.261 + return NS_OK; 1.262 +} 1.263 + 1.264 +NS_IMETHODIMP 1.265 +OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus) 1.266 +{ 1.267 + switch (mState) { 1.268 + case STATE_CHECKING : 1.269 + *aStatus = nsIDOMOfflineResourceList::CHECKING; 1.270 + return NS_OK; 1.271 + case STATE_DOWNLOADING : 1.272 + *aStatus = nsIDOMOfflineResourceList::DOWNLOADING; 1.273 + return NS_OK; 1.274 + default : 1.275 + *aStatus = nsIDOMOfflineResourceList::IDLE; 1.276 + return NS_OK; 1.277 + } 1.278 + 1.279 + return NS_ERROR_FAILURE; 1.280 +} 1.281 + 1.282 +NS_IMETHODIMP 1.283 +OfflineCacheUpdateChild::GetPartial(bool *aPartial) 1.284 +{ 1.285 + *aPartial = false; 1.286 + return NS_OK; 1.287 +} 1.288 + 1.289 +NS_IMETHODIMP 1.290 +OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI) 1.291 +{ 1.292 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.293 + 1.294 + NS_IF_ADDREF(*aManifestURI = mManifestURI); 1.295 + return NS_OK; 1.296 +} 1.297 + 1.298 +NS_IMETHODIMP 1.299 +OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded) 1.300 +{ 1.301 + NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); 1.302 + 1.303 + *aSucceeded = mSucceeded; 1.304 + 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +NS_IMETHODIMP 1.309 +OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade) 1.310 +{ 1.311 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.312 + 1.313 + *aIsUpgrade = mIsUpgrade; 1.314 + 1.315 + return NS_OK; 1.316 +} 1.317 + 1.318 +NS_IMETHODIMP 1.319 +OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI) 1.320 +{ 1.321 + return NS_ERROR_NOT_IMPLEMENTED; 1.322 +} 1.323 + 1.324 +NS_IMETHODIMP 1.325 +OfflineCacheUpdateChild::Cancel() 1.326 +{ 1.327 + return NS_ERROR_NOT_IMPLEMENTED; 1.328 +} 1.329 + 1.330 +NS_IMETHODIMP 1.331 +OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, 1.332 + bool aHoldWeak) 1.333 +{ 1.334 + LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this)); 1.335 + 1.336 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.337 + 1.338 + if (aHoldWeak) { 1.339 + nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver); 1.340 + mWeakObservers.AppendObject(weakRef); 1.341 + } else { 1.342 + mObservers.AppendObject(aObserver); 1.343 + } 1.344 + 1.345 + return NS_OK; 1.346 +} 1.347 + 1.348 +NS_IMETHODIMP 1.349 +OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) 1.350 +{ 1.351 + LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this)); 1.352 + 1.353 + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); 1.354 + 1.355 + for (int32_t i = 0; i < mWeakObservers.Count(); i++) { 1.356 + nsCOMPtr<nsIOfflineCacheUpdateObserver> observer = 1.357 + do_QueryReferent(mWeakObservers[i]); 1.358 + if (observer == aObserver) { 1.359 + mWeakObservers.RemoveObjectAt(i); 1.360 + return NS_OK; 1.361 + } 1.362 + } 1.363 + 1.364 + for (int32_t i = 0; i < mObservers.Count(); i++) { 1.365 + if (mObservers[i] == aObserver) { 1.366 + mObservers.RemoveObjectAt(i); 1.367 + return NS_OK; 1.368 + } 1.369 + } 1.370 + 1.371 + return NS_OK; 1.372 +} 1.373 + 1.374 +NS_IMETHODIMP 1.375 +OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result) 1.376 +{ 1.377 + NS_ENSURE_ARG(_result); 1.378 + 1.379 + *_result = mByteProgress; 1.380 + return NS_OK; 1.381 +} 1.382 + 1.383 +NS_IMETHODIMP 1.384 +OfflineCacheUpdateChild::Schedule() 1.385 +{ 1.386 + LOG(("OfflineCacheUpdateChild::Schedule [%p]", this)); 1.387 + 1.388 + NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child"); 1.389 + 1.390 + nsCOMPtr<nsPIDOMWindow> piWindow = 1.391 + do_QueryInterface(mWindow); 1.392 + mWindow = nullptr; 1.393 + 1.394 + nsIDocShell *docshell = piWindow->GetDocShell(); 1.395 + 1.396 + nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell); 1.397 + if (!item) { 1.398 + NS_WARNING("doc shell tree item is null"); 1.399 + return NS_ERROR_FAILURE; 1.400 + } 1.401 + 1.402 + nsCOMPtr<nsIDocShellTreeOwner> owner; 1.403 + item->GetTreeOwner(getter_AddRefs(owner)); 1.404 + 1.405 + nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner); 1.406 + // because owner implements nsITabChild, we can assume that it is 1.407 + // the one and only TabChild. 1.408 + TabChild* child = tabchild ? static_cast<TabChild*>(tabchild.get()) : nullptr; 1.409 + 1.410 + if (MissingRequiredTabChild(child, "offlinecacheupdate")) { 1.411 + return NS_ERROR_FAILURE; 1.412 + } 1.413 + 1.414 + URIParams manifestURI, documentURI; 1.415 + SerializeURI(mManifestURI, manifestURI); 1.416 + SerializeURI(mDocumentURI, documentURI); 1.417 + 1.418 + nsCOMPtr<nsIObserverService> observerService = 1.419 + mozilla::services::GetObserverService(); 1.420 + if (observerService) { 1.421 + LOG(("Calling offline-cache-update-added")); 1.422 + observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this), 1.423 + "offline-cache-update-added", 1.424 + nullptr); 1.425 + LOG(("Done offline-cache-update-added")); 1.426 + } 1.427 + 1.428 + // mDocument is non-null if both: 1.429 + // 1. this update was initiated by a document that referred a manifest 1.430 + // 2. the document has not already been loaded from the application cache 1.431 + // This tells the update to cache this document even in case the manifest 1.432 + // has not been changed since the last fetch. 1.433 + // See also nsOfflineCacheUpdate::ScheduleImplicit. 1.434 + bool stickDocument = mDocument != nullptr; 1.435 + 1.436 + // Need to addref ourself here, because the IPC stack doesn't hold 1.437 + // a reference to us. Will be released in RecvFinish() that identifies 1.438 + // the work has been done. 1.439 + child->SendPOfflineCacheUpdateConstructor(this, manifestURI, documentURI, 1.440 + stickDocument); 1.441 + 1.442 + // TabChild::DeallocPOfflineCacheUpdate will release this. 1.443 + NS_ADDREF_THIS(); 1.444 + 1.445 + return NS_OK; 1.446 +} 1.447 + 1.448 +bool 1.449 +OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId, 1.450 + const nsCString &cacheClientId) 1.451 +{ 1.452 + LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get())); 1.453 + 1.454 + nsresult rv; 1.455 + 1.456 + nsCOMPtr<nsIApplicationCache> cache = 1.457 + do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); 1.458 + if (NS_FAILED(rv)) 1.459 + return true; 1.460 + 1.461 + cache->InitAsHandle(cacheGroupId, cacheClientId); 1.462 + 1.463 + if (mDocument) { 1.464 + AssociateDocument(mDocument, cache); 1.465 + } 1.466 + 1.467 + nsCOMArray<nsIOfflineCacheUpdateObserver> observers; 1.468 + GatherObservers(observers); 1.469 + 1.470 + for (int32_t i = 0; i < observers.Count(); i++) 1.471 + observers[i]->ApplicationCacheAvailable(cache); 1.472 + 1.473 + return true; 1.474 +} 1.475 + 1.476 +bool 1.477 +OfflineCacheUpdateChild::RecvNotifyStateEvent(const uint32_t &event, 1.478 + const uint64_t &byteProgress) 1.479 +{ 1.480 + LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this)); 1.481 + 1.482 + mByteProgress = byteProgress; 1.483 + 1.484 + // Convert the public observer state to our internal state 1.485 + switch (event) { 1.486 + case nsIOfflineCacheUpdateObserver::STATE_CHECKING: 1.487 + mState = STATE_CHECKING; 1.488 + break; 1.489 + 1.490 + case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING: 1.491 + mState = STATE_DOWNLOADING; 1.492 + break; 1.493 + 1.494 + default: 1.495 + break; 1.496 + } 1.497 + 1.498 + nsCOMArray<nsIOfflineCacheUpdateObserver> observers; 1.499 + GatherObservers(observers); 1.500 + 1.501 + for (int32_t i = 0; i < observers.Count(); i++) 1.502 + observers[i]->UpdateStateChanged(this, event); 1.503 + 1.504 + return true; 1.505 +} 1.506 + 1.507 +bool 1.508 +OfflineCacheUpdateChild::RecvFinish(const bool &succeeded, 1.509 + const bool &isUpgrade) 1.510 +{ 1.511 + LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this)); 1.512 + 1.513 + nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this); 1.514 + 1.515 + mState = STATE_FINISHED; 1.516 + mSucceeded = succeeded; 1.517 + mIsUpgrade = isUpgrade; 1.518 + 1.519 + nsCOMPtr<nsIObserverService> observerService = 1.520 + mozilla::services::GetObserverService(); 1.521 + if (observerService) { 1.522 + LOG(("Calling offline-cache-update-completed")); 1.523 + observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this), 1.524 + "offline-cache-update-completed", 1.525 + nullptr); 1.526 + LOG(("Done offline-cache-update-completed")); 1.527 + } 1.528 + 1.529 + // This is by contract the last notification from the parent, release 1.530 + // us now. This is corresponding to AddRef in Schedule(). 1.531 + // TabChild::DeallocPOfflineCacheUpdate will call Release. 1.532 + OfflineCacheUpdateChild::Send__delete__(this); 1.533 + 1.534 + return true; 1.535 +} 1.536 + 1.537 +} 1.538 +}