diff --git a/dom/public/idl/base/nsIDOMClientInformation.idl b/dom/public/idl/base/nsIDOMClientInformation.idl index 33f57ec52b7..babc35f1af1 100755 --- a/dom/public/idl/base/nsIDOMClientInformation.idl +++ b/dom/public/idl/base/nsIDOMClientInformation.idl @@ -39,7 +39,7 @@ interface nsIDOMOfflineResourceList; -[scriptable, uuid(609439fa-63e4-4f71-9512-904867f154e7)] +[scriptable, uuid(21c6561e-4778-47ed-96d0-eadc46d6a3ec)] interface nsIDOMClientInformation : nsISupports { /** @@ -51,6 +51,8 @@ interface nsIDOMClientInformation : nsISupports void registerContentHandler(in DOMString mimeType, in DOMString uri, in DOMString title); void registerProtocolHandler(in DOMString protocol, in DOMString uri, in DOMString title); + boolean isLocallyAvailable(in DOMString uri, in boolean whenOffline); + readonly attribute nsIDOMOfflineResourceList offlineResources; }; diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 170c97362c1..fffd5994a36 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -68,6 +68,7 @@ #include "nsStyleCoord.h" #include "nsMimeTypeArray.h" #include "nsNetUtil.h" +#include "nsICachingChannel.h" #include "nsPluginArray.h" #include "nsIPluginHost.h" #ifdef OJI @@ -8503,6 +8504,77 @@ nsNavigator::RegisterProtocolHandler(const nsAString& aProtocol, return NS_OK; } + +NS_IMETHODIMP +nsNavigator::IsLocallyAvailable(const nsAString &aURI, + PRBool aWhenOffline, + PRBool *aIsAvailable) +{ + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI); + NS_ENSURE_SUCCESS(rv, rv); + + // This method of checking the cache will only work for http/https urls + PRBool match; + rv = uri->SchemeIs("http", &match); + NS_ENSURE_SUCCESS(rv, rv); + if (!match) { + rv = uri->SchemeIs("https", &match); + NS_ENSURE_SUCCESS(rv, rv); + if (!match) return NS_ERROR_DOM_BAD_URI; + } + + // Same origin check + nsCOMPtr stack = do_GetService(sJSStackContractID); + NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE); + + JSContext *cx = nsnull; + rv = stack->Peek(&cx); + NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); + + rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx, uri); + NS_ENSURE_SUCCESS(rv, rv); + + // these load flags cause an error to be thrown if there is no + // valid cache entry, and skip the load if there is. + // if the cache is busy, assume that it is not yet available rather + // than waiting for it to become available. + PRUint32 loadFlags = nsIChannel::INHIBIT_CACHING | + nsIChannel::LOAD_NO_NETWORK_IO | + nsICachingChannel::LOAD_ONLY_IF_MODIFIED | + nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY; + + if (aWhenOffline) { + loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE | + nsICachingChannel::LOAD_ONLY_FROM_CACHE; + } + + nsCOMPtr channel; + rv = NS_NewChannel(getter_AddRefs(channel), uri, + nsnull, nsnull, nsnull, loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stream; + rv = channel->Open(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + stream->Close(); + + nsresult status; + rv = channel->GetStatus(&status); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_SUCCEEDED(status)) { + nsCOMPtr httpChannel = do_QueryInterface(channel); + rv = httpChannel->GetRequestSucceeded(aIsAvailable); + NS_ENSURE_SUCCESS(rv, rv); + } else { + *aIsAvailable = PR_FALSE; + } + + return NS_OK; +} + NS_IMETHODIMP nsNavigator::GetOfflineResources(nsIDOMOfflineResourceList **aList) { diff --git a/dom/tests/mochitest/ajax/offline/Makefile.in b/dom/tests/mochitest/ajax/offline/Makefile.in index 73a5ad5ebcb..b0b597850bf 100644 --- a/dom/tests/mochitest/ajax/offline/Makefile.in +++ b/dom/tests/mochitest/ajax/offline/Makefile.in @@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = \ test_offlineResources.html \ + test_isLocallyAvailable.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/dom/tests/mochitest/ajax/offline/test_isLocallyAvailable.html b/dom/tests/mochitest/ajax/offline/test_isLocallyAvailable.html new file mode 100644 index 00000000000..357943c7001 --- /dev/null +++ b/dom/tests/mochitest/ajax/offline/test_isLocallyAvailable.html @@ -0,0 +1,94 @@ + + +navigator.isLocallyAvailable Test + + + + + + + + + + + + + + + diff --git a/netwerk/base/public/nsICachingChannel.idl b/netwerk/base/public/nsICachingChannel.idl index a1c2ba0d8fd..5971ff8b4ab 100644 --- a/netwerk/base/public/nsICachingChannel.idl +++ b/netwerk/base/public/nsICachingChannel.idl @@ -50,7 +50,7 @@ interface nsIFile; * 3) Support for uniquely identifying cached data in cases when the URL * is insufficient (e.g., HTTP form submission). */ -[scriptable, uuid(855bcc4d-0987-45b6-b138-3bf0bf407703)] +[scriptable, uuid(afafb719-bdf5-49c8-a4a9-db39ec331c9b)] interface nsICachingChannel : nsISupports { /** @@ -123,6 +123,12 @@ interface nsICachingChannel : nsISupports * Caching channel specific load flags: */ + /** + * This load flag causes the offline cache to be checked when fetching + * a request. It will be set automatically if the browser is offline. + */ + const unsigned long LOAD_CHECK_OFFLINE_CACHE = 1 << 27; + /** * This load flag causes the local cache to be skipped when fetching a * request. Unlike LOAD_BYPASS_CACHE, it does not force an end-to-end load @@ -139,7 +145,8 @@ interface nsICachingChannel : nsISupports /** * This load flag inhibits fetching from the net if the data in the cache * has been evicted. An error of NS_ERROR_DOCUMENT_NOT_CACHED will be sent - * to the listener's onStopRequest in this case. + * to the listener's onStopRequest in this case. This flag is set + * automatically when the application is offline. */ const unsigned long LOAD_ONLY_FROM_CACHE = 1 << 30; diff --git a/netwerk/base/public/nsIChannel.idl b/netwerk/base/public/nsIChannel.idl index 3a4e5939525..9698efe285f 100644 --- a/netwerk/base/public/nsIChannel.idl +++ b/netwerk/base/public/nsIChannel.idl @@ -259,4 +259,11 @@ interface nsIChannel : nsIRequest * should only do so with good reason. */ const unsigned long LOAD_CALL_CONTENT_SNIFFERS = 1 << 21; + + /** + * This load flag inhibits fetching from the net. An error of + * NS_ERROR_NEEDS_NETWORK will be sent to the listener's onStopRequest + * if network IO is necessary to complete the request. + */ + const unsigned long LOAD_NO_NETWORK_IO = 1 << 22; }; diff --git a/netwerk/base/public/nsNetError.h b/netwerk/base/public/nsNetError.h index 1a4c0670596..ec0bb9c122a 100644 --- a/netwerk/base/public/nsNetError.h +++ b/netwerk/base/public/nsNetError.h @@ -208,6 +208,13 @@ #define NS_ERROR_NET_INTERRUPT \ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 71) +/** + * The requested action would require network IO, but + * nsIChannel::LOAD_NO_NETWORK_IO was specified. + */ +#define NS_ERROR_NEEDS_NETWORK \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 72) + // XXX really need to better rationalize these error codes. are consumers of // necko really expected to know how to discern the meaning of these?? diff --git a/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp b/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp index 1d85df091fb..494a563905c 100644 --- a/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp +++ b/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp @@ -1662,6 +1662,13 @@ nsFtpState::Connect() mState = FTP_COMMAND_CONNECT; mNextState = FTP_S_USER; + if (mChannel->HasLoadFlag(nsIChannel::LOAD_NO_NETWORK_IO)){ + mInternalError = NS_ERROR_NEEDS_NETWORK; + mState = FTP_ERROR; + CloseWithStatus(mInternalError); + return; + } + nsresult rv = Process(); // check for errors. diff --git a/netwerk/protocol/gopher/src/nsGopherChannel.cpp b/netwerk/protocol/gopher/src/nsGopherChannel.cpp index 79926d24281..c9c3958fcd8 100644 --- a/netwerk/protocol/gopher/src/nsGopherChannel.cpp +++ b/netwerk/protocol/gopher/src/nsGopherChannel.cpp @@ -187,7 +187,10 @@ nsresult nsGopherContentStream::OpenSocket(nsIEventTarget *target) { // This function is called to get things started. - // + + if (mChannel->HasLoadFlag(nsIChannel::LOAD_NO_NETWORK_IO)) + return NS_ERROR_NEEDS_NETWORK; + // We begin by opening a socket to the specified host and wait for the // socket to become writable. diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index 2d33467c825..6c3f325ba43 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -272,7 +272,7 @@ nsHttpChannel::Connect(PRBool firstTime) // are we offline? PRBool offline = gIOService->IsOffline(); if (offline) - mLoadFlags |= LOAD_ONLY_FROM_CACHE; + mLoadFlags |= (LOAD_ONLY_FROM_CACHE | LOAD_CHECK_OFFLINE_CACHE); else if (PL_strcmp(mConnectionInfo->ProxyType(), "unknown") == 0) return ResolveProxy(); // Lazily resolve proxy info @@ -330,6 +330,10 @@ nsHttpChannel::Connect(PRBool firstTime) // check to see if authorization headers should be included AddAuthorizationHeaders(); + if (mLoadFlags & LOAD_NO_NETWORK_IO) { + return NS_ERROR_NEEDS_NETWORK; + } + // hit the net... rv = SetupTransaction(); if (NS_FAILED(rv)) return rv; @@ -1307,9 +1311,10 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) // Set the desired cache access mode accordingly... nsCacheAccessMode accessRequested; - if (offline || (mLoadFlags & INHIBIT_CACHING)) { + if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | INHIBIT_CACHING)) { // If we have been asked to bypass the cache and not write to the - // cache, then don't use the cache at all. + // cache, then don't use the cache at all. Unless we're actually + // offline, which takes precedence over BYPASS_LOCAL_CACHE. if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) return NS_ERROR_NOT_AVAILABLE; accessRequested = nsICache::ACCESS_READ; @@ -1330,7 +1335,7 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE, getter_AddRefs(mCacheEntry)); - if (offline && + if ((mLoadFlags & LOAD_CHECK_OFFLINE_CACHE) && !(NS_SUCCEEDED(rv) || rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)) { // couldn't find it in the main cache, check the offline cache @@ -1545,10 +1550,12 @@ nsHttpChannel::CheckCache() NS_ENSURE_SUCCESS(rv, rv); buf.Adopt(0); - // If we were only granted read access, then assume the entry is valid. - // unless it is INHBIT_CACHING - if (mCacheAccess == nsICache::ACCESS_READ && - !(mLoadFlags & INHIBIT_CACHING)) { + // Don't bother to validate LOAD_ONLY_FROM_CACHE items. + // Don't bother to validate items that are read-only, + // unless they are read-only because of INHIBIT_CACHING. + if (mLoadFlags & LOAD_ONLY_FROM_CACHE || + (mCacheAccess == nsICache::ACCESS_READ && + !(mLoadFlags & INHIBIT_CACHING))) { mCachedContentIsValid = PR_TRUE; return NS_OK; }