uriloader/prefetch/OfflineCacheUpdateChild.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 #include "OfflineCacheUpdateChild.h"
     7 #include "nsOfflineCacheUpdate.h"
     8 #include "mozilla/dom/ContentChild.h"
     9 #include "mozilla/dom/TabChild.h"
    10 #include "mozilla/ipc/URIUtils.h"
    11 #include "mozilla/net/NeckoCommon.h"
    13 #include "nsIApplicationCacheContainer.h"
    14 #include "nsIApplicationCacheChannel.h"
    15 #include "nsIApplicationCacheService.h"
    16 #include "nsIDocShell.h"
    17 #include "nsIDocShellTreeItem.h"
    18 #include "nsIDocShellTreeOwner.h"
    19 #include "nsIDOMWindow.h"
    20 #include "nsIDOMOfflineResourceList.h"
    21 #include "nsIDocument.h"
    22 #include "nsIObserverService.h"
    23 #include "nsIURL.h"
    24 #include "nsITabChild.h"
    25 #include "nsNetCID.h"
    26 #include "nsNetUtil.h"
    27 #include "nsServiceManagerUtils.h"
    28 #include "nsStreamUtils.h"
    29 #include "nsThreadUtils.h"
    30 #include "nsProxyRelease.h"
    31 #include "prlog.h"
    32 #include "nsIAsyncVerifyRedirectCallback.h"
    34 using namespace mozilla::ipc;
    35 using namespace mozilla::net;
    36 using mozilla::dom::TabChild;
    38 #if defined(PR_LOGGING)
    39 //
    40 // To enable logging (see prlog.h for full details):
    41 //
    42 //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
    43 //    set NSPR_LOG_FILE=offlineupdate.log
    44 //
    45 // this enables PR_LOG_ALWAYS level information and places all output in
    46 // the file offlineupdate.log
    47 //
    48 extern PRLogModuleInfo *gOfflineCacheUpdateLog;
    49 #endif
    51 #undef LOG
    52 #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
    54 #undef LOG_ENABLED
    55 #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
    57 namespace mozilla {
    58 namespace docshell {
    60 //-----------------------------------------------------------------------------
    61 // OfflineCacheUpdateChild::nsISupports
    62 //-----------------------------------------------------------------------------
    64 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
    65   NS_INTERFACE_MAP_ENTRY(nsISupports)
    66   NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
    67 NS_INTERFACE_MAP_END
    69 NS_IMPL_ADDREF(OfflineCacheUpdateChild)
    70 NS_IMPL_RELEASE(OfflineCacheUpdateChild)
    72 //-----------------------------------------------------------------------------
    73 // OfflineCacheUpdateChild <public>
    74 //-----------------------------------------------------------------------------
    76 OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow)
    77     : mState(STATE_UNINITIALIZED)
    78     , mIsUpgrade(false)
    79     , mAppID(NECKO_NO_APP_ID)
    80     , mInBrowser(false)
    81     , mWindow(aWindow)
    82     , mByteProgress(0)
    83 {
    84 }
    86 OfflineCacheUpdateChild::~OfflineCacheUpdateChild()
    87 {
    88     LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this));
    89 }
    91 void
    92 OfflineCacheUpdateChild::GatherObservers(nsCOMArray<nsIOfflineCacheUpdateObserver> &aObservers)
    93 {
    94     for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
    95         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
    96             do_QueryReferent(mWeakObservers[i]);
    97         if (observer)
    98             aObservers.AppendObject(observer);
    99         else
   100             mWeakObservers.RemoveObjectAt(i--);
   101     }
   103     for (int32_t i = 0; i < mObservers.Count(); i++) {
   104         aObservers.AppendObject(mObservers[i]);
   105     }
   106 }
   108 void
   109 OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument)
   110 {
   111     // The design is one document for one cache update on the content process.
   112     NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update");
   114     LOG(("Document %p added to update child %p", aDocument, this));
   116     // Add document only if it was not loaded from an offline cache.
   117     // If it were loaded from an offline cache then it has already
   118     // been associated with it and must not be again cached as
   119     // implicit (which are the reasons we collect documents here).
   120     nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
   121     if (!document)
   122         return;
   124     nsIChannel* channel = document->GetChannel();
   125     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
   126         do_QueryInterface(channel);
   127     if (!appCacheChannel)
   128         return;
   130     bool loadedFromAppCache;
   131     appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache);
   132     if (loadedFromAppCache)
   133         return;
   135     mDocument = aDocument;
   136 }
   138 nsresult
   139 OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument,
   140                                         nsIApplicationCache *aApplicationCache)
   141 {
   142     // Check that the document that requested this update was
   143     // previously associated with an application cache.  If not, it
   144     // should be associated with the new one.
   145     nsCOMPtr<nsIApplicationCacheContainer> container =
   146         do_QueryInterface(aDocument);
   147     if (!container)
   148         return NS_OK;
   150     nsCOMPtr<nsIApplicationCache> existingCache;
   151     nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache));
   152     NS_ENSURE_SUCCESS(rv, rv);
   154     if (!existingCache) {
   155 #if defined(PR_LOGGING)
   156         if (LOG_ENABLED()) {
   157             nsAutoCString clientID;
   158             if (aApplicationCache) {
   159                 aApplicationCache->GetClientID(clientID);
   160             }
   161             LOG(("Update %p: associating app cache %s to document %p",
   162                  this, clientID.get(), aDocument));
   163         }
   164 #endif
   166         rv = container->SetApplicationCache(aApplicationCache);
   167         NS_ENSURE_SUCCESS(rv, rv);
   168     }
   170     return NS_OK;
   171 }
   173 //-----------------------------------------------------------------------------
   174 // OfflineCacheUpdateChild::nsIOfflineCacheUpdate
   175 //-----------------------------------------------------------------------------
   177 NS_IMETHODIMP
   178 OfflineCacheUpdateChild::Init(nsIURI *aManifestURI,
   179                               nsIURI *aDocumentURI,
   180                               nsIDOMDocument *aDocument,
   181                               nsIFile *aCustomProfileDir,
   182                               uint32_t aAppID,
   183                               bool aInBrowser)
   184 {
   185     nsresult rv;
   187     // Make sure the service has been initialized
   188     nsOfflineCacheUpdateService* service =
   189         nsOfflineCacheUpdateService::EnsureService();
   190     if (!service)
   191         return NS_ERROR_FAILURE;
   193     if (aCustomProfileDir) {
   194         NS_ERROR("Custom Offline Cache Update not supported on child process");
   195         return NS_ERROR_NOT_IMPLEMENTED;
   196     }
   198     LOG(("OfflineCacheUpdateChild::Init [%p]", this));
   200     // Only http and https applications are supported.
   201     bool match;
   202     rv = aManifestURI->SchemeIs("http", &match);
   203     NS_ENSURE_SUCCESS(rv, rv);
   205     if (!match) {
   206         rv = aManifestURI->SchemeIs("https", &match);
   207         NS_ENSURE_SUCCESS(rv, rv);
   208         if (!match)
   209             return NS_ERROR_ABORT;
   210     }
   212     mManifestURI = aManifestURI;
   214     rv = mManifestURI->GetAsciiHost(mUpdateDomain);
   215     NS_ENSURE_SUCCESS(rv, rv);
   217     mDocumentURI = aDocumentURI;
   219     mState = STATE_INITIALIZED;
   221     if (aDocument)
   222         SetDocument(aDocument);
   224     mAppID = aAppID;
   225     mInBrowser = aInBrowser;
   227     return NS_OK;
   228 }
   230 NS_IMETHODIMP
   231 OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI,
   232                                   const nsACString& clientID,
   233                                   nsIURI *aDocumentURI)
   234 {
   235     NS_NOTREACHED("Not expected to do partial offline cache updates"
   236                   " on the child process");
   237     // For now leaving this method, we may discover we need it.
   238     return NS_ERROR_NOT_IMPLEMENTED;
   239 }
   241 NS_IMETHODIMP
   242 OfflineCacheUpdateChild::InitForUpdateCheck(nsIURI *aManifestURI,
   243                                             uint32_t aAppID,
   244                                             bool aInBrowser,
   245                                             nsIObserver *aObserver)
   246 {
   247     NS_NOTREACHED("Not expected to do only update checks"
   248                   " from the child process");
   249     return NS_ERROR_NOT_IMPLEMENTED;
   250 }
   252 NS_IMETHODIMP
   253 OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain)
   254 {
   255     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
   257     aUpdateDomain = mUpdateDomain;
   258     return NS_OK;
   259 }
   261 NS_IMETHODIMP
   262 OfflineCacheUpdateChild::GetStatus(uint16_t *aStatus)
   263 {
   264     switch (mState) {
   265     case STATE_CHECKING :
   266         *aStatus = nsIDOMOfflineResourceList::CHECKING;
   267         return NS_OK;
   268     case STATE_DOWNLOADING :
   269         *aStatus = nsIDOMOfflineResourceList::DOWNLOADING;
   270         return NS_OK;
   271     default :
   272         *aStatus = nsIDOMOfflineResourceList::IDLE;
   273         return NS_OK;
   274     }
   276     return NS_ERROR_FAILURE;
   277 }
   279 NS_IMETHODIMP
   280 OfflineCacheUpdateChild::GetPartial(bool *aPartial)
   281 {
   282     *aPartial = false;
   283     return NS_OK;
   284 }
   286 NS_IMETHODIMP
   287 OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI)
   288 {
   289     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
   291     NS_IF_ADDREF(*aManifestURI = mManifestURI);
   292     return NS_OK;
   293 }
   295 NS_IMETHODIMP
   296 OfflineCacheUpdateChild::GetSucceeded(bool *aSucceeded)
   297 {
   298     NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE);
   300     *aSucceeded = mSucceeded;
   302     return NS_OK;
   303 }
   305 NS_IMETHODIMP
   306 OfflineCacheUpdateChild::GetIsUpgrade(bool *aIsUpgrade)
   307 {
   308     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
   310     *aIsUpgrade = mIsUpgrade;
   312     return NS_OK;
   313 }
   315 NS_IMETHODIMP
   316 OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI)
   317 {
   318     return NS_ERROR_NOT_IMPLEMENTED;
   319 }
   321 NS_IMETHODIMP
   322 OfflineCacheUpdateChild::Cancel()
   323 {
   324     return NS_ERROR_NOT_IMPLEMENTED;
   325 }
   327 NS_IMETHODIMP
   328 OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
   329                                   bool aHoldWeak)
   330 {
   331     LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this));
   333     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
   335     if (aHoldWeak) {
   336         nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
   337         mWeakObservers.AppendObject(weakRef);
   338     } else {
   339         mObservers.AppendObject(aObserver);
   340     }
   342     return NS_OK;
   343 }
   345 NS_IMETHODIMP
   346 OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
   347 {
   348     LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this));
   350     NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
   352     for (int32_t i = 0; i < mWeakObservers.Count(); i++) {
   353         nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
   354             do_QueryReferent(mWeakObservers[i]);
   355         if (observer == aObserver) {
   356             mWeakObservers.RemoveObjectAt(i);
   357             return NS_OK;
   358         }
   359     }
   361     for (int32_t i = 0; i < mObservers.Count(); i++) {
   362         if (mObservers[i] == aObserver) {
   363             mObservers.RemoveObjectAt(i);
   364             return NS_OK;
   365         }
   366     }
   368     return NS_OK;
   369 }
   371 NS_IMETHODIMP
   372 OfflineCacheUpdateChild::GetByteProgress(uint64_t * _result)
   373 {
   374     NS_ENSURE_ARG(_result);
   376     *_result = mByteProgress;
   377     return NS_OK;
   378 }
   380 NS_IMETHODIMP
   381 OfflineCacheUpdateChild::Schedule()
   382 {
   383     LOG(("OfflineCacheUpdateChild::Schedule [%p]", this));
   385     NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child");
   387     nsCOMPtr<nsPIDOMWindow> piWindow = 
   388         do_QueryInterface(mWindow);
   389     mWindow = nullptr;
   391     nsIDocShell *docshell = piWindow->GetDocShell();
   393     nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(docshell);
   394     if (!item) {
   395       NS_WARNING("doc shell tree item is null");
   396       return NS_ERROR_FAILURE;
   397     }
   399     nsCOMPtr<nsIDocShellTreeOwner> owner;
   400     item->GetTreeOwner(getter_AddRefs(owner));
   402     nsCOMPtr<nsITabChild> tabchild = do_GetInterface(owner);
   403     // because owner implements nsITabChild, we can assume that it is
   404     // the one and only TabChild.
   405     TabChild* child = tabchild ? static_cast<TabChild*>(tabchild.get()) : nullptr;
   407     if (MissingRequiredTabChild(child, "offlinecacheupdate")) {
   408       return NS_ERROR_FAILURE;
   409     }
   411     URIParams manifestURI, documentURI;
   412     SerializeURI(mManifestURI, manifestURI);
   413     SerializeURI(mDocumentURI, documentURI);
   415     nsCOMPtr<nsIObserverService> observerService =
   416       mozilla::services::GetObserverService();
   417     if (observerService) {
   418       LOG(("Calling offline-cache-update-added"));
   419       observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
   420                                        "offline-cache-update-added",
   421                                        nullptr);
   422       LOG(("Done offline-cache-update-added"));
   423     }
   425     // mDocument is non-null if both:
   426     // 1. this update was initiated by a document that referred a manifest
   427     // 2. the document has not already been loaded from the application cache
   428     // This tells the update to cache this document even in case the manifest
   429     // has not been changed since the last fetch.
   430     // See also nsOfflineCacheUpdate::ScheduleImplicit.
   431     bool stickDocument = mDocument != nullptr; 
   433     // Need to addref ourself here, because the IPC stack doesn't hold
   434     // a reference to us. Will be released in RecvFinish() that identifies 
   435     // the work has been done.
   436     child->SendPOfflineCacheUpdateConstructor(this, manifestURI, documentURI,
   437                                               stickDocument);
   439     // TabChild::DeallocPOfflineCacheUpdate will release this.
   440     NS_ADDREF_THIS();
   442     return NS_OK;
   443 }
   445 bool
   446 OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId,
   447                                                   const nsCString &cacheClientId)
   448 {
   449     LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get()));
   451     nsresult rv;
   453     nsCOMPtr<nsIApplicationCache> cache =
   454         do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv);
   455     if (NS_FAILED(rv))
   456       return true;
   458     cache->InitAsHandle(cacheGroupId, cacheClientId);
   460     if (mDocument) {
   461         AssociateDocument(mDocument, cache);
   462     }
   464     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
   465     GatherObservers(observers);
   467     for (int32_t i = 0; i < observers.Count(); i++)
   468         observers[i]->ApplicationCacheAvailable(cache);
   470     return true;
   471 }
   473 bool
   474 OfflineCacheUpdateChild::RecvNotifyStateEvent(const uint32_t &event,
   475                                               const uint64_t &byteProgress)
   476 {
   477     LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this));
   479     mByteProgress = byteProgress;
   481     // Convert the public observer state to our internal state
   482     switch (event) {
   483         case nsIOfflineCacheUpdateObserver::STATE_CHECKING:
   484             mState = STATE_CHECKING;
   485             break;
   487         case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING:
   488             mState = STATE_DOWNLOADING;
   489             break;
   491         default:
   492             break;
   493     }
   495     nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
   496     GatherObservers(observers);
   498     for (int32_t i = 0; i < observers.Count(); i++)
   499         observers[i]->UpdateStateChanged(this, event);
   501     return true;
   502 }
   504 bool
   505 OfflineCacheUpdateChild::RecvFinish(const bool &succeeded,
   506                                     const bool &isUpgrade)
   507 {
   508     LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this));
   510     nsRefPtr<OfflineCacheUpdateChild> kungFuDeathGrip(this);
   512     mState = STATE_FINISHED;
   513     mSucceeded = succeeded;
   514     mIsUpgrade = isUpgrade;
   516     nsCOMPtr<nsIObserverService> observerService =
   517       mozilla::services::GetObserverService();
   518     if (observerService) {
   519         LOG(("Calling offline-cache-update-completed"));
   520         observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(this),
   521                                          "offline-cache-update-completed",
   522                                          nullptr);
   523         LOG(("Done offline-cache-update-completed"));
   524     }
   526     // This is by contract the last notification from the parent, release
   527     // us now. This is corresponding to AddRef in Schedule().
   528     // TabChild::DeallocPOfflineCacheUpdate will call Release.
   529     OfflineCacheUpdateChild::Send__delete__(this);
   531     return true;
   532 }
   534 }
   535 }

mercurial