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-10 11:21:57 +02:00
parent 375ab5a8b9
commit 74c5b794d1
5 changed files with 70 additions and 52 deletions

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl" #include "nsISupports.idl"
interface nsIURI; interface nsIPrincipal;
interface nsILoadContextInfo; interface nsILoadContextInfo;
interface nsICacheEntryOpenCallback; interface nsICacheEntryOpenCallback;
@ -16,27 +16,35 @@ interface nsICacheEntryOpenCallback;
/** /**
* nsIPackagedAppService * nsIPackagedAppService
*/ */
[scriptable, builtinclass, uuid(77f9a34d-d082-43f1-9f83-e852d0173cd5)] [scriptable, builtinclass, uuid(f35e5229-d08a-46eb-a574-2db4e22aee98)]
interface nsIPackagedAppService : nsISupports interface nsIPackagedAppService : nsISupports
{ {
/** /**
* @aURI is a URL to a packaged resource * @param aPrincipal
* - format: package_url + PACKAGED_APP_TOKEN + resource_path * the principal associated to the URL of a packaged resource
* - example: http://test.com/path/to/package!//resource.html * URL format: package_url + PACKAGED_APP_TOKEN + resource_path
* @aCallback is an object implementing nsICacheEntryOpenCallback * example: http://test.com/path/to/package!//resource.html
* - this is the target of the async result of the operation * @param aFlags
* - aCallback->OnCacheEntryCheck() is called to verify the entry is valid * the load flags used for downloading the package
* - aCallback->OnCacheEntryAvailable() is called with a pointer to the * @param aCallback
* the cached entry, if one exists, or an error code otherwise * an object implementing nsICacheEntryOpenCallback
* - aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable * this is the target of the async result of the operation
* is called * aCallback->OnCacheEntryCheck() is called to verify the entry is valid
* @aInfo is an object used to determine the cache jar this resource goes in. * aCallback->OnCacheEntryAvailable() is called with a pointer to the
* - usually created by calling GetLoadContextInfo(requestingChannel) * 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 * 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, * 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 * or if that resource has already been downloaded it will be served from
* the cache. * 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 NS_IMETHODIMP
PackagedAppService::RequestURI(nsIURI *aURI, PackagedAppService::GetResource(nsIPrincipal *aPrincipal,
nsILoadContextInfo *aInfo, uint32_t aLoadFlags,
nsICacheEntryOpenCallback *aCallback) nsILoadContextInfo *aInfo,
nsICacheEntryOpenCallback *aCallback)
{ {
// Check arguments are not null // Check arguments are not null
if (!aURI || !aCallback || !aInfo) { if (!aPrincipal || !aCallback || !aInfo) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
nsresult rv; 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"); MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
nsCOMPtr<nsIURI> packageURI; nsCOMPtr<nsIURI> packageURI;
rv = GetPackageURI(aURI, getter_AddRefs(packageURI)); rv = GetPackageURI(uri, getter_AddRefs(packageURI));
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return 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 // 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. // be called once the file is processed and saved in the cache.
downloader->AddCallback(aURI, aCallback); downloader->AddCallback(uri, aCallback);
return NS_OK; 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; nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel( rv = NS_NewChannel(
getter_AddRefs(channel), packageURI, nsContentUtils::GetSystemPrincipal(), getter_AddRefs(channel), packageURI, aPrincipal,
nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr,
nsIRequest::LOAD_NORMAL | extra_flags); aLoadFlags);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
@ -678,7 +678,7 @@ PackagedAppService::RequestURI(nsIURI *aURI,
return rv; return rv;
} }
downloader->AddCallback(aURI, aCallback); downloader->AddCallback(uri, aCallback);
nsCOMPtr<nsIStreamConverterService> streamconv = nsCOMPtr<nsIStreamConverterService> streamconv =
do_GetService("@mozilla.org/streamConverters;1", &rv); do_GetService("@mozilla.org/streamConverters;1", &rv);

View File

@ -23,10 +23,7 @@ class nsHttpResponseHead;
// or downloads the package. // or downloads the package.
// The package format is defined at: // The package format is defined at:
// https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format // https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format
// Downloading the package is triggered by calling requestURI(aURI, aInfo, aCallback) // Downloading the package is triggered by calling getResource()
// 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
class PackagedAppService final class PackagedAppService final
: public nsIPackagedAppService : public nsIPackagedAppService
{ {

View File

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

View File

@ -7,14 +7,14 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// //
// test_bad_args // 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 // test_callback_gets_called
// - checks the regular use case -> requesting a resource should asynchronously return an entry // - checks the regular use case -> requesting a resource should asynchronously return an entry
// test_same_content // test_same_content
// - makes another request for the same file, and checks that the same content is returned // - makes another request for the same file, and checks that the same content is returned
// test_request_number // test_request_number
// - this test does not make a request, but checks that the package has only // - 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_same_content should be returned from the cache.
// //
// test_package_does_not_exist // test_package_does_not_exist
@ -59,6 +59,13 @@ function packagedAppContentHandler(metadata, response)
response.bodyOutputStream.write(body, body.length); 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 // The package content
// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format // getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
var testData = { var testData = {
@ -92,7 +99,7 @@ XPCOMUtils.defineLazyGetter(this, "uri", function() {
var httpserver = null; var httpserver = null;
// The packaged app service initialized in run_test // The packaged app service initialized in run_test
var paservice = null; 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 // to check the correct resource path for the returned entry
var packagePath = null; 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 // These calls should fail, since one of the arguments is invalid or null
function test_bad_args() { 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.getResource(getPrincipal("http://test.com"), 0, 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.getResource(getPrincipal("http://test.com/package!//test"), 0, LoadContextInfo.default, null); }, "should have a callback");
Assert.throws(() => { paservice.requestURI(null, LoadContextInfo.default, cacheListener); }, "should have a URI"); Assert.throws(() => { paservice.getResource(null, 0, 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/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
run_next_test(); 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. // This tests that the callback gets called, and the cacheListener gets the proper content.
function test_callback_gets_called() { function test_callback_gets_called() {
packagePath = "/package"; 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 // Tests that requesting the same resource returns the same content
function test_same_content() { function test_same_content() {
packagePath = "/package"; 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. // 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 // This tests that new content is returned if the package has been updated
function test_updated_package() { function test_updated_package() {
packagePath = "/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'))); 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 // Tests that an error is returned for a non existing package
function test_package_does_not_exist() { function test_package_does_not_exist() {
packagePath = "/package_non_existent"; 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 // Tests that an error is returned for a non existing resource in a package
function test_file_does_not_exist() { function test_file_does_not_exist() {
packagePath = "/package"; // This package exists 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 // Checks that the resource with the proper headers inside the bad package is still returned
function test_bad_package() { function test_bad_package() {
packagePath = "/badPackage"; 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 // Checks that the request for a non-existent resource doesn't hang for a bad package
function test_bad_package_404() { function test_bad_package_404() {
packagePath = "/badPackage"; 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);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------