Bug 442812: Implement the application cache selection algorithm. r+sr=bz

This commit is contained in:
Honza Bambas 2008-08-27 18:15:32 -07:00
parent 14f256186b
commit bec376906f
12 changed files with 635 additions and 194 deletions

View File

@ -316,118 +316,7 @@ PRBool
nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
nsIURI* aTargetURI)
{
// Note that this is not an Equals() test on purpose -- for URIs that don't
// support host/port, we want equality to basically be object identity, for
// security purposes. Otherwise, for example, two javascript: URIs that
// are otherwise unrelated could end up "same origin", which would be
// unfortunate.
if (aSourceURI && aSourceURI == aTargetURI)
{
return PR_TRUE;
}
if (!aTargetURI || !aSourceURI)
{
return PR_FALSE;
}
// If either URI is a nested URI, get the base URI
nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(aSourceURI);
nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
if (!sourceBaseURI || !targetBaseURI)
return PR_FALSE;
// Compare schemes
nsCAutoString targetScheme;
PRBool sameScheme = PR_FALSE;
if (NS_FAILED( targetBaseURI->GetScheme(targetScheme) ) ||
NS_FAILED( sourceBaseURI->SchemeIs(targetScheme.get(), &sameScheme) ) ||
!sameScheme)
{
// Not same-origin if schemes differ
return PR_FALSE;
}
// special handling for file: URIs
if (targetScheme.EqualsLiteral("file"))
{
// in traditional unsafe behavior all files are the same origin
if (!sStrictFileOriginPolicy)
return PR_TRUE;
nsCOMPtr<nsIFileURL> sourceFileURL(do_QueryInterface(sourceBaseURI));
nsCOMPtr<nsIFileURL> targetFileURL(do_QueryInterface(targetBaseURI));
if (!sourceFileURL || !targetFileURL)
return PR_FALSE;
nsCOMPtr<nsIFile> sourceFile, targetFile;
sourceFileURL->GetFile(getter_AddRefs(sourceFile));
targetFileURL->GetFile(getter_AddRefs(targetFile));
if (!sourceFile || !targetFile)
return PR_FALSE;
// Otherwise they had better match
PRBool filesAreEqual = PR_FALSE;
nsresult rv = sourceFile->Equals(targetFile, &filesAreEqual);
return NS_SUCCEEDED(rv) && filesAreEqual;
}
// Special handling for mailnews schemes
if (targetScheme.EqualsLiteral("imap") ||
targetScheme.EqualsLiteral("mailbox") ||
targetScheme.EqualsLiteral("news"))
{
// Each message is a distinct trust domain; use the
// whole spec for comparison
nsCAutoString targetSpec;
nsCAutoString sourceSpec;
return ( NS_SUCCEEDED( targetBaseURI->GetSpec(targetSpec) ) &&
NS_SUCCEEDED( sourceBaseURI->GetSpec(sourceSpec) ) &&
targetSpec.Equals(sourceSpec) );
}
// Compare hosts
nsCAutoString targetHost;
nsCAutoString sourceHost;
if (NS_FAILED( targetBaseURI->GetHost(targetHost) ) ||
NS_FAILED( sourceBaseURI->GetHost(sourceHost) ) ||
!targetHost.Equals(sourceHost, nsCaseInsensitiveCStringComparator()))
{
// Not same-origin if hosts differ
return PR_FALSE;
}
// Compare ports
PRInt32 targetPort;
nsresult rv = targetBaseURI->GetPort(&targetPort);
PRInt32 sourcePort;
if (NS_SUCCEEDED(rv))
rv = sourceBaseURI->GetPort(&sourcePort);
PRBool result = NS_SUCCEEDED(rv) && targetPort == sourcePort;
// If the port comparison failed, see if either URL has a
// port of -1. If so, replace -1 with the default port
// for that scheme.
if (NS_SUCCEEDED(rv) && !result &&
(sourcePort == -1 || targetPort == -1))
{
NS_ENSURE_TRUE(sIOService, PR_FALSE);
PRInt32 defaultPort = NS_GetDefaultPort(targetScheme.get());
if (defaultPort == -1)
return PR_FALSE; // No default port for this scheme
if (sourcePort == -1)
sourcePort = defaultPort;
else if (targetPort == -1)
targetPort = defaultPort;
result = targetPort == sourcePort;
}
return result;
return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
}
NS_IMETHODIMP

View File

@ -1250,6 +1250,11 @@ public:
*/
static PRBool OfflineAppAllowed(nsIURI *aURI);
/**
* Check whether an application should be allowed to use offline APIs.
*/
static PRBool OfflineAppAllowed(nsIPrincipal *aPrincipal);
/**
* Increases the count of blockers preventing scripts from running.
* NOTE: You might want to use nsAutoScriptBlocker rather than calling

View File

@ -59,6 +59,7 @@ REQUIRES = xpcom \
js \
webshell \
necko \
nkcache \
mimetype \
exthandler \
chardet \

View File

@ -73,6 +73,7 @@
#include "nsIOfflineCacheUpdate.h"
#include "nsIApplicationCache.h"
#include "nsIApplicationCacheContainer.h"
#include "nsIApplicationCacheService.h"
#include "nsIScriptSecurityManager.h"
#include "nsIDOMLoadStatus.h"
#include "nsICookieService.h"
@ -96,6 +97,10 @@
#include "nsPresShellIterator.h"
#include "nsPIDOMWindow.h"
#include "mozAutoDocUpdate.h"
#include "nsIWebNavigation.h"
#include "nsIDocumentLoader.h"
#include "nsICachingChannel.h"
#include "nsICacheEntryDescriptor.h"
PRLogModuleInfo* gContentSinkLogModuleInfo;
@ -847,79 +852,304 @@ nsContentSink::PrefetchHref(const nsAString &aHref,
}
}
nsresult
nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey)
{
aCacheKey.Truncate();
nsresult rv;
nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> token;
rv = cachingChannel->GetCacheToken(getter_AddRefs(token));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICacheEntryDescriptor> descriptor = do_QueryInterface(token, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = descriptor->GetKey(aCacheKey);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
nsIURI *aManifestURI,
PRBool aIsTopDocument,
PRBool aFetchedWithHTTPGetOrEquiv,
CacheSelectionAction *aAction)
{
*aAction = CACHE_SELECTION_NONE;
nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
do_QueryInterface(mDocument);
NS_ASSERTION(applicationCacheDocument,
"mDocument must implement nsIApplicationCacheContainer.");
nsresult rv;
// We might decide on a new application cache...
nsCOMPtr<nsIApplicationCache> applicationCache = aLoadApplicationCache;
if (applicationCache) {
nsCAutoString groupID;
rv = applicationCache->GetGroupID(groupID);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> groupURI;
rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
NS_ENSURE_SUCCESS(rv, rv);
PRBool equal = PR_FALSE;
rv = groupURI->Equals(aManifestURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (!equal) {
// This is a foreign entry, mark it as such. If this is a
// toplevel load, force a reload to avoid loading the foreign
// entry. The next attempt will not choose this cache entry
// (because it has been marked foreign).
nsCAutoString cachekey;
rv = GetChannelCacheKey(mDocument->GetChannel(), cachekey);
NS_ENSURE_SUCCESS(rv, rv);
rv = applicationCache->MarkEntry(cachekey,
nsIApplicationCache::ITEM_FOREIGN);
NS_ENSURE_SUCCESS(rv, rv);
if (aIsTopDocument) {
*aAction = CACHE_SELECTION_RELOAD;
}
return NS_OK;
}
if (aIsTopDocument) {
// This is a top level document and the http manifest attribute
// URI is equal to the manifest URI of the cache the document
// was loaded from - associate the document with that cache and
// invoke the cache update process.
rv = applicationCacheDocument->SetApplicationCache(applicationCache);
NS_ENSURE_SUCCESS(rv, rv);
*aAction = CACHE_SELECTION_UPDATE;
}
}
else {
// The document was not loaded from an application cache
// Here we know the manifest has the same origin as the
// document. There is call to CheckMayLoad() on it above.
if (!aFetchedWithHTTPGetOrEquiv) {
// The document was not loaded using HTTP GET or equivalent
// method. The spec says to run the cache selection algorithm w/o
// the manifest specified but we can just do return NS_OK here.
return NS_OK;
}
// If there is an existing application cache for this manifest,
// associate it with the document.
nsCAutoString manifestURISpec;
rv = aManifestURI->GetAsciiSpec(manifestURISpec);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIApplicationCacheService> appCacheService =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
if (!appCacheService) {
// No application cache service, nothing to do here.
return NS_OK;
}
rv = appCacheService->GetActiveCache(manifestURISpec,
getter_AddRefs(applicationCache));
NS_ENSURE_SUCCESS(rv, rv);
if (applicationCache) {
rv = applicationCacheDocument->SetApplicationCache(applicationCache);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
// XXX bug 443023: if there is already a scheduled update or
// update in progress we have to add this document as
// an implicit entry.
}
// Always do an update in this case
*aAction = CACHE_SELECTION_UPDATE;
}
if (applicationCache) {
// We are now associated with an application cache. This item
// should be marked as an implicit entry.
nsCAutoString cachekey;
rv = GetChannelCacheKey(mDocument->GetChannel(), cachekey);
if (NS_SUCCEEDED(rv)) {
rv = applicationCache->MarkEntry(cachekey,
nsIApplicationCache::ITEM_IMPLICIT);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;
}
nsresult
nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache,
PRBool aIsTopDocument,
nsIURI **aManifestURI,
CacheSelectionAction *aAction)
{
*aManifestURI = nsnull;
*aAction = CACHE_SELECTION_NONE;
if (!aIsTopDocument || !aLoadApplicationCache) {
return NS_OK;
}
nsresult rv;
// The document was loaded from an application cache, use that
// application cache as the document's application cache.
nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
do_QueryInterface(mDocument);
NS_ASSERTION(applicationCacheDocument,
"mDocument must implement nsIApplicationCacheContainer.");
rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
NS_ENSURE_SUCCESS(rv, rv);
// Return the uri and invoke the update process for the selected
// application cache.
nsCAutoString groupID;
rv = aLoadApplicationCache->GetGroupID(groupID);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewURI(aManifestURI, groupID);
NS_ENSURE_SUCCESS(rv, rv);
*aAction = CACHE_SELECTION_UPDATE;
return NS_OK;
}
void
nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
{
// Only check the manifest for root document nodes.
if (aElement != mDocument->GetRootContent()) {
return;
}
nsresult rv;
// Check for a manifest= attribute.
nsAutoString manifestSpec;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
if (manifestSpec.IsEmpty() ||
manifestSpec.FindChar('#') != kNotFound) {
// Grab the application cache the document was loaded from, if any.
nsCOMPtr<nsIApplicationCache> applicationCache;
nsCOMPtr<nsIApplicationCacheContainer> applicationCacheChannel =
do_QueryInterface(mDocument->GetChannel());
if (applicationCacheChannel) {
rv = applicationCacheChannel->GetApplicationCache(
getter_AddRefs(applicationCache));
if (NS_FAILED(rv)) {
return;
}
}
if (manifestSpec.IsEmpty() && !applicationCache) {
// Not loaded from an application cache, and no manifest
// attribute. Nothing to do here.
return;
}
// We only care about manifests in toplevel windows.
nsCOMPtr<nsPIDOMWindow> pwindow =
do_QueryInterface(mDocument->GetScriptGlobalObject());
if (!pwindow) {
// The manifest attribute is handled differently if the document is
// not toplevel.
nsCOMPtr<nsIDOMWindow> window = mDocument->GetWindow();
if (!window)
return;
}
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(pwindow->GetOuterWindow());
if (!window) {
return;
}
nsCOMPtr<nsIDOMWindow> parent;
window->GetParent(getter_AddRefs(parent));
if (parent.get() != window.get()) {
return;
}
// Only update if the document has permission to use offline APIs.
if (!nsContentUtils::OfflineAppAllowed(mDocumentURI)) {
return;
}
// XXX: at this point in the spec there is an algorithm for
// confirming whether the cache that was selected at load time was
// the proper application cache for this document. This will
// be implemented in a separate patch; For now just assume that we
// chose an acceptable application cache.
nsCOMPtr<nsIApplicationCacheContainer> channelContainer =
do_QueryInterface(mDocument->GetChannel());
nsCOMPtr<nsIApplicationCacheContainer> docContainer =
do_QueryInterface(mDocument);
if (channelContainer && docContainer) {
nsCOMPtr<nsIApplicationCache> appCache;
channelContainer->GetApplicationCache(getter_AddRefs(appCache));
docContainer->SetApplicationCache(appCache);
}
PRBool isTop = (parent == window);
CacheSelectionAction action = CACHE_SELECTION_NONE;
nsCOMPtr<nsIURI> manifestURI;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
manifestSpec, mDocument,
mDocumentURI);
if (!manifestURI) {
return;
if (manifestSpec.IsEmpty()) {
rv = SelectDocAppCacheNoManifest(applicationCache,
isTop,
getter_AddRefs(manifestURI),
&action);
if (NS_FAILED(rv)) {
return;
}
}
else {
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
manifestSpec, mDocument,
mDocumentURI);
if (!manifestURI) {
return;
}
// Documents must list a manifest from the same origin
rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, PR_TRUE);
if (NS_FAILED(rv)) {
return;
}
// Only continue if the document has permission to use offline APIs.
if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
return;
}
PRBool fetchedWithHTTPGetOrEquiv = PR_FALSE;
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel()));
if (httpChannel) {
nsCAutoString method;
rv = httpChannel->GetRequestMethod(method);
if (NS_SUCCEEDED(rv))
fetchedWithHTTPGetOrEquiv = method.Equals("GET");
}
rv = SelectDocAppCache(applicationCache, manifestURI, isTop,
fetchedWithHTTPGetOrEquiv, &action);
if (NS_FAILED(rv)) {
return;
}
}
// Documents must list a manifest from the same origin
nsresult rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, PR_TRUE);
if (NS_FAILED(rv)) {
switch (action)
{
case CACHE_SELECTION_NONE:
return;
}
case CACHE_SELECTION_UPDATE: {
nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
// Start the update
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
if (updateService) {
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
}
break;
}
case CACHE_SELECTION_RELOAD: {
// This situation occurs only for toplevel documents, see bottom
// of SelectDocAppCache method.
NS_ASSERTION(isTop, "Should only reload toplevel documents!");
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
webNav->Stop(nsIWebNavigation::STOP_ALL);
webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
break;
}
}
}
void

View File

@ -76,6 +76,7 @@ class nsIContent;
class nsIViewManager;
class nsNodeInfoManager;
class nsScriptLoader;
class nsIApplicationCache;
#ifdef NS_DEBUG
@ -150,6 +151,25 @@ protected:
nsContentSink();
virtual ~nsContentSink();
enum CacheSelectionAction {
// There is no offline cache manifest specified by the document,
// or the document was loaded from a cache other than the one it
// specifies via its manifest attribute and IS NOT a top-level
// document, or an error occurred during the cache selection
// algorithm.
CACHE_SELECTION_NONE = 0,
// The offline cache manifest must be updated.
CACHE_SELECTION_UPDATE = 1,
// The document was loaded from a cache other than the one it
// specifies via its manifest attribute and IS a top-level
// document. In this case, the document is marked as foreign in
// the cache it was loaded from and must be reloaded from the
// correct cache (the one it specifies).
CACHE_SELECTION_RELOAD = 2
};
nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
nsISupports* aContainer, nsIChannel* aChannel);
@ -171,6 +191,60 @@ protected:
void PrefetchHref(const nsAString &aHref, nsIContent *aSource,
PRBool aExplicit);
// Gets the cache key (used to identify items in a cache) of the channel.
nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);
// There is an offline cache manifest attribute specified and the
// document is allowed to use the offline cache. Process the cache
// selection algorithm for this document and the manifest. Result is
// an action that must be taken on the manifest, see
// CacheSelectionAction enum above.
//
// @param aLoadApplicationCache
// The application cache from which the load originated, if
// any.
// @param aManifestURI
// The manifest URI listed in the document.
// @param aIsTopDocument
// TRUE if this is a toplevel document.
// @param aFetchedWithHTTPGetOrEquiv
// TRUE if this was fetched using the HTTP GET method.
// @param aAction
// Out parameter, returns the action that should be performed
// by the calling function.
nsresult SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
nsIURI *aManifestURI,
PRBool aIsTopDocument,
PRBool aFetchedWithHTTPGetOrEquiv,
CacheSelectionAction *aAction);
// There is no offline cache manifest attribute specified. Process
// the cache selection algorithm w/o the manifest. Result is an
// action that must be taken, see CacheSelectionAction enum
// above. In case the offline cache manifest has to be updated the
// manifest URI is returned in aManifestURI.
//
// @param aLoadApplicationCache
// The application cache from which the load originated, if
// any.
// @param aIsTopDocument
// TRUE if this is a toplevel document.
// @param aManifestURI
// Out parameter, returns the manifest URI of the cache that
// was selected.
// @param aAction
// Out parameter, returns the action that should be performed
// by the calling function.
nsresult SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache,
PRBool aIsTopDocument,
nsIURI **aManifestURI,
CacheSelectionAction *aAction);
// Searches for the offline cache manifest attribute and calls one
// of the above defined methods to select the document's application
// cache, let it be associated with the document and eventually
// schedule the cache update process.
void ProcessOfflineManifest(nsIContent *aElement);
// Tries to scroll to the URI's named anchor. Once we've successfully

View File

@ -811,6 +811,16 @@ nsContentUtils::OfflineAppAllowed(nsIURI *aURI)
return NS_OfflineAppAllowed(aURI, sPrefBranch);
}
/* static */
PRBool
nsContentUtils::OfflineAppAllowed(nsIPrincipal *aPrincipal)
{
nsCOMPtr<nsIURI> codebaseURI;
aPrincipal->GetURI(getter_AddRefs(codebaseURI));
return OfflineAppAllowed(codebaseURI);
}
// static
void
nsContentUtils::Shutdown()

View File

@ -338,7 +338,11 @@ nsDOMOfflineResourceList::Add(const nsAString& aURI)
do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->Init(PR_TRUE, mManifestURI, mDocumentURI);
nsCAutoString clientID;
rv = appCache->GetClientID(clientID);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->InitPartial(mManifestURI, clientID, mDocumentURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->AddDynamicURI(requestedURI);

View File

@ -74,6 +74,7 @@
#include "nsIStringStream.h"
#include "nsILocalFile.h"
#include "nsIFileStreams.h"
#include "nsIFileURL.h"
#include "nsIProtocolProxyService.h"
#include "nsIProxyInfo.h"
#include "nsIFileStreams.h"
@ -1465,4 +1466,128 @@ NS_OfflineAppAllowed(nsIURI *aURI, nsIPrefBranch *aPrefBranch = nsnull)
return allowed;
}
inline PRBool
NS_SecurityCompareURIs(nsIURI* aSourceURI,
nsIURI* aTargetURI,
PRBool aStrictFileOriginPolicy)
{
// Note that this is not an Equals() test on purpose -- for URIs that don't
// support host/port, we want equality to basically be object identity, for
// security purposes. Otherwise, for example, two javascript: URIs that
// are otherwise unrelated could end up "same origin", which would be
// unfortunate.
if (aSourceURI && aSourceURI == aTargetURI)
{
return PR_TRUE;
}
if (!aTargetURI || !aSourceURI)
{
return PR_FALSE;
}
// If either URI is a nested URI, get the base URI
nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(aSourceURI);
nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
if (!sourceBaseURI || !targetBaseURI)
return PR_FALSE;
// Compare schemes
nsCAutoString targetScheme;
PRBool sameScheme = PR_FALSE;
if (NS_FAILED( targetBaseURI->GetScheme(targetScheme) ) ||
NS_FAILED( sourceBaseURI->SchemeIs(targetScheme.get(), &sameScheme) ) ||
!sameScheme)
{
// Not same-origin if schemes differ
return PR_FALSE;
}
// special handling for file: URIs
if (targetScheme.EqualsLiteral("file"))
{
// in traditional unsafe behavior all files are the same origin
if (!aStrictFileOriginPolicy)
return PR_TRUE;
nsCOMPtr<nsIFileURL> sourceFileURL(do_QueryInterface(sourceBaseURI));
nsCOMPtr<nsIFileURL> targetFileURL(do_QueryInterface(targetBaseURI));
if (!sourceFileURL || !targetFileURL)
return PR_FALSE;
nsCOMPtr<nsIFile> sourceFile, targetFile;
sourceFileURL->GetFile(getter_AddRefs(sourceFile));
targetFileURL->GetFile(getter_AddRefs(targetFile));
if (!sourceFile || !targetFile)
return PR_FALSE;
// Otherwise they had better match
PRBool filesAreEqual = PR_FALSE;
nsresult rv = sourceFile->Equals(targetFile, &filesAreEqual);
return NS_SUCCEEDED(rv) && filesAreEqual;
}
// Special handling for mailnews schemes
if (targetScheme.EqualsLiteral("imap") ||
targetScheme.EqualsLiteral("mailbox") ||
targetScheme.EqualsLiteral("news"))
{
// Each message is a distinct trust domain; use the
// whole spec for comparison
nsCAutoString targetSpec;
nsCAutoString sourceSpec;
return ( NS_SUCCEEDED( targetBaseURI->GetSpec(targetSpec) ) &&
NS_SUCCEEDED( sourceBaseURI->GetSpec(sourceSpec) ) &&
targetSpec.Equals(sourceSpec) );
}
// Compare hosts
nsCAutoString targetHost;
nsCAutoString sourceHost;
if (NS_FAILED( targetBaseURI->GetAsciiHost(targetHost) ) ||
NS_FAILED( sourceBaseURI->GetAsciiHost(sourceHost) ))
{
return PR_FALSE;
}
#ifdef MOZILLA_INTERNAL_API
if (!targetHost.Equals(sourceHost, nsCaseInsensitiveCStringComparator() ))
#else
if (!targetHost.Equals(sourceHost, CaseInsensitiveCompare))
#endif
{
return PR_FALSE;
}
// Compare ports
PRInt32 targetPort;
nsresult rv = targetBaseURI->GetPort(&targetPort);
PRInt32 sourcePort;
if (NS_SUCCEEDED(rv))
rv = sourceBaseURI->GetPort(&sourcePort);
PRBool result = NS_SUCCEEDED(rv) && targetPort == sourcePort;
// If the port comparison failed, see if either URL has a
// port of -1. If so, replace -1 with the default port
// for that scheme.
if (NS_SUCCEEDED(rv) && !result &&
(sourcePort == -1 || targetPort == -1))
{
PRInt32 defaultPort = NS_GetDefaultPort(targetScheme.get());
if (defaultPort == -1)
return PR_FALSE; // No default port for this scheme
if (sourcePort == -1)
sourcePort = defaultPort;
else if (targetPort == -1)
targetPort = defaultPort;
result = targetPort == sourcePort;
}
return result;
}
#endif // !nsNetUtil_h__

View File

@ -44,6 +44,8 @@
#include "nsNetUtil.h"
#include "nsAutoPtr.h"
#include "nsEscape.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsCRT.h"
@ -692,6 +694,20 @@ nsOfflineCacheDevice::~nsOfflineCacheDevice()
Shutdown();
}
/* static */
PRBool
nsOfflineCacheDevice::GetStrictFileOriginPolicy()
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
PRBool retval;
if (prefs && NS_SUCCEEDED(prefs->GetBoolPref("security.fileuri.strict_origin_policy", &retval)))
return retval;
// As default value use true (be more strict)
return PR_TRUE;
}
PRUint32
nsOfflineCacheDevice::CacheSize()
{
@ -1745,13 +1761,40 @@ nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key,
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> keyURI;
rv = NS_NewURI(getter_AddRefs(keyURI), key);
NS_ENSURE_SUCCESS(rv, rv);
while (hasRows) {
nsCString clientID;
rv = statement->GetUTF8String(0, clientID);
PRInt32 itemType;
rv = statement->GetInt32(1, &itemType);
NS_ENSURE_SUCCESS(rv, rv);
if (mActiveCaches.Contains(clientID))
return GetApplicationCache(clientID, out);
if (!(itemType & nsIApplicationCache::ITEM_FOREIGN)) {
nsCAutoString clientID;
rv = statement->GetUTF8String(0, clientID);
NS_ENSURE_SUCCESS(rv, rv);
if (mActiveCaches.Contains(clientID)) {
nsCAutoString groupID;
rv = GetGroupForCache(clientID, groupID);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> groupURI;
rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
if (NS_SUCCEEDED(rv)) {
// When we are choosing an initial cache to load the top
// level document from, the URL of that document must have
// the same origin as the manifest, according to the spec.
// The following check is here because explicit, fallback
// and dynamic entries might have origin different from the
// manifest origin. XXX: dynamic shouldn't?
if (NS_SecurityCompareURIs(keyURI, groupURI,
GetStrictFileOriginPolicy()))
return GetApplicationCache(clientID, out);
}
}
}
rv = statement->ExecuteStep(&hasRows);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -184,6 +184,8 @@ private:
nsIWeakReference *weakRef,
void *ctx);
static PRBool GetStrictFileOriginPolicy();
PRBool Initialized() { return mDB != nsnull; }
nsresult InitActiveCaches();

View File

@ -115,7 +115,7 @@ interface nsIOfflineCacheUpdateObserver : nsISupports {
* load its items one by one, sending itemCompleted() to any registered
* observers.
*/
[scriptable, uuid(4b206247-82ee-46cf-a8b7-f7284e753bc2)]
[scriptable, uuid(877261bb-b952-4d27-847e-859bdd47c0ec)]
interface nsIOfflineCacheUpdate : nsISupports {
/**
* Fetch the status of the running update. This will return a value
@ -154,21 +154,30 @@ interface nsIOfflineCacheUpdate : nsISupports {
/**
* Initialize the update.
*
* @param aPartialUpdate
* TRUE if the update should just update the URIs given to it,
* FALSE if all URLs for the owner domain should be added.
* @param aManifestURI
* The manifest URI to be checked, or for partial updates the
* manifest that should own resources that are added.
* The manifest URI to be checked.
* @param aDocumentURI
* The page that is requesting the update.
*/
void init(in boolean aPartialUpdate,
in nsIURI aManifestURI,
in nsIURI aDocumentURI);
void init(in nsIURI aManifestURI, in nsIURI aDocumentURI);
/**
* Add a URI to the offline cache as part of the update.
* Initialize the update for partial processing.
*
* @param aManifestURI
* The manifest URI of the related cache.
* @param aClientID
* Client ID of the cache to store resource to. This ClientID
* must be ID of cache in the cache group identified by
* the manifest URI passed in the first parameter.
* @param aDocumentURI
* The page that is requesting the update. May be null
* when this information is unknown.
*/
void initPartial(in nsIURI aManifestURI, in ACString aClientID, in nsIURI aDocumentURI);
/**
* Add a dynamic URI to the offline cache as part of the update.
*
* @param aURI
* The URI to add.

View File

@ -46,6 +46,7 @@
#include "nsICacheService.h"
#include "nsICacheSession.h"
#include "nsICachingChannel.h"
#include "nsIDocumentLoader.h"
#include "nsIDOMWindow.h"
#include "nsIDOMOfflineResourceList.h"
#include "nsIObserverService.h"
@ -57,6 +58,7 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsStringEnumerator.h"
#include "nsThreadUtils.h"
#include "prlog.h"
@ -577,6 +579,7 @@ nsOfflineManifestItem::HandleManifestLine(const nsCString::const_iterator &aBegi
// this should have been dealt with earlier
return NS_ERROR_FAILURE;
}
case PARSE_CACHE_ENTRIES: {
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), line, nsnull, mURI);
@ -810,8 +813,7 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey)
}
nsresult
nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
nsIURI *aManifestURI,
nsOfflineCacheUpdate::Init(nsIURI *aManifestURI,
nsIURI *aDocumentURI)
{
nsresult rv;
@ -822,9 +824,9 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
if (!service)
return NS_ERROR_FAILURE;
LOG(("nsOfflineCacheUpdate::Init [%p, %d]", this, aPartialUpdate));
LOG(("nsOfflineCacheUpdate::Init [%p]", this));
mPartialUpdate = aPartialUpdate;
mPartialUpdate = PR_FALSE;
// Only http and https applications are supported.
PRBool match;
@ -858,20 +860,66 @@ nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
getter_AddRefs(mPreviousApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
// Partial updates to existing application caches don't need a new cache.
if (aPartialUpdate && mPreviousApplicationCache) {
mApplicationCache = mPreviousApplicationCache;
} else {
rv = cacheService->CreateApplicationCache(manifestSpec,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
}
rv = cacheService->CreateApplicationCache(manifestSpec,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
rv = mApplicationCache->GetClientID(mClientID);
NS_ENSURE_SUCCESS(rv, rv);
mState = STATE_INITIALIZED;
return NS_OK;
}
nsresult
nsOfflineCacheUpdate::InitPartial(nsIURI *aManifestURI,
const nsACString& clientID,
nsIURI *aDocumentURI)
{
nsresult rv;
// Make sure the service has been initialized
nsOfflineCacheUpdateService* service =
nsOfflineCacheUpdateService::EnsureService();
if (!service)
return NS_ERROR_FAILURE;
LOG(("nsOfflineCacheUpdate::InitPartial [%p]", this));
mPartialUpdate = PR_TRUE;
mClientID = clientID;
mDocumentURI = aDocumentURI;
mManifestURI = aManifestURI;
rv = mManifestURI->GetAsciiHost(mUpdateDomain);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIApplicationCacheService> cacheService =
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = cacheService->GetApplicationCache(mClientID,
getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
if (!mApplicationCache) {
nsCAutoString manifestSpec;
rv = GetCacheKey(mManifestURI, manifestSpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = cacheService->CreateApplicationCache
(manifestSpec, getter_AddRefs(mApplicationCache));
NS_ENSURE_SUCCESS(rv, rv);
}
nsCAutoString groupID;
rv = mApplicationCache->GetGroupID(groupID);
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewURI(getter_AddRefs(mManifestURI), groupID);
NS_ENSURE_SUCCESS(rv, rv);
mState = STATE_INITIALIZED;
return NS_OK;
}
@ -1610,6 +1658,7 @@ nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI,
LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
this, aManifestURI, aDocumentURI, aDocument));
// Proceed with cache update
PendingUpdate *update = new PendingUpdate();
update->mManifestURI = aManifestURI;
update->mDocumentURI = aDocumentURI;
@ -1737,7 +1786,7 @@ nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI,
if (!update)
return NS_ERROR_OUT_OF_MEMORY;
rv = update->Init(PR_FALSE, aManifestURI, aDocumentURI);
rv = update->Init(aManifestURI, aDocumentURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = update->Schedule();