Bug 1187159 - Pass principal and flags from the requesting channel to the packaged app channel r=honzab

This commit is contained in:
Valentin Gosu 2015-08-08 07:11:56 +02:00
parent bc5665176b
commit 1e73e39a9d
5 changed files with 69 additions and 52 deletions

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
interface nsIURI;
interface nsIPrincipal;
interface nsILoadContextInfo;
interface nsICacheEntryOpenCallback;
@ -16,27 +16,35 @@ interface nsICacheEntryOpenCallback;
/**
* nsIPackagedAppService
*/
[scriptable, builtinclass, uuid(77f9a34d-d082-43f1-9f83-e852d0173cd5)]
[scriptable, builtinclass, uuid(f35e5229-d08a-46eb-a574-2db4e22aee98)]
interface nsIPackagedAppService : nsISupports
{
/**
* @aURI is a URL to a packaged resource
* - format: package_url + PACKAGED_APP_TOKEN + resource_path
* - example: http://test.com/path/to/package!//resource.html
* @aCallback is an object implementing nsICacheEntryOpenCallback
* - this is the target of the async result of the operation
* - aCallback->OnCacheEntryCheck() is called to verify the entry is valid
* - aCallback->OnCacheEntryAvailable() is called with a pointer to the
* the cached entry, if one exists, or an error code otherwise
* - aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
* is called
* @aInfo is an object used to determine the cache jar this resource goes in.
* - usually created by calling GetLoadContextInfo(requestingChannel)
* @param aPrincipal
* the principal associated to the URL of a packaged resource
* URL format: package_url + PACKAGED_APP_TOKEN + resource_path
* example: http://test.com/path/to/package!//resource.html
* @param aFlags
* the load flags used for downloading the package
* @param aCallback
* an object implementing nsICacheEntryOpenCallback
* this is the target of the async result of the operation
* aCallback->OnCacheEntryCheck() is called to verify the entry is valid
* aCallback->OnCacheEntryAvailable() is called with a pointer to the
* the cached entry, if one exists, or an error code otherwise
* aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
* is called
* @param aInfo
* an object used to determine the cache jar this resource goes in.
* usually created by calling GetLoadContextInfo(requestingChannel)
*
* Calling this method will either download the package containing the given
* resource URI, store it in the cache and pass the cache entry to aCallback,
* or if that resource has already been downloaded it will be served from
* the cache.
*/
void requestURI(in nsIURI aURI, in nsILoadContextInfo aInfo, in nsICacheEntryOpenCallback aCallback);
void getResource(in nsIPrincipal aPrincipal,
in uint32_t aFlags,
in nsILoadContextInfo aInfo,
in nsICacheEntryOpenCallback aCallback);
};

View File

@ -605,23 +605,30 @@ PackagedAppService::GetPackageURI(nsIURI *aURI, nsIURI **aPackageURI)
}
NS_IMETHODIMP
PackagedAppService::RequestURI(nsIURI *aURI,
nsILoadContextInfo *aInfo,
nsICacheEntryOpenCallback *aCallback)
PackagedAppService::GetResource(nsIPrincipal *aPrincipal,
uint32_t aLoadFlags,
nsILoadContextInfo *aInfo,
nsICacheEntryOpenCallback *aCallback)
{
// Check arguments are not null
if (!aURI || !aCallback || !aInfo) {
if (!aPrincipal || !aCallback || !aInfo) {
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
LogURI("PackagedAppService::RequestURI", this, aURI, aInfo);
nsCOMPtr<nsIURI> uri;
rv = aPrincipal->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
LogURI("PackagedAppService::GetResource", this, uri, aInfo);
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
nsCOMPtr<nsIURI> packageURI;
rv = GetPackageURI(aURI, getter_AddRefs(packageURI));
rv = GetPackageURI(uri, getter_AddRefs(packageURI));
if (NS_FAILED(rv)) {
return rv;
}
@ -643,22 +650,15 @@ PackagedAppService::RequestURI(nsIURI *aURI,
// downloaded, we will add the callback to the package's queue, and it will
// be called once the file is processed and saved in the cache.
downloader->AddCallback(aURI, aCallback);
downloader->AddCallback(uri, aCallback);
return NS_OK;
}
// We need to set this flag, because the package metadata
// needs to have a separate entry for anonymous channels.
uint32_t extra_flags = 0;
if (aInfo->IsAnonymous()) {
extra_flags = nsIRequest::LOAD_ANONYMOUS;
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(
getter_AddRefs(channel), packageURI, nsContentUtils::GetSystemPrincipal(),
getter_AddRefs(channel), packageURI, aPrincipal,
nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr,
nsIRequest::LOAD_NORMAL | extra_flags);
aLoadFlags);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -678,7 +678,7 @@ PackagedAppService::RequestURI(nsIURI *aURI,
return rv;
}
downloader->AddCallback(aURI, aCallback);
downloader->AddCallback(uri, aCallback);
nsCOMPtr<nsIStreamConverterService> streamconv =
do_GetService("@mozilla.org/streamConverters;1", &rv);

View File

@ -23,10 +23,7 @@ class nsHttpResponseHead;
// or downloads the package.
// The package format is defined at:
// https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format
// Downloading the package is triggered by calling requestURI(aURI, aInfo, aCallback)
// aURI is the subresource uri - http://domain.com/path/package!//resource.html
// aInfo is a nsILoadContextInfo used to pick the cache jar the resource goes into
// aCallback is the target of the async call to requestURI
// Downloading the package is triggered by calling getResource()
class PackagedAppService final
: public nsIPackagedAppService
{

View File

@ -5197,6 +5197,10 @@ nsHttpChannel::BeginConnect()
// If this is a packaged app resource, the content will be fetched
// by the packaged app service into the cache, and the cache entry will
// be passed to OnCacheEntryAvailable.
// Pass the original load flags to the packaged app request.
uint32_t loadFlags = mLoadFlags;
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
mLoadFlags |= LOAD_FROM_CACHE;
mLoadFlags &= ~VALIDATE_ALWAYS;
@ -5207,7 +5211,8 @@ nsHttpChannel::BeginConnect()
return rv;
}
rv = pas->RequestURI(mURI, GetLoadContextInfo(this), this);
nsCOMPtr<nsIPrincipal> principal = GetURIPrincipal();
rv = pas->GetResource(principal, loadFlags, GetLoadContextInfo(this), this);
if (NS_FAILED(rv)) {
AsyncAbort(rv);
}

View File

@ -7,14 +7,14 @@
// ----------------------------------------------------------------------------
//
// test_bad_args
// - checks that calls to nsIPackagedAppService::requestURI do not accept a null argument
// - checks that calls to nsIPackagedAppService::GetResource do not accept a null argument
// test_callback_gets_called
// - checks the regular use case -> requesting a resource should asynchronously return an entry
// test_same_content
// - makes another request for the same file, and checks that the same content is returned
// test_request_number
// - this test does not make a request, but checks that the package has only
// been requested once. The entry returned by the call to requestURI in
// been requested once. The entry returned by the call to getResource in
// test_same_content should be returned from the cache.
//
// test_package_does_not_exist
@ -59,6 +59,13 @@ function packagedAppContentHandler(metadata, response)
response.bodyOutputStream.write(body, body.length);
}
function getPrincipal(url) {
let uri = createURI(url);
return Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager)
.getNoAppCodebasePrincipal(uri);
}
// The package content
// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
var testData = {
@ -92,7 +99,7 @@ XPCOMUtils.defineLazyGetter(this, "uri", function() {
var httpserver = null;
// The packaged app service initialized in run_test
var paservice = null;
// This variable is set before requestURI is called. The listener uses this variable
// This variable is set before getResource is called. The listener uses this variable
// to check the correct resource path for the returned entry
var packagePath = null;
@ -170,10 +177,10 @@ var cacheListener = new packagedResourceListener(testData.content[0].data);
// These calls should fail, since one of the arguments is invalid or null
function test_bad_args() {
Assert.throws(() => { paservice.requestURI(createURI("http://test.com"), LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), LoadContextInfo.default, null); }, "should have a callback");
Assert.throws(() => { paservice.requestURI(null, LoadContextInfo.default, cacheListener); }, "should have a URI");
Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com"), 0, LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com/package!//test"), 0, LoadContextInfo.default, null); }, "should have a callback");
Assert.throws(() => { paservice.getResource(null, 0, LoadContextInfo.default, cacheListener); }, "should have a URI");
Assert.throws(() => { paservice.getResource(getPrincipal("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
run_next_test();
}
@ -182,13 +189,13 @@ function test_bad_args() {
// This tests that the callback gets called, and the cacheListener gets the proper content.
function test_callback_gets_called() {
packagePath = "/package";
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
}
// Tests that requesting the same resource returns the same content
function test_same_content() {
packagePath = "/package";
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
}
// Check the content handler has been called the expected number of times.
@ -200,7 +207,7 @@ function test_request_number() {
// This tests that new content is returned if the package has been updated
function test_updated_package() {
packagePath = "/package";
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default,
paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default,
new packagedResourceListener(testData.content[0].data.replace(/\.\.\./g, 'xxx')));
}
@ -224,13 +231,13 @@ var listener404 = {
// Tests that an error is returned for a non existing package
function test_package_does_not_exist() {
packagePath = "/package_non_existent";
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, listener404);
paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, listener404);
}
// Tests that an error is returned for a non existing resource in a package
function test_file_does_not_exist() {
packagePath = "/package"; // This package exists
paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
paservice.getResource(getPrincipal(uri + packagePath + "!//file_non_existent.html"), 0, LoadContextInfo.default, listener404);
}
// ----------------------------------------------------------------------------
@ -271,13 +278,13 @@ function packagedAppBadContentHandler(metadata, response)
// Checks that the resource with the proper headers inside the bad package is still returned
function test_bad_package() {
packagePath = "/badPackage";
paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
paservice.getResource(getPrincipal(uri + packagePath + "!//index.html"), 0, LoadContextInfo.default, cacheListener);
}
// Checks that the request for a non-existent resource doesn't hang for a bad package
function test_bad_package_404() {
packagePath = "/badPackage";
paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
paservice.getResource(getPrincipal(uri + packagePath + "!//file_non_existent.html"), 0, LoadContextInfo.default, listener404);
}
// ----------------------------------------------------------------------------