Bug 1186290 - Notify TabParent to switch process when loading a signed package. r=honzab, r=kanru.

This commit is contained in:
Henry Chang 2015-10-02 05:25:22 -07:00
parent a85371cccc
commit 82e53ac666
17 changed files with 473 additions and 17 deletions

View File

@ -108,6 +108,12 @@ using namespace mozilla::services;
using namespace mozilla::widget;
using namespace mozilla::jsipc;
#if DEUBG
#define LOG(args...) printf_stderr(args)
#else
#define LOG(...)
#endif
// The flags passed by the webProgress notifications are 16 bits shifted
// from the ones registered by webProgressListeners.
#define NOTIFY_FLAG_SHIFT 16
@ -437,6 +443,104 @@ TabParent::IsVisible()
return visible;
}
static void LogChannelRelevantInfo(nsIURI* aURI,
nsIPrincipal* aLoadingPrincipal,
nsIPrincipal* aChannelResultPrincipal,
nsContentPolicyType aContentPolicyType) {
nsCString loadingOrigin;
aLoadingPrincipal->GetOrigin(loadingOrigin);
nsCString uriString;
aURI->GetAsciiSpec(uriString);
LOG("Loading %s from origin %s (type: %d)\n", uriString.get(),
loadingOrigin.get(),
aContentPolicyType);
nsCString resultPrincipalOrigin;
aChannelResultPrincipal->GetOrigin(resultPrincipalOrigin);
LOG("Result principal origin: %s\n", resultPrincipalOrigin.get());
}
bool
TabParent::ShouldSwitchProcess(nsIChannel* aChannel)
{
// If we lack of any information which is required to decide the need of
// process switch, consider that we should switch process.
// Prepare the channel loading principal.
nsCOMPtr<nsILoadInfo> loadInfo;
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, true);
nsCOMPtr<nsIPrincipal> loadingPrincipal;
loadInfo->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal));
NS_ENSURE_TRUE(loadingPrincipal, true);
// Prepare the channel result principal.
nsCOMPtr<nsIPrincipal> resultPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelResultPrincipal(aChannel, getter_AddRefs(resultPrincipal));
// Log the debug info which is used to decide the need of proces switch.
nsCOMPtr<nsIURI> uri;
aChannel->GetURI(getter_AddRefs(uri));
LogChannelRelevantInfo(uri, loadingPrincipal, resultPrincipal,
loadInfo->GetContentPolicyType());
// Check if the signed package is loaded from the same origin.
bool sameOrigin = false;
loadingPrincipal->Equals(resultPrincipal, &sameOrigin);
if (sameOrigin) {
LOG("Loading singed package from the same origin. Don't switch process.\n");
return false;
}
// If this is not a top level document, there's no need to switch process.
if (nsIContentPolicy::TYPE_DOCUMENT != loadInfo->GetContentPolicyType()) {
LOG("Subresource of a document. No need to switch process.\n");
return false;
}
// If this is a brand new process created to load the signed package
// (triggered by previous OnStartSignedPackageRequest), the loading origin
// will be "moz-safe-about:blank". In that case, we don't need to switch process
// again. We compare with "moz-safe-about:blank" without appId/isBrowserElement/etc
// taken into account. That's why we use originNoSuffix.
nsCString loadingOriginNoSuffix;
loadingPrincipal->GetOriginNoSuffix(loadingOriginNoSuffix);
if (loadingOriginNoSuffix.EqualsLiteral("moz-safe-about:blank")) {
LOG("The content is already loaded by a brand new process.\n");
return false;
}
return true;
}
void
TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel)
{
if (!ShouldSwitchProcess(aChannel)) {
return;
}
nsCOMPtr<nsIURI> uri;
aChannel->GetURI(getter_AddRefs(uri));
aChannel->Cancel(NS_BINDING_FAILED);
nsCString uriString;
uri->GetAsciiSpec(uriString);
LOG("We decide to switch process. Call nsFrameLoader::SwitchProcessAndLoadURIs: %s\n",
uriString.get());
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
NS_ENSURE_TRUE_VOID(frameLoader);
nsresult rv = frameLoader->SwitchProcessAndLoadURI(uri);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to switch process.");
}
}
void
TabParent::DestroyInternal()
{

View File

@ -442,6 +442,10 @@ public:
int32_t& aDragAreaX, int32_t& aDragAreaY);
layout::RenderFrameParent* GetRenderFrame();
// Called by HttpChannelParent. The function may use a new process to
// reload the URI associated with the given channel.
void OnStartSignedPackageRequest(nsIChannel* aChannel);
protected:
bool ReceiveMessage(const nsString& aMessage,
bool aSync,
@ -482,6 +486,10 @@ protected:
void SetHasContentOpener(bool aHasContentOpener);
// Decide whether we have to use a new process to reload the URI associated
// with the given channel.
bool ShouldSwitchProcess(nsIChannel* aChannel);
ContentCacheInParent mContentCache;
nsIntRect mRect;

View File

@ -69,6 +69,7 @@ XPIDL_SOURCES += [
'nsINSSErrorsService.idl',
'nsINullChannel.idl',
'nsIPACGenerator.idl',
'nsIPackagedAppChannelListener.idl',
'nsIPackagedAppService.idl',
'nsIPackagedAppUtils.idl',
'nsIPackagedAppVerifier.idl',

View File

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
/**
* nsIPackagedAppChannelListener
*/
[scriptable, uuid(27caf7d0-3c0e-11e5-b970-0800200c9a66)]
interface nsIPackagedAppChannelListener : nsISupports
{
/**
* @param aPackageId
* the package identifier of the signed package that we are going
* to load. The identifier will be defined in the manifest of the
* package.
*
* This callback is to notify a signed package is about to load. Some
* component else will be in charge of responding to this fact properly.
* The procotol layer should have no idea what to do with this.
*
*/
void onStartSignedPackageRequest(in ACString aPackageId);
};

View File

@ -154,6 +154,7 @@ NS_IMPL_ISUPPORTS(HttpChannelParent,
nsIProgressEventSink,
nsIRequestObserver,
nsIStreamListener,
nsIPackagedAppChannelListener,
nsIParentChannel,
nsIAuthPromptProvider,
nsIParentRedirectingChannel,
@ -1036,6 +1037,19 @@ HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
return true;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIPackagedAppChannelListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParent::OnStartSignedPackageRequest(const nsACString& aPackageId)
{
if (mTabParent) {
mTabParent->OnStartSignedPackageRequest(mChannel);
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIRequestObserver
//-----------------------------------------------------------------------------

View File

@ -22,6 +22,7 @@
#include "mozilla/dom/ipc/IdType.h"
#include "nsINetworkInterceptController.h"
#include "nsIDeprecationWarner.h"
#include "nsIPackagedAppChannelListener.h"
class nsICacheEntry;
class nsIAssociatedContentSecurity;
@ -46,6 +47,7 @@ class HttpChannelParent final : public PHttpChannelParent
, public nsINetworkInterceptController
, public nsIDeprecationWarner
, public DisconnectableParent
, public nsIPackagedAppChannelListener
, public HttpChannelSecurityWarningReporter
{
virtual ~HttpChannelParent();
@ -54,6 +56,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPACKAGEDAPPCHANNELLISTENER
NS_DECL_NSIPARENTCHANNEL
NS_DECL_NSIPARENTREDIRECTINGCHANNEL
NS_DECL_NSIPROGRESSEVENTSINK

View File

@ -12,6 +12,7 @@
#include "mozilla/unused.h"
#include "nsIRedirectChannelRegistrar.h"
#include "nsIHttpEventSink.h"
#include "nsIPackagedAppChannelListener.h"
using mozilla::unused;
@ -38,6 +39,7 @@ NS_IMPL_ISUPPORTS(HttpChannelParentListener,
nsIStreamListener,
nsIRequestObserver,
nsIChannelEventSink,
nsIPackagedAppChannelListener,
nsIRedirectResultListener)
//-----------------------------------------------------------------------------
@ -97,6 +99,22 @@ HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest,
return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
}
//-----------------------------------------------------------------------------
// HttpChannelParentListener::nsIPackagedAppChannelListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpChannelParentListener::OnStartSignedPackageRequest(const nsACString& aPackageId)
{
nsCOMPtr<nsIPackagedAppChannelListener> listener = do_QueryInterface(mNextListener);
if (listener) {
listener->OnStartSignedPackageRequest(aPackageId);
} else {
NS_WARNING("mNextListener is not nsIPackagedAppChannelListener");
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// HttpChannelParentListener::nsIInterfaceRequestor
//-----------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@
#include "nsIInterfaceRequestor.h"
#include "nsIChannelEventSink.h"
#include "nsIRedirectResultListener.h"
#include "nsIPackagedAppChannelListener.h"
namespace mozilla {
namespace net {
@ -20,12 +21,14 @@ class HttpChannelParent;
class HttpChannelParentListener final : public nsIInterfaceRequestor
, public nsIChannelEventSink
, public nsIRedirectResultListener
, public nsIPackagedAppChannelListener
, public nsIStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIPACKAGEDAPPCHANNELLISTENER
NS_DECL_NSIREDIRECTRESULTLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER

View File

@ -726,7 +726,8 @@ PackagedAppService::PackagedAppDownloader::OnDataAvailable(nsIRequest *aRequest,
nsresult
PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
nsICacheEntryOpenCallback *aCallback)
nsICacheEntryOpenCallback *aCallback,
nsIChannel* aRequester)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
nsAutoCString spec;
@ -735,6 +736,8 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
LogURI("PackagedAppDownloader::AddCallback", this, aURI);
LOG(("[%p] > callback: %p\n", this, aCallback));
nsCOMPtr<nsIPackagedAppChannelListener> listener = do_QueryInterface(aRequester);
// Check if we already have a resource waiting for this resource
nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
if (array) {
@ -746,9 +749,12 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
// This is the case where a package downloader is still running and we
// peek data from it.
// TODO: Bug 1186290 to notify that the signed packaged content is ready
// to load.
if (mVerifier && mVerifier->GetIsPackageSigned()) {
// TODO: Bug 1178526 will deal with the package identifier things.
// For now we just use the origin as the identifier.
listener->OnStartSignedPackageRequest(mVerifier->GetPackageOrigin());
listener = nullptr; // So that the request will not be added to the queue.
}
mCacheStorage->AsyncOpenURI(aURI, EmptyCString(),
nsICacheStorage::OPEN_READONLY, aCallback);
} else {
@ -765,6 +771,12 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
newArray->AppendObject(aCallback);
mCallbacks.Put(spec, newArray);
}
// Add the outer channel for notifying OnStartSignedPackageRequest.
if (listener) {
mRequesters.AppendObject(listener);
}
return NS_OK;
}
@ -859,9 +871,19 @@ PackagedAppService::PackagedAppDownloader::ClearCallbacks(nsresult aResult)
void
PackagedAppService::PackagedAppDownloader::NotifyOnStartSignedPackageRequest(const nsACString& aPackageOrigin)
{
// TODO: Bug 1186290 to notify whoever wants to know when the signed package is
// about to load.
LOG(("Notifying the signed package is ready to load."));
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mRequesters is not thread safe");
LOG(("Ready to notify OnStartSignedPackageRequest to all requesters."));
// Notify all requesters that a signed package is about to download and let
// TabParent to decide if the request needs to be re-made in a new process.
for (uint32_t i = 0; i < mRequesters.Length(); i++) {
nsCOMPtr<nsIPackagedAppChannelListener> requester = mRequesters.ObjectAt(i);
LOG(("Notifying %p OnStartSignedPackageRequest. New origin: %s", requester.get(),
nsCString(aPackageOrigin).get()));
requester->OnStartSignedPackageRequest(aPackageOrigin);
}
mRequesters.Clear();
}
void PackagedAppService::PackagedAppDownloader::InstallSignedPackagedApp(const ResourceCacheInfo* aInfo)
@ -965,6 +987,15 @@ PackagedAppService::PackagedAppDownloader::OnResourceVerified(const ResourceCach
return OnError(ERROR_RESOURCE_VERIFIED_FAILED);
}
// If this package is signed and there is any pending requests, we just notify
// right now no matter if this is the requested resource. Doing this can
// have the potential process switch be done as early as possible.
if (mVerifier->GetIsPackageSigned()) {
// TODO: Bug 1178526 will deal with the package identifier things.
// For now we just use the origin as the identifier.
NotifyOnStartSignedPackageRequest(mVerifier->GetPackageOrigin());
}
// Serve this resource to all listeners.
CallCallbacks(aInfo->mURI, aInfo->mCacheEntry, aInfo->mStatusCode);
@ -1096,8 +1127,7 @@ PackagedAppService::GetResource(nsIChannel *aChannel,
// If we find that the package that the file belongs to is currently being
// 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(uri, aCallback);
downloader->AddCallback(uri, aCallback, aChannel);
return NS_OK;
}
@ -1127,7 +1157,7 @@ PackagedAppService::GetResource(nsIChannel *aChannel,
return rv;
}
downloader->AddCallback(uri, aCallback);
downloader->AddCallback(uri, aCallback, aChannel);
nsCOMPtr<nsIStreamConverterService> streamconv =
do_GetService("@mozilla.org/streamConverters;1", &rv);

View File

@ -13,6 +13,7 @@
#include "PackagedAppVerifier.h"
#include "nsIMultiPartChannel.h"
#include "PackagedAppVerifier.h"
#include "nsIPackagedAppChannelListener.h"
namespace mozilla {
namespace net {
@ -124,7 +125,10 @@ private:
const nsACString& aPackageOrigin);
// Registers a callback which gets called when the given nsIURI is downloaded
// aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
nsresult AddCallback(nsIURI *aURI, nsICacheEntryOpenCallback *aCallback);
// aRequester is the outer channel who makes the request for aURI.
nsresult AddCallback(nsIURI *aURI,
nsICacheEntryOpenCallback *aCallback,
nsIChannel* aRequester);
// Remove the callback from the resource callback list.
nsresult RemoveCallbacks(nsICacheEntryOpenCallback* aCallback);
@ -209,6 +213,9 @@ private:
// Deal with verification and delegate callbacks to the downloader.
nsRefPtr<PackagedAppVerifier> mVerifier;
// The outer channels which have issued the request to the downloader.
nsCOMArray<nsIPackagedAppChannelListener> mRequesters;
// The package origin without signed package origin identifier.
// If you need the origin with the signity taken into account, use
// PackagedAppVerifier::GetPackageOrigin().

View File

@ -104,6 +104,16 @@ public:
// Used to explicitly clear the listener to avoid circula reference.
void ClearListener() { mListener = nullptr; }
bool GetIsPackageSigned() const
{
return mIsPackageSigned;
}
const nsACString& GetPackageOrigin() const
{
return mPackageOrigin;
}
static const char* kSignedPakOriginMetadataKey;
private:

View File

@ -4853,6 +4853,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsIChannel)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIPackagedAppChannelListener)
NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
@ -5662,6 +5663,25 @@ nsHttpChannel::GetRequestMethod(nsACString& aMethod)
return HttpBaseChannel::GetRequestMethod(aMethod);
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIPackagedAppChannelListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::OnStartSignedPackageRequest(const nsACString& aPackageId)
{
nsCOMPtr<nsIPackagedAppChannelListener> listener;
NS_QueryNotificationCallbacks(this, listener);
if (listener) {
listener->OnStartSignedPackageRequest(aPackageId);
} else {
LOG(("nsHttpChannel::OnStartSignedPackageRequest [this=%p], no listener on %p", this, mListener.get()));
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIRequestObserver
//-----------------------------------------------------------------------------

View File

@ -9,6 +9,7 @@
#include "HttpBaseChannel.h"
#include "nsTArray.h"
#include "nsIPackagedAppChannelListener.h"
#include "nsICachingChannel.h"
#include "nsICacheEntry.h"
#include "nsICacheEntryOpenCallback.h"
@ -59,6 +60,7 @@ public:
class nsHttpChannel final : public HttpBaseChannel
, public HttpAsyncAborter<nsHttpChannel>
, public nsIStreamListener
, public nsIPackagedAppChannelListener
, public nsICachingChannel
, public nsICacheEntryOpenCallback
, public nsITransportEventSink
@ -76,6 +78,7 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIPACKAGEDAPPCHANNELLISTENER
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
NS_DECL_NSICACHEINFOCHANNEL
NS_DECL_NSICACHINGCHANNEL

View File

@ -11,6 +11,7 @@ support-files =
redirect_idn.html
empty.html
web_packaged_app.sjs
signed_web_packaged_app.sjs
[test_arraybufferinputstream.html]
[test_partially_cached_content.html]
@ -25,4 +26,6 @@ skip-if = e10s
[test_xhr_method_case.html]
skip-if = e10s
[test_idn_redirect.html]
[test_signed_web_packaged_app.html]
skip-if = e10s || buildapp != 'browser'
[test_web_packaged_app.html]

View File

@ -0,0 +1,37 @@
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
function handleRequest(request, response)
{
response.setHeader("Content-Type", "application/package", false);
response.write(octetStreamData.packageHeader + octetStreamData.getData());
return;
}
// The package content
// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
var octetStreamData = {
packageHeader: 'manifest-signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\r\n',
content: [
{ headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n <head>\r\n <script> alert('OK: hello'); alert('DONE'); </script>\r\n</head>\r\n Web Packaged App Index\r\n</html>\r\n", type: "text/html" },
{ headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
{ headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
],
token : "gc0pJq0M:08jU534c0p",
getData: function() {
var str = "";
for (var i in this.content) {
str += "--" + this.token + "\r\n";
for (var j in this.content[i].headers) {
str += this.content[i].headers[j] + "\r\n";
}
str += "\r\n";
str += this.content[i].data + "\r\n";
}
str += "--" + this.token + "--";
return str;
}
}

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<title> Web packaged app </title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
var Cc = SpecialPowers.Cc;
var Ci = SpecialPowers.Ci;
var Cu = SpecialPowers.Cu;
var Cr = SpecialPowers.Cr;
SpecialPowers.pushPrefEnv(
{ "set": [["network.http.enable-packaged-apps", true],
["network.http.packaged-apps-developer-mode", true],
["dom.ipc.processPriorityManager.testMode", true],
["dom.ipc.processPriorityManager.enabled", true],
["dom.ipc.tabs.disabled", false],
["dom.ipc.processCount", 3],
["dom.mozBrowserFramesEnabled", true]] },
() => SpecialPowers.pushPermissions([
{ "type": "browser", "allow": 1, "context": document }
], function() {
runTest();
}));
SimpleTest.waitForExplicitFinish();
// Listen for and count process-created event. Since we are loading a
// signed content from a remote tab, there shouls be two processes created.
// One is for remote tab and one for the signed package.
var kProcessCreatedTopic = "process-priority-manager:TEST-ONLY:process-created";
var processCreatedCnt = 0;
SpecialPowers.addObserver(() => {
processCreatedCnt++;
if (processCreatedCnt == 1) {
ok(true, "We have one more process to create.");
} else if (processCreatedCnt == 2) {
SimpleTest.finish();
}
}, kProcessCreatedTopic, /* weak = */ false);
function runTest() {
var iframe = document.createElement("iframe");
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('remote', 'true');
iframe.setAttribute("src", "http://example.org:80");
iframe.addEventListener("mozbrowserloadend", function loadend(e) {
iframe.removeEventListener("mozbrowserloadend", loadend);
ok(true, "Got mozbrowserloadend");
iframe.setAttribute("src", "http://mochi.test:8888/tests/netwerk/test/mochitests/signed_web_packaged_app.sjs!//scripts/app.js");
});
document.body.appendChild(iframe);
}
</script>
</pre>
</body>
</html>

View File

@ -41,6 +41,9 @@ Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
let gPrefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
// The number of times this package has been requested
// This number might be reset by tests that use it
var packagedAppRequestsMade = 0;
@ -65,7 +68,7 @@ function packagedAppContentHandler(metadata, response)
response.bodyOutputStream.write(body, body.length);
}
function getChannelForURL(url) {
function getChannelForURL(url, notificationCallbacks) {
let uri = createURI(url);
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
@ -77,17 +80,24 @@ function getChannelForURL(url) {
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
});
tmpChannel.notificationCallbacks =
new LoadContextCallback(principal.appId,
principal.isInBrowserElement,
false,
false);
if (notificationCallbacks) {
// Use custom notificationCallbacks if any.
tmpChannel.notificationCallbacks = notificationCallbacks;
} else {
tmpChannel.notificationCallbacks =
new LoadContextCallback(principal.appId,
principal.isInBrowserElement,
false,
false);
}
return tmpChannel;
}
// The package content
// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
var testData = {
packageHeader: 'manifest-signature: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\r\n',
content: [
{ headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n <head>\r\n <script src=\"/scripts/app.js\"></script>\r\n ...\r\n </head>\r\n ...\r\n</html>\r\n", type: "text/html" },
{ headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
@ -136,8 +146,17 @@ function run_test()
packagedAppWorseContentHandler.bind(null, i));
}
httpserver.registerPathHandler("/signedPackage", signedPackagedAppContentHandler);
httpserver.start(-1);
// We will enable the developer mode in 'test_signed_package_callback'.
// So restore it after testing.
//
// TODO: To be removed in Bug 1178518.
do_register_cleanup(function() {
gPrefs.clearUserPref("network.http.packaged-apps-developer-mode");
});
paservice = Cc["@mozilla.org/network/packaged-app-service;1"]
.getService(Ci.nsIPackagedAppService);
ok(!!paservice, "test service exists");
@ -155,6 +174,9 @@ function run_test()
add_test(test_bad_package);
add_test(test_bad_package_404);
add_test(test_signed_package_callback);
add_test(test_unsigned_package_callback);
// Channels created by addons could have no load info.
// In debug mode this triggers an assertion, but we still want to test that
// it works in optimized mode. See bug 1196021 comment 17
@ -474,3 +496,81 @@ function test_worse_package_4() {
function test_worse_package_5() {
test_worse_package(5, true);
}
//-----------------------------------------------------------------------------
function signedPackagedAppContentHandler(metadata, response)
{
response.setHeader("Content-Type", 'application/package');
var body = testData.packageHeader + testData.getData();
response.bodyOutputStream.write(body, body.length);
}
// Used as a stub when the cache listener is not important.
let dummyCacheListener = {
QueryInterface: function (iid) {
if (iid.equals(Ci.nsICacheEntryOpenCallback) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
onCacheEntryAvailable: function () {}
};
function test_signed_package_callback()
{
// TODO: To be removed in Bug 1178518.
gPrefs.setBoolPref("network.http.packaged-apps-developer-mode", true);
packagePath = "/signedPackage";
let url = uri + packagePath + "!//index.html";
let channel = getChannelForURL(url, {
onStartSignedPackageRequest: function(aPackageId) {
ok(true, "onStartSignedPackageRequest is notifited as expected");
run_next_test();
},
getInterface: function (iid) {
return this.QueryInterface(iid);
},
QueryInterface: function (iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIInterfaceRequestor) ||
iid.equals(Ci.nsIPackagedAppChannelListener)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
});
paservice.getResource(channel, dummyCacheListener);
}
function test_unsigned_package_callback()
{
packagePath = "/package";
let url = uri + packagePath + "!//index.html";
let channel = getChannelForURL(url, {
onStartSignedPackageRequest: function(aPackageId) {
ok(false, "Unsigned package shouldn't be called.");
},
getInterface: function (iid) {
return this.QueryInterface(iid);
},
QueryInterface: function (iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIInterfaceRequestor) ||
iid.equals(Ci.nsIPackagedAppChannelListener)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
},
});
// Pass cacheListener since we rely on 'run_next_test' in it.
paservice.getResource(channel, cacheListener);
}