Bug 1177621 - SharedWorkers should not be shared between a private and a non-private documents, r=nsm

This commit is contained in:
Andrea Marchesini 2015-06-26 11:18:18 -07:00
parent 8d5f94de1b
commit 2e26b7957b
17 changed files with 291 additions and 19 deletions

View File

@ -14027,6 +14027,11 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
return NS_OK;
}
// No in private browsing
if (mInPrivateBrowsing) {
return NS_OK;
}
if (mSandboxFlags) {
// If we're sandboxed, don't intercept.
return NS_OK;

View File

@ -2989,18 +2989,29 @@ nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
if (!aDoc) {
return false;
}
bool isPrivate = false;
nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
nsCOMPtr<nsIInterfaceRequestor> callbacks;
if (loadGroup) {
loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
isPrivate = loadContext && loadContext->UsePrivateBrowsing();
}
} else {
nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
isPrivate = channel && NS_UsePrivateBrowsing(channel);
return IsInPrivateBrowsing(loadGroup);
}
nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
return channel && NS_UsePrivateBrowsing(channel);
}
// static
bool
nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup)
{
if (!aLoadGroup) {
return false;
}
bool isPrivate = false;
nsCOMPtr<nsIInterfaceRequestor> callbacks;
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
isPrivate = loadContext && loadContext->UsePrivateBrowsing();
}
return isPrivate;
}

View File

@ -69,6 +69,7 @@ class nsIImageLoadingContent;
class nsIInterfaceRequestor;
class nsIIOService;
class nsILineBreaker;
class nsILoadGroup;
class nsIMessageBroadcaster;
class nsNameSpaceManager;
class nsIObserver;
@ -730,6 +731,11 @@ public:
*/
static bool IsInPrivateBrowsing(nsIDocument* aDoc);
/**
* Returns true if this loadGroup uses Private Browsing.
*/
static bool IsInPrivateBrowsing(nsILoadGroup* aLoadGroup);
/**
* If aNode is not an element, return true exactly when aContent's binding
* parent is null.

View File

@ -405,7 +405,7 @@ partial interface Navigator {
// Service Workers/Navigation Controllers
partial interface Navigator {
[Pref="dom.serviceWorkers.enabled"]
[Func="ServiceWorkerContainer::IsEnabled"]
readonly attribute ServiceWorkerContainer serviceWorker;
};

View File

@ -8,7 +8,7 @@
*
*/
[Pref="dom.serviceWorkers.enabled",
[Func="ServiceWorkerContainer::IsEnabled",
Exposed=Window]
interface ServiceWorkerContainer : EventTarget {
// FIXME(nsm):

View File

@ -276,7 +276,7 @@ GetWorkerPref(const nsACString& aPref,
void
GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName,
const nsACString& aCacheName, WorkerType aWorkerType,
nsCString& aKey)
bool aPrivateBrowsing, nsCString& aKey)
{
aKey.Truncate();
NS_NAMED_LITERAL_CSTRING(sharedPrefix, "shared|");
@ -286,10 +286,12 @@ GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName,
aWorkerType == WorkerTypeService);
MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared, aCacheName.IsEmpty());
MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aCacheName.IsEmpty());
MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aPrivateBrowsing);
aKey.SetCapacity(servicePrefix.Length() + aScriptSpec.Length() +
aName.Length() + aCacheName.Length() + 1);
aName.Length() + aCacheName.Length() + 3);
aKey.Append(aWorkerType == WorkerTypeService ? servicePrefix : sharedPrefix);
aKey.Append(aPrivateBrowsing ? "1|" : "0|");
nsACString::const_iterator start, end;
aName.BeginReading(start);
@ -1517,7 +1519,8 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
nsAutoCString key;
GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName,
cacheName, aWorkerPrivate->Type(), key);
cacheName, aWorkerPrivate->Type(),
aWorkerPrivate->IsInPrivateBrowsing(), key);
MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key));
SharedWorkerInfo* sharedWorkerInfo =
@ -1631,7 +1634,8 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
EmptyCString();
GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
match.mSharedWorkerInfo->mName,
cacheName, aWorkerPrivate->Type(), key);
cacheName, aWorkerPrivate->Type(),
aWorkerPrivate->IsInPrivateBrowsing(), key);
domainInfo->mSharedWorkerInfos.Remove(key);
}
}
@ -2387,7 +2391,7 @@ RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
nsAutoCString key;
GenerateSharedWorkerKey(scriptSpec, aName,
NS_ConvertUTF16toUTF8(aLoadInfo->mServiceWorkerCacheName),
aType, key);
aType, aLoadInfo->mPrivateBrowsing, key);
if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) &&
domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) {
@ -2467,7 +2471,8 @@ RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
EmptyCString();
GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
match.mSharedWorkerInfo->mName,
cacheName, aWorkerPrivate->Type(), key);
cacheName, aWorkerPrivate->Type(),
aWorkerPrivate->IsInPrivateBrowsing(), key);
domainInfo->mSharedWorkerInfos.Remove(key);
}
}

View File

@ -15,6 +15,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ServiceWorkerContainerBinding.h"
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
@ -33,6 +34,25 @@ NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
mControllerWorker, mReadyPromise)
/* static */ bool
ServiceWorkerContainer::IsEnabled(JSContext* aCx, JSObject* aGlobal)
{
MOZ_ASSERT(NS_IsMainThread());
JS::Rooted<JSObject*> global(aCx, aGlobal);
nsCOMPtr<nsPIDOMWindow> window = Navigator::GetWindowFromGlobal(global);
if (!window) {
return false;
}
nsIDocument* doc = window->GetExtantDoc();
if (!doc || nsContentUtils::IsInPrivateBrowsing(doc)) {
return false;
}
return Preferences::GetBool("dom.serviceWorkers.enabled", false);
}
ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindow* aWindow)
: DOMEventTargetHelper(aWindow)
{

View File

@ -33,6 +33,8 @@ public:
IMPL_EVENT_HANDLER(error)
IMPL_EVENT_HANDLER(message)
static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow);
virtual JSObject*

View File

@ -3612,7 +3612,7 @@ ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttribut
bool
ServiceWorkerManager::IsAvailable(const OriginAttributes& aOriginAttributes,
nsIURI* aURI)
nsIURI* aURI)
{
MOZ_ASSERT(aURI);
@ -3625,6 +3625,7 @@ bool
ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
{
MOZ_ASSERT(aDoc);
MOZ_ASSERT(!nsContentUtils::IsInPrivateBrowsing(aDoc));
nsRefPtr<ServiceWorkerRegistrationInfo> registration;
nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
@ -3747,6 +3748,7 @@ ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
info.mIndexedDBAllowed =
indexedDB::IDBFactory::AllowedForPrincipal(aPrincipal);
info.mPrivateBrowsing = false;
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = aPrincipal->GetCsp(getter_AddRefs(csp));

View File

@ -2336,6 +2336,7 @@ WorkerLoadInfo::WorkerLoadInfo()
, mIsInPrivilegedApp(false)
, mIsInCertifiedApp(false)
, mIndexedDBAllowed(false)
, mPrivateBrowsing(true)
{
MOZ_COUNT_CTOR(WorkerLoadInfo);
}
@ -2390,6 +2391,7 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
mIsInCertifiedApp = aOther.mIsInCertifiedApp;
mIndexedDBAllowed = aOther.mIndexedDBAllowed;
mPrivateBrowsing = aOther.mPrivateBrowsing;
}
template <class Derived>
@ -4163,6 +4165,7 @@ WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal,
mLoadInfo.mLoadGroup = aLoadGroup;
mLoadInfo.mPrincipalInfo = new PrincipalInfo();
mLoadInfo.mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(aLoadGroup);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
@ -4964,6 +4967,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
loadInfo.mFromWindow = aParent->IsFromWindow();
loadInfo.mWindowID = aParent->WindowID();
loadInfo.mIndexedDBAllowed = aParent->IsIndexedDBAllowed();
loadInfo.mPrivateBrowsing = aParent->IsInPrivateBrowsing();
} else {
AssertIsOnMainThread();
@ -5083,6 +5087,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
loadInfo.mFromWindow = true;
loadInfo.mWindowID = globalWindow->WindowID();
loadInfo.mIndexedDBAllowed = IDBFactory::AllowedForWindow(globalWindow);
loadInfo.mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(document);
} else {
// Not a window
MOZ_ASSERT(isChrome);
@ -5124,6 +5129,7 @@ WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
loadInfo.mFromWindow = false;
loadInfo.mWindowID = UINT64_MAX;
loadInfo.mIndexedDBAllowed = true;
loadInfo.mPrivateBrowsing = false;
}
MOZ_ASSERT(loadInfo.mPrincipal);

View File

@ -753,6 +753,12 @@ public:
return mLoadInfo.mIndexedDBAllowed;
}
bool
IsInPrivateBrowsing() const
{
return mLoadInfo.mPrivateBrowsing;
}
void
GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers);

View File

@ -259,6 +259,7 @@ struct WorkerLoadInfo
bool mIsInPrivilegedApp;
bool mIsInCertifiedApp;
bool mIndexedDBAllowed;
bool mPrivateBrowsing;
WorkerLoadInfo();
~WorkerLoadInfo();

View File

@ -48,6 +48,8 @@ support-files =
workersDisabled_worker.js
file_url.jsm
bug1062920_worker.js
empty.html
sharedWorker_privateBrowsing.js
[test_WorkerDebugger.xul]
[test_WorkerDebugger.initialize.xul]
@ -76,3 +78,4 @@ support-files =
[test_workersDisabled.xul]
[test_url.xul]
[test_bug1062920.xul]
[test_sharedWorker_privateBrowsing.html]

View File

@ -4,3 +4,4 @@ support-files =
app/*
[test_app_installation.html]
[test_privateBrowsing.html]

View File

@ -0,0 +1,93 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for ServiceWorker - Private Browsing</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"?>
</head>
<body>
<script type="application/javascript">
const Ci = Components.interfaces;
var mainWindow;
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefBranch.setIntPref("browser.startup.page", 0);
prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
function testOnWindow(aIsPrivate, aCallback) {
var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
win.addEventListener("DOMContentLoaded", function onInnerLoad() {
if (win.content.location.href != contentPage) {
win.gBrowser.loadURI(contentPage);
return;
}
win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
SimpleTest.executeSoon(function() { aCallback(win); });
}, true);
if (!aIsPrivate) {
win.gBrowser.loadURI(contentPage);
}
}, true);
}
function setupWindow() {
mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
runTest();
}
var wN;
var wP;
function doTests() {
testOnWindow(false, function(aWin) {
wN = aWin;
ok("serviceWorker" in wN.content.navigator, "ServiceWorkers are available for normal windows");
testOnWindow(true, function(aWin) {
wP = aWin;
ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows");
SimpleTest.finish();
});
});
}
var steps = [
setupWindow,
doTests
];
function runTest() {
if (!steps.length) {
wN.close();
wP.close();
prefBranch.clearUserPref("browser.startup.page")
prefBranch.clearUserPref("browser.startup.homepage_override.mstone");
SimpleTest.finish();
return;
}
var step = steps.shift();
step();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.serviceWorkers.enabled", true]]}, runTest);
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
var counter = 0;
onconnect = function(evt) {
evt.ports[0].postMessage(++counter);
}

View File

@ -0,0 +1,106 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for SharedWorker - Private Browsing</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"?>
</head>
<body>
<script type="application/javascript">
const Ci = Components.interfaces;
var mainWindow;
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefBranch.setIntPref("browser.startup.page", 0);
prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
function testOnWindow(aIsPrivate, aCallback) {
var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
win.addEventListener("DOMContentLoaded", function onInnerLoad() {
if (win.content.location.href != contentPage) {
win.gBrowser.loadURI(contentPage);
return;
}
win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
SimpleTest.executeSoon(function() { aCallback(win); });
}, true);
if (!aIsPrivate) {
win.gBrowser.loadURI(contentPage);
}
}, true);
}
function setupWindow() {
mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
runTest();
}
var wN;
var wP;
function doTests() {
testOnWindow(false, function(aWin) {
wN = aWin;
testOnWindow(true, function(aWin) {
wP = aWin;
var sharedWorker1 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
sharedWorker1.port.onmessage = function(event) {
is(event.data, 1, "Only 1 sharedworker expected in the private window");
var sharedWorker2 = new wN.content.SharedWorker('sharedWorker_privateBrowsing.js');
sharedWorker2.port.onmessage = function(event) {
is(event.data, 1, "Only 1 sharedworker expected in the normal window");
var sharedWorker3 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
sharedWorker3.port.onmessage = function(event) {
is(event.data, 2, "Only 2 sharedworker expected in the private window");
SimpleTest.finish();
}
}
}
});
});
}
var steps = [
setupWindow,
doTests
];
function runTest() {
if (!steps.length) {
wN.close();
wP.close();
prefBranch.clearUserPref("browser.startup.page")
prefBranch.clearUserPref("browser.startup.homepage_override.mstone");
SimpleTest.finish();
return;
}
var step = steps.shift();
step();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.workers.sharedWorkers.enabled", true]]}, runTest);
</script>
</body>
</html>