implement navigator.isLocallyAvailable. b=373231, r=biesi, sr=jst

This commit is contained in:
dcamp@mozilla.com 2007-07-08 15:15:51 -07:00
parent 6af62a1de7
commit 95ba46800c
10 changed files with 219 additions and 12 deletions

View File

@ -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;
};

View File

@ -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<nsIURI> 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<nsIJSContextStack> 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<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri,
nsnull, nsnull, nsnull, loadFlags);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> 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<nsIHttpChannel> 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)
{

View File

@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_offlineResources.html \
test_isLocallyAvailable.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -0,0 +1,94 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>navigator.isLocallyAvailable Test</title>
<link rel="offline-resource" href="http://localhost:8888/tests/SimpleTest/SimpleTest.js">
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript">
function run_test()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
// check invalid urls
try {
navigator.isLocallyAvailable("http://different.origin.com", false);
ok(false, "can't check from a different origin");
} catch(e) {
ok(true, "can't check from a different origin");
}
try {
navigator.isLocallyAvailable("ftp://localhost/blah/blah", false);
ok(false, "urls must be http or https");
} catch(e) {
ok(true, "urls must be http or https");
}
// check an URL that should definitely not be there
ok(navigator.isLocallyAvailable("http://localhost:8888/blah/blah/blah",
false) == false,
"unknown item shouldn't be available online");
ok(navigator.isLocallyAvailable("http://localhost:8888/blah/blah/blah",
true) == false,
"unknown item shouldn't be available offline");
// check a URL that should be available on and offline
var url = "http://localhost:8888/tests/SimpleTest/SimpleTest.js";
ok(navigator.isLocallyAvailable(url, false) == true,
url + " should be available online");
ok(navigator.isLocallyAvailable(url, true) == true,
url + " should be available offline");
// Pull it out of the disk cache
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
var session = cacheService.createSession
("HTTP",
Components.interfaces.nsICache.STORE_ON_DISK,
Components.interfaces.nsICache.STREAM_BASED);
var entry = session.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_WRITE, false);
entry.doom();
entry.close();
// Now it should be available offline but not online
ok(navigator.isLocallyAvailable(url, false) == false,
url + " should not be available online");
ok(navigator.isLocallyAvailable(url, true) == true,
url + " should be available offline");
// Clear the offline cache/ownership on the way out
var cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
.getService(Components.interfaces.nsICacheService);
var cacheSession = cacheService.createSession("HTTP-offline",
Components.interfaces.nsICache.STORE_OFFLINE,
true)
.QueryInterface(Components.interfaces.nsIOfflineCacheSession);
cacheSession.setOwnedKeys(window.location.host,
window.location.protocol + "//" + window.location.host + window.location.pathname,
0, []);
cacheSession.setOwnedKeys(window.location.host, "", 0, []);
cacheSession.evictUnownedEntries();
cacheService.evictEntries(Components.interfaces.nsICache.STORE_OFFLINE);
SimpleTest.finish();
}
// Give the offline resources some time to load.
setTimeout("run_test()", 1000);
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
</body>
</html>

View File

@ -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;

View File

@ -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;
};

View File

@ -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??

View File

@ -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.

View File

@ -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.

View File

@ -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;
}