Bug 1197379 - Remove support for intercepting app:// URIs using service workers; r=jdm

This commit is contained in:
Ehsan Akhgari 2016-01-25 14:03:13 -05:00
parent 99925bf95f
commit dc29e5f515
53 changed files with 98 additions and 1812 deletions

View File

@ -119,7 +119,6 @@ IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled)
nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
if (scheme.LowerCaseEqualsLiteral("https") ||
scheme.LowerCaseEqualsLiteral("app") ||
scheme.LowerCaseEqualsLiteral("file")) {
return true;
}

View File

@ -417,8 +417,7 @@ TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
if (aSchemeValidOut) {
nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
*aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") ||
scheme.LowerCaseEqualsLiteral("https") ||
scheme.LowerCaseEqualsLiteral("app");
scheme.LowerCaseEqualsLiteral("https");
}
uint32_t queryPos;

View File

@ -13,8 +13,6 @@
#include "nsSerializationHelper.h"
#include "mozilla/net/HttpBaseChannel.h"
#include "mozilla/ipc/ChannelInfo.h"
#include "nsIJARChannel.h"
#include "nsJARChannel.h"
#include "nsNetUtil.h"
using namespace mozilla;
@ -90,32 +88,20 @@ ChannelInfo::ResurrectInfoOnChannel(nsIChannel* aChannel)
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mInited);
// These pointers may be null at this point. They must be checked before
// being dereferenced.
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(aChannel);
nsCOMPtr<nsIJARChannel> jarChannel =
do_QueryInterface(aChannel);
if (!mSecurityInfo.IsEmpty()) {
nsCOMPtr<nsISupports> infoObj;
nsresult rv = NS_DeserializeObject(mSecurityInfo, getter_AddRefs(infoObj));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (httpChannel) {
net::HttpBaseChannel* httpBaseChannel =
static_cast<net::HttpBaseChannel*>(httpChannel.get());
rv = httpBaseChannel->OverrideSecurityInfo(infoObj);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
if (NS_WARN_IF(!jarChannel)) {
return NS_ERROR_FAILURE;
}
static_cast<nsJARChannel*>(jarChannel.get())->
OverrideSecurityInfo(infoObj);
nsCOMPtr<nsIHttpChannel> httpChannel =
do_QueryInterface(aChannel);
MOZ_ASSERT(httpChannel);
net::HttpBaseChannel* httpBaseChannel =
static_cast<net::HttpBaseChannel*>(httpChannel.get());
rv = httpBaseChannel->OverrideSecurityInfo(infoObj);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}

View File

@ -14,7 +14,6 @@
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIJARChannel.h"
#include "nsIScriptSecurityManager.h"
#include "nsIThreadRetargetableRequest.h"
#include "nsIUploadChannel2.h"
@ -463,7 +462,6 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
RefPtr<InternalResponse> response;
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
// On a successful redirect we perform the following substeps of HTTP Fetch,
// step 5, "redirect status", step 11.
@ -508,18 +506,6 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
if (NS_WARN_IF(NS_FAILED(rv))) {
NS_WARNING("Failed to visit all headers.");
}
} else if (jarChannel) {
// We simulate the http protocol for jar/app requests
uint32_t responseStatus = 200;
nsAutoCString statusText;
response = new InternalResponse(responseStatus, NS_LITERAL_CSTRING("OK"));
ErrorResult result;
nsAutoCString contentType;
jarChannel->GetContentType(contentType);
response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
contentType,
result);
MOZ_ASSERT(!result.Failed());
} else {
response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));

View File

@ -306,14 +306,6 @@ InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel)
// TODO: remove following code once securityMode is fully implemented (bug 1189945)
// We only support app:// protocol interception in non-release builds.
#ifndef RELEASE_BUILD
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aChannel);
if (jarChannel) {
return RequestMode::No_cors;
}
#endif
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
uint32_t corsMode;

View File

@ -36,8 +36,6 @@ IPDL_SOURCES += [
LOCAL_INCLUDES += [
'../workers',
# For nsJARChannel.h
'/modules/libjar',
# For HttpBaseChannel.h dependencies
'/netwerk/base',
# For nsDataHandler.h

View File

@ -14,7 +14,6 @@
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIJARChannel.h"
#include "nsINetworkInterceptController.h"
#include "nsIMutableArray.h"
#include "nsIScriptError.h"
@ -1766,15 +1765,7 @@ ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
aScriptURI->SchemeIs("http", &isHttp);
aScriptURI->SchemeIs("https", &isHttps);
if (NS_WARN_IF(!isHttp && !isHttps)) {
#ifdef RELEASE_BUILD
return NS_ERROR_DOM_SECURITY_ERR;
#else
bool isApp = false;
aScriptURI->SchemeIs("app", &isApp);
if (NS_WARN_IF(!isApp)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
#endif
}
nsCString cleanedScope;

View File

@ -998,7 +998,6 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable
nsCString mMethod;
nsString mClientId;
bool mIsReload;
DebugOnly<bool> mIsHttpChannel;
RequestMode mRequestMode;
RequestRedirect mRequestRedirect;
RequestCredentials mRequestCredentials;
@ -1021,7 +1020,6 @@ public:
, mScriptSpec(aScriptSpec)
, mClientId(aDocumentId)
, mIsReload(aIsReload)
, mIsHttpChannel(false)
, mRequestMode(RequestMode::No_cors)
, mRequestRedirect(RequestRedirect::Follow)
// By default we set it to same-origin since normal HTTP fetches always
@ -1070,61 +1068,46 @@ public:
nsCOMPtr<nsIURI> referrerURI;
rv = NS_GetReferrerFromChannel(channel, getter_AddRefs(referrerURI));
// We can't bail on failure since certain non-http channels like JAR
// channels are intercepted but don't have referrers.
if (NS_SUCCEEDED(rv) && referrerURI) {
NS_ENSURE_SUCCESS(rv, rv);
if (referrerURI) {
rv = referrerURI->GetSpec(mReferrer);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (httpChannel) {
mIsHttpChannel = true;
MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
rv = httpChannel->GetRequestMethod(mMethod);
rv = httpChannel->GetRequestMethod(mMethod);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
// This is safe due to static_asserts at top of file.
uint32_t redirectMode;
internalChannel->GetRedirectMode(&redirectMode);
mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
mRequestCredentials = InternalRequest::MapChannelToRequestCredentials(channel);
rv = httpChannel->VisitNonDefaultRequestHeaders(this);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
if (uploadChannel) {
MOZ_ASSERT(!mUploadStream);
bool bodyHasHeaders = false;
rv = uploadChannel->GetUploadStreamHasHeaders(&bodyHasHeaders);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
// This is safe due to static_asserts at top of file.
uint32_t redirectMode;
internalChannel->GetRedirectMode(&redirectMode);
mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
mRequestCredentials = InternalRequest::MapChannelToRequestCredentials(channel);
rv = httpChannel->VisitNonDefaultRequestHeaders(this);
nsCOMPtr<nsIInputStream> uploadStream;
rv = uploadChannel->CloneUploadStream(getter_AddRefs(uploadStream));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
if (uploadChannel) {
MOZ_ASSERT(!mUploadStream);
bool bodyHasHeaders = false;
rv = uploadChannel->GetUploadStreamHasHeaders(&bodyHasHeaders);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> uploadStream;
rv = uploadChannel->CloneUploadStream(getter_AddRefs(uploadStream));
NS_ENSURE_SUCCESS(rv, rv);
if (bodyHasHeaders) {
HandleBodyWithHeaders(uploadStream);
} else {
mUploadStream = uploadStream;
}
}
} else {
nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
// If it is not an HTTP channel it must be a JAR one.
NS_ENSURE_TRUE(jarChannel, NS_ERROR_NOT_AVAILABLE);
mMethod = "GET";
mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
mRequestCredentials = RequestCredentials::Omit;
if (bodyHasHeaders) {
HandleBodyWithHeaders(uploadStream);
} else {
mUploadStream = uploadStream;
}
}
@ -1213,9 +1196,7 @@ private:
}
RefPtr<Request> request = new Request(global, internalReq);
// TODO: remove conditional on http here once app protocol support is
// removed from service worker interception
MOZ_ASSERT_IF(mIsHttpChannel && internalReq->IsNavigationRequest(),
MOZ_ASSERT_IF(internalReq->IsNavigationRequest(),
request->Redirect() == RequestRedirect::Manual);
RootedDictionary<FetchEventInit> init(aCx);

View File

@ -724,83 +724,54 @@ CompareNetwork::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
if (httpChannel) {
bool requestSucceeded;
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
if (NS_WARN_IF(!requestSucceeded)) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
nsAutoCString maxScope;
// Note: we explicitly don't check for the return value here, because the
// absence of the header is not an error condition.
Unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Service-Worker-Allowed"),
maxScope);
mManager->SetMaxScope(maxScope);
bool isFromCache = false;
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(httpChannel));
if (cacheChannel) {
cacheChannel->IsFromCache(&isFromCache);
}
// [9.2 Update]4.13, If response's cache state is not "local",
// set registration's last update check time to the current time
if (!isFromCache) {
RefPtr<ServiceWorkerRegistrationInfo> registration =
mManager->GetRegistration();
registration->RefreshLastUpdateCheckTime();
}
nsAutoCString mimeType;
rv = httpChannel->GetContentType(mimeType);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
return rv;
}
if (!mimeType.LowerCaseEqualsLiteral("text/javascript") &&
!mimeType.LowerCaseEqualsLiteral("application/x-javascript") &&
!mimeType.LowerCaseEqualsLiteral("application/javascript")) {
mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
return rv;
}
bool requestSucceeded;
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
else {
// The only supported request schemes are http, https, and app.
// Above, we check to ensure that the request is http or https
// based on the channel qi. Here we test the scheme to ensure
// that it is app. Otherwise, bail.
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
if (NS_WARN_IF(!channel)) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
rv = channel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
nsAutoCString scheme;
rv = uri->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(rv);
return NS_OK;
}
if (NS_WARN_IF(!requestSucceeded)) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
if (NS_WARN_IF(!scheme.LowerCaseEqualsLiteral("app"))) {
mManager->NetworkFinished(NS_ERROR_FAILURE);
return NS_OK;
}
nsAutoCString maxScope;
// Note: we explicitly don't check for the return value here, because the
// absence of the header is not an error condition.
Unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Service-Worker-Allowed"),
maxScope);
mManager->SetMaxScope(maxScope);
bool isFromCache = false;
nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(httpChannel));
if (cacheChannel) {
cacheChannel->IsFromCache(&isFromCache);
}
// [9.2 Update]4.13, If response's cache state is not "local",
// set registration's last update check time to the current time
if (!isFromCache) {
RefPtr<ServiceWorkerRegistrationInfo> registration =
mManager->GetRegistration();
registration->RefreshLastUpdateCheckTime();
}
nsAutoCString mimeType;
rv = httpChannel->GetContentType(mimeType);
if (NS_WARN_IF(NS_FAILED(rv))) {
mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
return rv;
}
if (!mimeType.LowerCaseEqualsLiteral("text/javascript") &&
!mimeType.LowerCaseEqualsLiteral("application/x-javascript") &&
!mimeType.LowerCaseEqualsLiteral("application/javascript")) {
mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
return rv;
}
char16_t* buffer = nullptr;

View File

@ -1,3 +0,0 @@
application.list contains a list of files that are in application.zip.
To update application.zip when changing one of those files, run makezip.sh.

View File

@ -1,8 +0,0 @@
controlled.html
foo.txt
index.html
manifest.webapp
sw.js
test.js
test_doc_load_interception.js
unregister.html

View File

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1161684</title>
<script src='test.js'></script>
<script src='test_doc_load_interception.js'></script>
<script type='application/javascript;version=1.7'>
function runTests() {
return Promise.resolve()
.then(navigator.serviceWorker.ready)
.then(() => {
return testFetchAppResource('foo.txt',
'swresponse', 'text/plain');
})
.then(() => {
return testFetchAppResource('foo.txt?fetch=1',
'networkresponse', 'text/plain');
})
.then(() => {
return testFetchAppResource('test_custom_content_type',
'customContentType', 'text/html');
})
// XXX: Cross-origin interceptions without CORS result in opaque responses
// which are illegal for navigations like iframes. (bug 1183313)
//.then(testRedirectedResponse)
//.then(testRedirectedHttpsResponse)
//.then(testCachedRedirectedResponse)
//.then(testCachedRedirectedHttpsResponse)
.then(done);
}
</script>
</head>
<body onload='runTests()'>
</body>
</html>

View File

@ -1 +0,0 @@
networkresponse

View File

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1161684</title>
<script src='test.js'></script>
<script type='application/javascript;version=1.7'>
function registerServiceWorker() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.ready.then(() => {
resolve();
});
navigator.serviceWorker.register('sw.js', {scope: '.'})
.then(registration => {
ok(true, 'service worker registered');
})
.catch(reject);
});
}
function runTests() {
return Promise.resolve()
.then(() => { return testFetchAppResource('foo.txt',
'networkresponse'); })
.then(registerServiceWorker)
.then(ready);
}
</script>
</head>
<body onload='runTests()'>
</body>
</html>

View File

@ -1,4 +0,0 @@
#!/bin/sh
rm application.zip
zip application.zip `cat application.list`

View File

@ -1,5 +0,0 @@
{
"name": "App",
"launch_path": "/index.html",
"description": "Test app for bug 1161684"
}

View File

@ -1,2 +0,0 @@
<!DOCTYPE html>
real index

View File

@ -1 +0,0 @@
Access-Control-Allow-Origin: *

View File

@ -1,5 +0,0 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Access-Control-Allow-Origin", "*", false);
response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/app-protocol/realindex.html", false);
}

View File

@ -1,5 +0,0 @@
function handleRequest(request, response) {
response.setStatusLine(null, 308, "Permanent Redirect");
response.setHeader("Access-Control-Allow-Origin", "*", false);
response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/app-protocol/realindex.html", false);
}

View File

@ -1,69 +0,0 @@
const kHTTPRedirect = "http://example.com/tests/dom/workers/test/serviceworkers/app-protocol/redirect.sjs";
const kHTTPSRedirect = "https://example.com/tests/dom/workers/test/serviceworkers/app-protocol/redirect-https.sjs";
self.addEventListener('install', (event) => {
event.waitUntil(
self.caches.open("origin-app-cache")
.then(c => {
return Promise.all(
[
c.add(kHTTPRedirect),
c.add(kHTTPSRedirect),
]
);
})
);
});
self.addEventListener('fetch', (event) => {
if (event.request.url.indexOf('foo.txt') >= 0) {
var url = new URL(event.request.url);
var shouldFetch = url.searchParams.get('fetch');
if (shouldFetch) {
event.respondWith(fetch(event.request));
return;
}
event.respondWith(new Response('swresponse', {
headers: {'Content-Type': 'text/plain'}
}));
}
if (event.request.url.indexOf('test_doc_load_interception.js') >= 0 ) {
var scriptContent = 'alert("OK: Script modified by service worker")';
event.respondWith(new Response(scriptContent, {
headers: {'Content-Type': 'application/javascript'}
}));
}
if (event.request.url.indexOf('test_custom_content_type') >= 0) {
event.respondWith(new Response('customContentType', {
headers: {'Content-Type': 'text/html'}
}));
}
if (event.request.url.indexOf('redirected.html') >= 0) {
event.respondWith(fetch(kHTTPRedirect));
}
if (event.request.url.indexOf('redirected-https.html') >= 0) {
event.respondWith(fetch(kHTTPSRedirect));
}
if (event.request.url.indexOf('redirected-cached.html') >= 0) {
event.respondWith(
self.caches.open("origin-app-cache")
.then(c => {
return c.match(kHTTPRedirect);
})
);
}
if (event.request.url.indexOf('redirected-https-cached.html') >= 0) {
event.respondWith(
self.caches.open("origin-app-cache")
.then(c => {
return c.match(kHTTPSRedirect);
})
);
}
});

View File

@ -1,72 +0,0 @@
function ok(aCondition, aMessage) {
if (aCondition) {
alert('OK: ' + aMessage);
} else {
alert('KO: ' + aMessage);
}
}
function ready() {
alert('READY');
}
function done() {
alert('DONE');
}
function testFetchAppResource(aUrl,
aExpectedResponse,
aExpectedContentType) {
return fetch(aUrl).then(res => {
ok(true, 'fetch should resolve for ' + aUrl);
if (res.type == 'error') {
ok(false, 'fetch failed');
}
ok(res.status == 200, 'status should be 200');
ok(res.statusText == 'OK', 'statusText should be OK');
if (aExpectedContentType) {
var headers = res.headers.getAll('Content-Type');
ok(headers.length, "Headers length");
var contentType = res.headers.get('Content-Type');
ok(contentType == aExpectedContentType, ('content type ' +
contentType + ' should match with ' + aExpectedContentType));
}
return res.text().then(body => {
ok(body == aExpectedResponse, 'body ' + body +
' should match with ' + aExpectedResponse);
});
});
}
function testRedirectedResponse() {
return testRedirectedResponseWorker("redirected", "IFRAMELOADED");
}
function testRedirectedHttpsResponse() {
return testRedirectedResponseWorker("redirected-https", "HTTPSIFRAMELOADED");
}
function testCachedRedirectedResponse() {
return testRedirectedResponseWorker("redirected-cached", "IFRAMELOADED");
}
function testCachedRedirectedHttpsResponse() {
return testRedirectedResponseWorker("redirected-https-cached", "HTTPSIFRAMELOADED");
}
function testRedirectedResponseWorker(aFrameId, aAlert) {
// Because of the CSP policies applied to privileged apps, we cannot run
// inline script inside realindex.html, and loading a script from the app://
// URI is also not an option, so we let the parent iframe which has access
// to the SpecialPowers API use those privileges to access the document.
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.src = aFrameId + ".html";
iframe.id = aFrameId;
return new Promise(resolve => {
iframe.addEventListener("load", event => {
alert(aAlert);
resolve();
}, false);
});
}

View File

@ -1 +0,0 @@
alert('KO: Should not load this file, but the sw modified version instead');

View File

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src='test.js'></script>
<script type='application/javascript;version=1.7'>
function unregisterServiceWorker() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.getRegistration(".").then((reg) => {
reg.unregister().then(resolve);
})
.catch(reject);
});
}
function onLoad() {
return Promise.resolve()
.then(unregisterServiceWorker)
.then(done);
}
</script>
</head>
<body onload='onLoad()'>
</body>
</html>

View File

@ -1,6 +0,0 @@
{
"name": "App",
"launch_path": "/index.html",
"description": "Test app for bug 1161684",
"package_path": "application.zip"
}

View File

@ -1 +0,0 @@
Content-Type: application/manifest+json

View File

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1169249</title>
<script type='application/javascript;version=1.7'>
function ok(aCondition, aMessage) {
if (aCondition) {
alert('OK: ' + aMessage);
} else {
alert('KO: ' + aMessage);
}
}
function ready() {
alert('READY');
}
function registerServiceWorker() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.ready.then(() => {
ready();
resolve();
});
navigator.serviceWorker.register('sw.js', {scope: '.'})
.then(registration => {
ok(true, 'service worker registered');
})
.catch(reject);
});
}
function runTests() {
return Promise.resolve()
.then(registerServiceWorker)
.then(ready)
}
</script>
</head>
<body onload='runTests()'>
</body>
</html>

View File

@ -1,5 +0,0 @@
{
"name": "App",
"launch_path": "/index.html",
"description": "Test app for bug 1169249"
}

View File

@ -1 +0,0 @@
Content-Type: application/manifest+json

View File

@ -1 +0,0 @@
// Useless service worker.

View File

@ -1,17 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test app for bug 1178233</title>
<script type='application/javascript;version=1.7'>
function onLoad() {
navigator.serviceWorker.ready.then(function(swr) {
parent.postMessage({status: "callback", data: "done"}, '*');
});
}
</script>
</head>
<body onload='onLoad()'>
</body>
</html>

View File

@ -1,81 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1178233</title>
<script type='application/javascript;version=1.7'>
function ok(aCondition, aMessage) {
if (aCondition) {
alert('OK: ' + aMessage);
} else {
alert('KO: ' + aMessage);
}
}
function done() {
alert('DONE');
}
function testFrame(src) {
return new Promise(function(resolve, reject) {
var iframe = document.createElement("iframe");
iframe.src = src;
window.onmessage = function(e) {
if (e.data.status == "callback") {
window.onmessage = null;
var result = e.data.data;
iframe.src = "about:blank";
document.body.removeChild(iframe);
iframe = null;
resolve(result);
}
};
document.body.appendChild(iframe);
});
}
function registerServiceWorker() {
return navigator.serviceWorker.register('sw.sjs', {scope: '.'});
}
function runTests() {
var lastSeenVersion;
return Promise.resolve()
.then(function() {
// Check whether the service worker is already registered and save its
// version.
return navigator.serviceWorker.getRegistration(".").then(function(swr) {
if (!swr) {
lastSeenVersion = 0;
return registerServiceWorker();
}
return testFrame('version.html').then(function(body) {
lastSeenVersion = parseInt(body);
return Promise.resolve();
});
});
})
.then(function() {
// Wait until the service worker start controlling the client.
return testFrame('client.html');
})
.then(function() {
return new Promise((resolve, reject) => {
testFrame('wait_for_update.html').then(function() {
// Fetch current version. It should be greater than the last seen version.
testFrame('version.html').then(function(body) {
var currentVersion = parseInt(body);
ok(lastSeenVersion < currentVersion, "New service worker version seen");
resolve();
});
});
});
})
.then(done);
}
</script>
</head>
<body onload='runTests()'>
</body>
</html>

View File

@ -1,5 +0,0 @@
{
"name": "App",
"launch_path": "/index.html",
"description": "Test app for bug 1178233"
}

View File

@ -1 +0,0 @@
Content-Type: application/manifest+json

View File

@ -1,22 +0,0 @@
function handleRequest(request, response) {
var stateName = request.scheme + 'counter';
if (!getState(stateName)) {
setState(stateName, '1');
} else {
// Make sure that we pass a string value to setState!
setState(stateName, "" + (parseInt(getState(stateName)) + 1));
}
response.setHeader("Content-Type", "application/javascript", false);
response.write(getScript(stateName));
}
function getScript(stateName) {
return "oninstall = function(evt) {" +
"evt.waitUntil(self.skipWaiting());" +
"}; " +
"onfetch = function(evt) {" +
"if (evt.request.url.indexOf('get-sw-version') > -1) {" +
"evt.respondWith(new Response('" + getState(stateName) + "'));" +
"}" +
"};";
}

View File

@ -1,19 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test app for bug 1178233</title>
<script type='application/javascript;version=1.7'>
function onLoad() {
fetch("get-sw-version").then(function(r) {
return r.text();
}).then(function(body) {
parent.postMessage({status: "callback", data: body}, "*");
});
}
</script>
</head>
<body onload='onLoad()'>
</body>
</html>

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1178233</title>
<script type='application/javascript;version=1.7'>
function update() {
alert('UPDATE');
}
function onLoad() {
navigator.serviceWorker.getRegistration(".").then(function(swr) {
swr.onupdatefound = function() {
swr.onupdatefound = null;
parent.postMessage({status: "callback", data: "done"}, "*");
};
update();
});
}
</script>
</head>
<body onload='onLoad()'>
</body>
</html>

View File

@ -1,51 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Test app for bug 1169249</title>
<script type='application/javascript;version=1.7'>
function ok(aCondition, aMessage) {
if (aCondition) {
alert('OK: ' + aMessage);
} else {
alert('KO: ' + aMessage);
}
}
function ready() {
alert('READY');
}
function registerServiceWorker() {
return new Promise((resolve, reject) => {
navigator.serviceWorker.ready.then(() => {
ready();
resolve();
});
navigator.serviceWorker.register('../empty.js', {scope: '.'})
.then(registration => {
ok(true, 'service worker registered');
})
.catch(reject);
});
}
function AddIframe() {
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('src', "http://test/chrome/dom/workers/test/serviceworkers/serviceworker.html");
document.body.appendChild(iframe);
iframe.addEventListener("mozbrowserloadend", resolve);
});
}
function runTests() {
return Promise.resolve()
.then(AddIframe)
.then(registerServiceWorker)
.then(ready)
}
</script>
</head>
<body onload='runTests()'>
</body>
</html>

View File

@ -1,5 +0,0 @@
{
"name": "App",
"launch_path": "/index.html",
"description": "Test app for bug 1169249"
}

View File

@ -1 +0,0 @@
Content-Type: application/manifest+json

View File

@ -1,9 +1,6 @@
[DEFAULT]
skip-if = buildapp == 'b2g' || os == 'android'
support-files =
app/*
app2/*
app3/*
chrome_helpers.js
empty.js
serviceworker.html
@ -13,10 +10,6 @@ support-files =
worker.js
worker2.js
[test_aboutserviceworkers.html]
skip-if = true #bug 1193319
[test_clear_origin_data.html]
[test_app_installation.html]
[test_privateBrowsing.html]
[test_serviceworkerinfo.xul]
[test_serviceworkermanager.xul]

View File

@ -161,7 +161,6 @@ support-files =
claim_worker_2.js
claim_clients/client.html
claim_fetch_worker.js
app-protocol/*
force_refresh_worker.js
sw_clients/refresher.html
sw_clients/refresher_compressed.html
@ -199,8 +198,6 @@ support-files =
redirect.sjs
open_window/client.html
[test_app_protocol.html]
skip-if = release_build || (e10s && debug && os == 'win')
[test_bug1151916.html]
skip-if = e10s && debug && os == 'win'
[test_claim.html]

View File

@ -1,164 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1178233
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1178233</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script type="application/javascript;version=1.7">
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gServiceWorkerManager",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
XPCOMUtils.defineLazyServiceGetter(this, "gAppsService",
"@mozilla.org/AppsService;1",
"nsIAppsService");
SimpleTest.waitForExplicitFinish();
const gOrigin = 'http://test/chrome/dom/workers/test/serviceworkers/app2';
const appManifestURL = gOrigin + '/manifest.webapp';
let gApp;
function uninstallApp() {
return new Promise((resolve, reject) => {
if (!gApp) {
return reject();
}
let req = navigator.mozApps.mgmt.uninstall(gApp);
req.onsuccess = resolve;
req.onerror = reject;
});
}
function update() {
return new Promise((resolve, reject) => {
let registrations = [];
registrations = gServiceWorkerManager.getAllRegistrations();
ok(registrations.length === 1, "The registration shows up in about:serviceworker page");
for (let i = 0; i < registrations.length; i++) {
let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
if (!registration) {
reject();
return;
}
info('Update service worker registration');
gServiceWorkerManager.propagateSoftUpdate(
registration.principal.originAttributes,
registration.scope
);
}
resolve();
});
}
function testApp() {
if (!gApp) {
ok(false, 'No test application to launch');
return Promise.reject();
}
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
let domParent = document.getElementById('container');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/UPDATE/.exec(message)) {
ok(true, "Message from app: " + message);
update();
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/DONE/.exec(message)) {
ok(true, "Message from app: " + message);
iframe.src = "about:blank";
domParent.removeChild(iframe);
iframe = null;
resolve();
}
}, false);
domParent.appendChild(iframe);
ok(true, "origin " + gOrigin + gApp.manifest.launch_path);
SpecialPowers.wrap(iframe.contentWindow).location =
gOrigin + gApp.manifest.launch_path;
});
}
function installApp() {
return new Promise((resolve, reject) => {
let req = navigator.mozApps.install(appManifestURL);
req.onsuccess = function() {
gApp = req.result;
is(req.result.manifestURL, appManifestURL, 'app installed');
if (req.result.installState == 'installed') {
is(req.result.installState, 'installed', 'app downloaded');
resolve()
} else {
req.result.ondownloadapplied = function() {
is(req.result.installState, 'installed', 'app downloaded');
resolve();
}
}
}
req.onerror = reject;
});
}
function setup() {
info('Setting up');
return new Promise((resolve, reject) => {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({'set': [
['dom.mozBrowserFramesEnabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.testing.enabled', true],
['dom.serviceWorkers.interception.enabled', true]
]}, () => {
SpecialPowers.pushPermissions([
{ 'type': 'webapps-manage', 'allow': 1, 'context': document },
{ 'type': 'browser', 'allow': 1, 'context': document },
{ 'type': 'embed-apps', 'allow': 1, 'context': document }
], () => {
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(resolve);
});
});
});
});
}
function go() {
setup()
.then(installApp)
.then(testApp)
.then(uninstallApp)
.then(SimpleTest.finish)
.catch(function(e) {
ok(false, 'Unexpected error ' + e);
SimpleTest.finish();
});
}
</script>
</head>
<body onload="go()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1171917">Mozilla Bug 1178233</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<div id="container"></div>
</body>
</html>

View File

@ -1,168 +0,0 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id={1169249}
-->
<head>
<title>Test for Bug {1169249}</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1169249}">Mozilla Bug {1169249}</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gServiceWorkerManager",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
XPCOMUtils.defineLazyServiceGetter(this, "gAppsService",
"@mozilla.org/AppsService;1",
"nsIAppsService");
SimpleTest.waitForExplicitFinish();
const gOrigin = 'http://test/chrome/dom/workers/test/serviceworkers/app';
const appManifestURL = gOrigin + '/manifest.webapp';
let gApp;
addLoadEvent(go);
function setup() {
info('Setting up');
return new Promise((resolve, reject) => {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({'set': [
['dom.mozBrowserFramesEnabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.testing.enabled', true],
['dom.serviceWorkers.interception.enabled', true],
]}, () => {
SpecialPowers.pushPermissions([
{ 'type': 'webapps-manage', 'allow': 1, 'context': document },
{ 'type': 'browser', 'allow': 1, 'context': document },
{ 'type': 'embed-apps', 'allow': 1, 'context': document }
], () => {
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(resolve);
});
});
});
});
}
function installApp() {
return new Promise((resolve, reject) => {
let req = navigator.mozApps.install(appManifestURL);
req.onsuccess = function() {
gApp = req.result;
is(req.result.manifestURL, appManifestURL, 'app installed');
if (req.result.installState == 'installed') {
is(req.result.installState, 'installed', 'app downloaded');
resolve()
} else {
req.result.ondownloadapplied = function() {
is(req.result.installState, 'installed', 'app downloaded');
resolve();
}
}
}
req.onerror = reject;
});
}
function checkSwRegistration(aExpectedRegistrations) {
return new Promise((resolve, reject) => {
let registrations = [];
registrations = gServiceWorkerManager.getAllRegistrations();
is(registrations.length, aExpectedRegistrations.length,
"There should be " + aExpectedRegistrations.length + " registrations");
for (let i = 0; i < registrations.length; i++) {
let registration = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
if (!registration) {
reject();
return;
}
is(registration.principal.origin, aExpectedRegistrations[i].origin,
"Registration principal should be as expected");
}
resolve();
});
}
function launchApp() {
if (!gApp) {
ok(false, 'No test application to launch');
return Promise.reject();
}
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/READY/.exec(message)) {
ok(true, 'Message from app: ' + message);
resolve();
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
ok(true, "origin " + gOrigin + gApp.manifest.launch_path);
SpecialPowers.wrap(iframe.contentWindow).location =
gOrigin + gApp.manifest.launch_path;
});
}
function uninstallApp() {
return new Promise((resolve, reject) => {
if (!gApp) {
return reject();
}
let req = navigator.mozApps.mgmt.uninstall(gApp);
req.onsuccess = resolve;
req.onerror = reject;
});
}
function go() {
setup()
.then(installApp)
.then(launchApp)
.then(() => {
app = gAppsService.getAppByManifestURL(gApp.manifestURL);
return checkSwRegistration([{
origin: app.principal.origin
}]);
})
.then(uninstallApp)
.then(() => {
return checkSwRegistration([]);
})
.then(SimpleTest.finish)
.catch((e) => {
ok(false, 'Unexpected error ' + e);
SimpleTest.finish();
});
}
</script>
</pre>
<div id="container"></div>
</body>
</html>

View File

@ -1,193 +0,0 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1161684 - Allow JAR channels to be intercepted by service workers</title>
<script type='text/javascript' src='/tests/SimpleTest/SimpleTest.js'></script>
<link rel='stylesheet' type='text/css' href='/tests/SimpleTest/test.css' />
</head>
<body onload='runTests()'>
<p id='display'></p>
<div id='content' style='display: none'></div>
<pre id='test'></pre>
<script class='testbody' type='application/javascript;version=1.7'>
SimpleTest.waitForExplicitFinish();
const appManifestURL =
'http://mochi.test:8888/tests/dom/workers/test/serviceworkers/app-protocol/update.webapp';
let gApp;
function setup() {
return new Promise((resolve, reject) => {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({'set': [
['dom.mozBrowserFramesEnabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
["dom.serviceWorkers.interception.enabled", true],
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.testing.enabled', true],
['dom.caches.enabled', true],
]}, () => {
SpecialPowers.pushPermissions([
{ 'type': 'webapps-manage', 'allow': 1, 'context': document },
{ 'type': 'browser', 'allow': 1, 'context': document },
{ 'type': 'embed-apps', 'allow': 1, 'context': document }
], () => {
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(resolve);
});
});
});
});
}
function installApp() {
return new Promise((resolve, reject) => {
let req = navigator.mozApps.installPackage(appManifestURL);
req.onsuccess = function() {
gApp = req.result;
is(req.result.manifestURL, appManifestURL, 'app installed');
if (req.result.installState == 'installed') {
is(req.result.installState, 'installed', 'app downloaded');
resolve()
} else {
req.result.ondownloadapplied = function() {
is(req.result.installState, 'installed', 'app downloaded');
resolve();
}
}
}
req.onerror = reject;
});
}
function launchApp() {
if (!gApp) {
ok(false, 'No test application to launch');
return Promise.reject();
}
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/READY/.exec(message)) {
ok(true, "Message from app: " + message);
resolve();
} else {
ok(false, "Unexpected message received: " + message);
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
SpecialPowers.wrap(iframe.contentWindow).location =
gApp.origin + gApp.manifest.launch_path;
});
}
function loadControlled() {
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/HTTPSIFRAMELOADED/.exec(message)) {
let doc = SpecialPowers.wrap(iframe).contentDocument;
let innerDoc = SpecialPowers.wrap(doc.getElementById("redirected-https").contentDocument);
let innerLocation = innerDoc.defaultView.location;
is(innerDoc.domain, "example.org", "Correct domain expected (https)");
is(innerLocation.origin, "https://example.org", "Correct origin expected (https)");
} else if (/IFRAMELOADED/.exec(message)) {
let doc = SpecialPowers.wrap(iframe).contentDocument;
let innerDoc = SpecialPowers.wrap(doc.getElementById("redirected").contentDocument);
let innerLocation = innerDoc.defaultView.location;
is(innerDoc.domain, "example.org", "Correct domain expected");
is(innerLocation.origin, "http://example.org", "Correct origin expected");
} else if (/DONE/.exec(message)) {
ok(true, "Messaging from app complete");
iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
let domParent = document.getElementById('container');
domParent.removeChild(iframe);
resolve();
} else {
ok(false, "Unexpected message received: " + message);
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
SpecialPowers.wrap(iframe.contentWindow).location =
gApp.origin + '/controlled.html';
});
}
function loadUnregister() {
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/KO/.exec(message)) {
ok(false, "Message from app: " + message);
} else if (/DONE/.exec(message)) {
ok(true, "Messaging from app complete");
iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
let domParent = document.getElementById('container');
domParent.removeChild(iframe);
resolve();
} else {
ok(false, "Unexpected message received: " + message);
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
SpecialPowers.wrap(iframe.contentWindow).location =
gApp.origin + '/unregister.html';
});
}
function uninstallApp() {
return new Promise((resolve, reject) => {
if (!gApp) {
return reject();
}
let req = navigator.mozApps.mgmt.uninstall(gApp);
req.onsuccess = resolve;
req.onerror = reject;
});
}
function runTests() {
setup()
.then(installApp)
.then(launchApp)
.then(loadControlled)
.then(loadUnregister)
.then(uninstallApp)
.then(SimpleTest.finish)
.catch((e) => {
ok(false, 'Unexpected error ' + e);
SimpleTest.finish();
});
}
</script>
<div id='container'></div>
</body>
</html>

View File

@ -1,152 +0,0 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id={1233644}
-->
<head>
<title>Test for Bug {1233644}</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1233644}">Mozilla Bug {1233644}</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.7">
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gServiceWorkerManager",
"@mozilla.org/serviceworkers/manager;1",
"nsIServiceWorkerManager");
SimpleTest.waitForExplicitFinish();
const gOrigin = 'http://test/chrome/dom/workers/test/serviceworkers/app3';
const appManifestURL = gOrigin + '/manifest.webapp';
let gApp;
addLoadEvent(go);
function setup() {
info('Setting up');
return new Promise((resolve, reject) => {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.pushPrefEnv({'set': [
['dom.mozBrowserFramesEnabled', true],
['dom.serviceWorkers.exemptFromPerDomainMax', true],
['dom.serviceWorkers.enabled', true],
['dom.serviceWorkers.testing.enabled', true],
['dom.serviceWorkers.interception.enabled', true],
]}, () => {
SpecialPowers.pushPermissions([
{ 'type': 'webapps-manage', 'allow': 1, 'context': document },
{ 'type': 'browser', 'allow': 1, 'context': document },
{ 'type': 'embed-apps', 'allow': 1, 'context': document }
], () => {
SpecialPowers.autoConfirmAppInstall(() => {
SpecialPowers.autoConfirmAppUninstall(resolve);
});
});
});
});
}
function installApp() {
return new Promise((resolve, reject) => {
let req = navigator.mozApps.install(appManifestURL);
req.onsuccess = function() {
gApp = req.result;
is(req.result.manifestURL, appManifestURL, 'app installed');
if (req.result.installState == 'installed') {
is(req.result.installState, 'installed', 'app downloaded');
resolve()
} else {
req.result.ondownloadapplied = function() {
is(req.result.installState, 'installed', 'app downloaded');
resolve();
}
}
}
req.onerror = reject;
});
}
function pushPermission() {
return new Promise((resolve, reject) => {
SpecialPowers.pushPermissions([
{'type': 'browser', 'allow': 1, 'context': {manifestURL: appManifestURL}}], resolve);
});
}
function checkSwRegistration() {
return new Promise((resolve, reject) => {
let registrations = gServiceWorkerManager.getAllRegistrations();
is(registrations.length, 0, "All registrations should be removed.");
resolve();
});
}
function launchApp() {
if (!gApp) {
ok(false, 'No test application to launch');
return Promise.reject();
}
return new Promise((resolve, reject) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.setAttribute('mozapp', gApp.manifestURL);
iframe.addEventListener('mozbrowsershowmodalprompt', function listener(e) {
let message = e.detail.message;
if (/OK/.exec(message)) {
ok(true, "Message from app: " + message);
} else if (/READY/.exec(message)) {
ok(true, 'Message from app: ' + message);
resolve();
}
}, false);
let domParent = document.getElementById('container');
domParent.appendChild(iframe);
SpecialPowers.wrap(iframe.contentWindow).location =
gOrigin + gApp.manifest.launch_path;
});
}
function uninstallApp() {
return new Promise((resolve, reject) => {
if (!gApp) {
return reject();
}
let req = navigator.mozApps.mgmt.uninstall(gApp);
req.onsuccess = resolve;
req.onerror = reject;
});
}
function go() {
setup()
.then(installApp)
.then(pushPermission)
.then(launchApp)
.then(uninstallApp)
.then(checkSwRegistration)
.then(SimpleTest.finish)
.catch((e) => {
ok(false, 'Unexpected error ' + e);
SimpleTest.finish();
});
}
</script>
</pre>
<div id="container"></div>
</body>
</html>

View File

@ -1,166 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
/* 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 "InterceptedJARChannel.h"
#include "nsIPipe.h"
#include "mozilla/dom/ChannelInfo.h"
using namespace mozilla::net;
NS_IMPL_ISUPPORTS(InterceptedJARChannel, nsIInterceptedChannel)
InterceptedJARChannel::InterceptedJARChannel(nsJARChannel* aChannel,
nsINetworkInterceptController* aController)
: mController(aController)
, mChannel(aChannel)
{
}
NS_IMETHODIMP
InterceptedJARChannel::GetResponseBody(nsIOutputStream** aStream)
{
NS_IF_ADDREF(*aStream = mResponseBody);
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
{
NS_ENSURE_ARG(aPolicyType);
nsCOMPtr<nsILoadInfo> loadInfo;
nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_SUCCESS(rv, rv);
*aPolicyType = loadInfo->InternalContentPolicyType();
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::GetChannel(nsIChannel** aChannel)
{
NS_IF_ADDREF(*aChannel = mChannel);
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::GetSecureUpgradedChannelURI(nsIURI** aURI)
{
return mChannel->GetURI(aURI);
}
NS_IMETHODIMP
InterceptedJARChannel::ResetInterception()
{
if (!mChannel) {
return NS_ERROR_NOT_AVAILABLE;
}
mResponseBody = nullptr;
mSynthesizedInput = nullptr;
mChannel->ResetInterception();
mReleaseHandle = nullptr;
mChannel = nullptr;
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::SynthesizeStatus(uint16_t aStatus,
const nsACString& aReason)
{
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::SynthesizeHeader(const nsACString& aName,
const nsACString& aValue)
{
if (aName.LowerCaseEqualsLiteral("content-type")) {
mContentType = aValue;
}
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::FinishSynthesizedResponse(const nsACString& aFinalURLSpec)
{
if (NS_WARN_IF(!mChannel)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (!aFinalURLSpec.IsEmpty()) {
// We don't support rewriting responses for JAR channels where the principal
// needs to be modified.
return NS_ERROR_NOT_IMPLEMENTED;
}
mChannel->OverrideWithSynthesizedResponse(mSynthesizedInput, mContentType);
mResponseBody = nullptr;
mReleaseHandle = nullptr;
mChannel = nullptr;
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::Cancel(nsresult aStatus)
{
MOZ_ASSERT(NS_FAILED(aStatus));
if (!mChannel) {
return NS_ERROR_FAILURE;
}
nsresult rv = mChannel->Cancel(aStatus);
NS_ENSURE_SUCCESS(rv, rv);
mResponseBody = nullptr;
mReleaseHandle = nullptr;
mChannel = nullptr;
return NS_OK;
}
NS_IMETHODIMP
InterceptedJARChannel::SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo)
{
if (!mChannel) {
return NS_ERROR_FAILURE;
}
return aChannelInfo->ResurrectInfoOnChannel(mChannel);
}
NS_IMETHODIMP
InterceptedJARChannel::GetConsoleReportCollector(nsIConsoleReportCollector**)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
InterceptedJARChannel::SetReleaseHandle(nsISupports* aHandle)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mReleaseHandle);
MOZ_ASSERT(aHandle);
mReleaseHandle = aHandle;
return NS_OK;
}
void
InterceptedJARChannel::NotifyController()
{
nsresult rv = NS_NewPipe(getter_AddRefs(mSynthesizedInput),
getter_AddRefs(mResponseBody),
0, UINT32_MAX, true, true);
NS_ENSURE_SUCCESS_VOID(rv);
rv = mController->ChannelIntercepted(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
rv = ResetInterception();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
"Failed to resume intercepted network request");
}
mController = nullptr;
}

View File

@ -1,63 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set expandtab ts=2 sw=2 sts=2 cin: */
/* 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/. */
#ifndef InterceptedJARChannel_h
#define InterceptedJARChannel_h
#include "nsJAR.h"
#include "nsJARChannel.h"
#include "nsIInputStream.h"
#include "nsIInputStreamPump.h"
#include "nsINetworkInterceptController.h"
#include "nsIOutputStream.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Maybe.h"
class nsIStreamListener;
class nsJARChannel;
namespace mozilla {
namespace net {
// An object representing a channel that has been intercepted. This avoids
// complicating the actual channel implementation with the details of
// synthesizing responses.
class InterceptedJARChannel : public nsIInterceptedChannel
{
// The interception controller to notify about the successful channel
// interception.
nsCOMPtr<nsINetworkInterceptController> mController;
// The actual channel being intercepted.
RefPtr<nsJARChannel> mChannel;
// Reader-side of the synthesized response body.
nsCOMPtr<nsIInputStream> mSynthesizedInput;
// The stream to write the body of the synthesized response.
nsCOMPtr<nsIOutputStream> mResponseBody;
nsCOMPtr<nsISupports> mReleaseHandle;
// The content type of the synthesized response.
nsCString mContentType;
virtual ~InterceptedJARChannel() {};
public:
InterceptedJARChannel(nsJARChannel* aChannel,
nsINetworkInterceptController* aController);
NS_DECL_ISUPPORTS
NS_DECL_NSIINTERCEPTEDCHANNEL
void NotifyController();
};
} // namespace net
} // namespace mozilla
#endif // InterceptedJARChannel_h

View File

@ -23,14 +23,12 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'jar'
EXPORTS += [
'InterceptedJARChannel.h',
'nsJARURI.h',
'nsZipArchive.h',
'zipstruct.h',
]
UNIFIED_SOURCES += [
'InterceptedJARChannel.cpp',
'nsJARProtocolHandler.cpp',
'nsJARURI.cpp',
]

View File

@ -25,8 +25,6 @@
#include "mozilla/net/RemoteOpenFileChild.h"
#include "nsITabChild.h"
#include "private/pprio.h"
#include "nsINetworkInterceptController.h"
#include "InterceptedJARChannel.h"
#include "nsInputStreamPump.h"
using namespace mozilla;
@ -203,7 +201,6 @@ nsJARChannel::nsJARChannel()
, mIsPending(false)
, mIsUnsafe(true)
, mOpeningRemote(false)
, mSynthesizedStreamLength(0)
, mBlockRemoteFiles(false)
{
if (!gJarProtocolLog)
@ -521,8 +518,6 @@ nsJARChannel::GetStatus(nsresult *status)
{
if (mPump && NS_SUCCEEDED(mStatus))
mPump->GetStatus(status);
else if (mSynthesizedResponsePump && NS_SUCCEEDED(mStatus))
mSynthesizedResponsePump->GetStatus(status);
else
*status = mStatus;
return NS_OK;
@ -534,8 +529,6 @@ nsJARChannel::Cancel(nsresult status)
mStatus = status;
if (mPump)
return mPump->Cancel(status);
if (mSynthesizedResponsePump)
return mSynthesizedResponsePump->Cancel(status);
NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
return NS_OK;
@ -546,8 +539,6 @@ nsJARChannel::Suspend()
{
if (mPump)
return mPump->Suspend();
if (mSynthesizedResponsePump)
return mSynthesizedResponsePump->Suspend();
NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
return NS_OK;
@ -558,8 +549,6 @@ nsJARChannel::Resume()
{
if (mPump)
return mPump->Resume();
if (mSynthesizedResponsePump)
return mSynthesizedResponsePump->Resume();
NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
return NS_OK;
@ -863,78 +852,6 @@ nsJARChannel::Open2(nsIInputStream** aStream)
return Open(aStream);
}
bool
nsJARChannel::BypassServiceWorker() const
{
return mLoadFlags & LOAD_BYPASS_SERVICE_WORKER;
}
bool
nsJARChannel::ShouldIntercept()
{
LOG(("nsJARChannel::ShouldIntercept [this=%x]\n", this));
// We only intercept app:// requests
if (!mAppURI) {
return false;
}
nsCOMPtr<nsINetworkInterceptController> controller;
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
NS_GET_IID(nsINetworkInterceptController),
getter_AddRefs(controller));
bool shouldIntercept = false;
if (controller && !BypassServiceWorker() && mLoadInfo) {
nsresult rv = controller->ShouldPrepareForIntercept(mAppURI,
nsContentUtils::IsNonSubresourceRequest(this),
&shouldIntercept);
NS_ENSURE_SUCCESS(rv, false);
}
return shouldIntercept;
}
void nsJARChannel::ResetInterception()
{
LOG(("nsJARChannel::ResetInterception [this=%x]\n", this));
// Continue with the original request.
nsresult rv = ContinueAsyncOpen();
NS_ENSURE_SUCCESS_VOID(rv);
}
void
nsJARChannel::OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput,
const nsACString& aContentType)
{
// In our current implementation, the FetchEvent handler will copy the
// response stream completely into the pipe backing the input stream so we
// can treat the available as the length of the stream.
uint64_t available;
nsresult rv = aSynthesizedInput->Available(&available);
if (NS_WARN_IF(NS_FAILED(rv))) {
mSynthesizedStreamLength = -1;
} else {
mSynthesizedStreamLength = int64_t(available);
}
rv = nsInputStreamPump::Create(getter_AddRefs(mSynthesizedResponsePump),
aSynthesizedInput,
int64_t(-1), int64_t(-1), 0, 0, true);
if (NS_WARN_IF(NS_FAILED(rv))) {
aSynthesizedInput->Close();
return;
}
SetContentType(aContentType);
mIsUnsafe = false;
FinishAsyncOpen();
rv = mSynthesizedResponsePump->AsyncRead(this, nullptr);
NS_ENSURE_SUCCESS_VOID(rv);
}
NS_IMETHODIMP
nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
{
@ -961,48 +878,6 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
mListenerContext = ctx;
mIsPending = true;
// Bug 1171651 - Disable the interception of app:// URIs in service workers
// on release builds
#ifndef RELEASE_BUILD
// Check if this channel should intercept the network request and prepare
// for a possible synthesized response instead.
if (ShouldIntercept()) {
nsCOMPtr<nsINetworkInterceptController> controller;
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
NS_GET_IID(nsINetworkInterceptController),
getter_AddRefs(controller));
RefPtr<InterceptedJARChannel> intercepted =
new InterceptedJARChannel(this, controller);
intercepted->NotifyController();
// We get the JAREntry so we can infer the content type later in case
// that it isn't provided along with the synthesized response.
nsresult rv = mJarURI->GetJAREntry(mJarEntry);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
#endif
return ContinueAsyncOpen();
}
NS_IMETHODIMP
nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
{
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return AsyncOpen(listener, nullptr);
}
nsresult
nsJARChannel::ContinueAsyncOpen()
{
LOG(("nsJARChannel::ContinueAsyncOpen [this=%x]\n", this));
nsresult rv = LookupFile(true);
if (NS_FAILED(rv)) {
mIsPending = false;
@ -1057,19 +932,21 @@ nsJARChannel::ContinueAsyncOpen()
return rv;
}
FinishAsyncOpen();
return NS_OK;
}
void
nsJARChannel::FinishAsyncOpen()
{
if (mLoadGroup)
mLoadGroup->AddRequest(this, nullptr);
mOpened = true;
return NS_OK;
}
NS_IMETHODIMP
nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
{
nsCOMPtr<nsIStreamListener> listener = aListener;
nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
NS_ENSURE_SUCCESS(rv, rv);
return AsyncOpen(listener, nullptr);
}
//-----------------------------------------------------------------------------

View File

@ -10,7 +10,6 @@
#include "nsIJARChannel.h"
#include "nsIJARURI.h"
#include "nsIInputStreamPump.h"
#include "InterceptedJARChannel.h"
#include "nsIInterfaceRequestor.h"
#include "nsIProgressEventSink.h"
#include "nsIStreamListener.h"
@ -30,12 +29,6 @@
class nsJARInputThunk;
class nsInputStreamPump;
namespace mozilla {
namespace net {
class InterceptedJARChannel;
} // namespace net
} // namespace mozilla
//-----------------------------------------------------------------------------
class nsJARChannel final : public nsIJARChannel
@ -80,23 +73,6 @@ private:
mozilla::net::MemoryDownloader::Data aData)
override;
bool BypassServiceWorker() const;
// Returns true if this channel should intercept the network request and
// prepare for a possible synthesized response instead.
bool ShouldIntercept();
nsresult ContinueAsyncOpen();
void FinishAsyncOpen();
// Discard the prior interception and continue with the original network
// request.
void ResetInterception();
// Override this channel's pending response with a synthesized one. The
// content will be asynchronously read from the pump.
void OverrideWithSynthesizedResponse(nsIInputStream* aSynthesizedInput,
const nsACString& aContentType);
nsCString mSpec;
bool mOpened;
@ -135,12 +111,8 @@ private:
nsCString mJarEntry;
nsCString mInnerJarEntry;
RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
int64_t mSynthesizedStreamLength;
// True if this channel should not download any remote files.
bool mBlockRemoteFiles;
friend class mozilla::net::InterceptedJARChannel;
};
#endif // nsJARChannel_h__

View File

@ -15,6 +15,7 @@
#include "nsXULAppAPI.h"
#include "nsPrincipal.h"
#include "nsContentSecurityManager.h"
#include "nsContentUtils.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/Preferences.h"