Bug 1151495 - Support permission prompting from workers for IDB, r=bent

This commit is contained in:
Andrea Marchesini 2015-05-06 09:07:57 +01:00
Родитель 65f9cd87c9
Коммит f2ba43b3fd
7 изменённых файлов: 444 добавлений и 1 удалений

Просмотреть файл

@ -40,6 +40,8 @@
#include "PermissionRequestBase.h"
#include "ProfilerHelpers.h"
#include "ReportInternalError.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#ifdef DEBUG
#include "IndexedDatabaseManager.h"
@ -57,7 +59,13 @@
#endif // DEBUG || GC_ON_IPC_MESSAGES
namespace mozilla {
using ipc::PrincipalInfo;
namespace dom {
using namespace workers;
namespace indexedDB {
/*******************************************************************************
@ -777,6 +785,235 @@ DispatchSuccessEvent(ResultHelper* aResultHelper,
}
}
class WorkerPermissionChallenge;
// This class calles WorkerPermissionChallenge::OperationCompleted() in the
// worker thread.
class WorkerPermissionOperationCompleted final : public WorkerRunnable
{
nsRefPtr<WorkerPermissionChallenge> mChallenge;
public:
WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate,
WorkerPermissionChallenge* aChallenge)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mChallenge(aChallenge)
{
MOZ_ASSERT(NS_IsMainThread());
}
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
};
// This class used to do prompting in the main thread and main process.
class WorkerPermissionRequest final : public PermissionRequestBase
{
nsRefPtr<WorkerPermissionChallenge> mChallenge;
public:
WorkerPermissionRequest(Element* aElement,
nsIPrincipal* aPrincipal,
WorkerPermissionChallenge* aChallenge)
: PermissionRequestBase(aElement, aPrincipal)
, mChallenge(aChallenge)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aChallenge);
}
private:
~WorkerPermissionRequest()
{
MOZ_ASSERT(NS_IsMainThread());
}
virtual void
OnPromptComplete(PermissionValue aPermissionValue) override;
};
// This class is used in the main thread of all child processes.
class WorkerPermissionRequestChildProcessActor final
: public PIndexedDBPermissionRequestChild
{
nsRefPtr<WorkerPermissionChallenge> mChallenge;
public:
explicit WorkerPermissionRequestChildProcessActor(
WorkerPermissionChallenge* aChallenge)
: mChallenge(aChallenge)
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aChallenge);
}
protected:
~WorkerPermissionRequestChildProcessActor()
{}
virtual bool
Recv__delete__(const uint32_t& aPermission) override;
};
class WorkerPermissionChallenge final : public nsRunnable
, public WorkerFeature
{
public:
WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
BackgroundFactoryRequestChild* aActor,
IDBFactory* aFactory,
const PrincipalInfo& aPrincipalInfo)
: mWorkerPrivate(aWorkerPrivate)
, mActor(aActor)
, mFactory(aFactory)
, mPrincipalInfo(aPrincipalInfo)
{
MOZ_ASSERT(mWorkerPrivate);
MOZ_ASSERT(aActor);
MOZ_ASSERT(aFactory);
mWorkerPrivate->AssertIsOnWorkerThread();
}
NS_IMETHOD
Run() override
{
bool completed = RunInternal();
if (completed) {
OperationCompleted();
}
return NS_OK;
}
virtual bool
Notify(JSContext* aCx, workers::Status aStatus) override
{
// We don't care about the notification. We just want to keep the
// mWorkerPrivate alive.
return true;
}
void
OperationCompleted()
{
if (NS_IsMainThread()) {
nsRefPtr<WorkerPermissionOperationCompleted> runnable =
new WorkerPermissionOperationCompleted(mWorkerPrivate, this);
if (!runnable->Dispatch(nullptr)) {
NS_WARNING("Failed to dispatch a runnable to the worker thread.");
return;
}
return;
}
MOZ_ASSERT(mActor);
mActor->AssertIsOnOwningThread();
MaybeCollectGarbageOnIPCMessage();
nsRefPtr<IDBFactory> factory;
mFactory.swap(factory);
mActor->SendPermissionRetry();
mActor = nullptr;
mWorkerPrivate->AssertIsOnWorkerThread();
JSContext* cx = mWorkerPrivate->GetJSContext();
mWorkerPrivate->RemoveFeature(cx, this);
}
private:
bool
RunInternal()
{
MOZ_ASSERT(NS_IsMainThread());
// Walk up to our containing page
WorkerPrivate* wp = mWorkerPrivate;
while (wp->GetParent()) {
wp = wp->GetParent();
}
nsPIDOMWindow* window = wp->GetWindow();
if (!window) {
return true;
}
nsresult rv;
nsCOMPtr<nsIPrincipal> principal =
mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return true;
}
if (XRE_GetProcessType() == GeckoProcessType_Default) {
nsCOMPtr<Element> ownerElement =
do_QueryInterface(window->GetChromeEventHandler());
if (NS_WARN_IF(!ownerElement)) {
return true;
}
nsRefPtr<WorkerPermissionRequest> helper =
new WorkerPermissionRequest(ownerElement, principal, this);
PermissionRequestBase::PermissionValue permission;
if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) {
return true;
}
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
permission == PermissionRequestBase::kPermissionDenied ||
permission == PermissionRequestBase::kPermissionPrompt);
return permission != PermissionRequestBase::kPermissionPrompt;
}
TabChild* tabChild = TabChild::GetFrom(window);
MOZ_ASSERT(tabChild);
IPC::Principal ipcPrincipal(principal);
auto* actor = new WorkerPermissionRequestChildProcessActor(this);
tabChild->SendPIndexedDBPermissionRequestConstructor(actor, ipcPrincipal);
return false;
}
private:
WorkerPrivate* mWorkerPrivate;
BackgroundFactoryRequestChild* mActor;
nsRefPtr<IDBFactory> mFactory;
PrincipalInfo mPrincipalInfo;
};
void
WorkerPermissionRequest::OnPromptComplete(PermissionValue aPermissionValue)
{
MOZ_ASSERT(NS_IsMainThread());
mChallenge->OperationCompleted();
}
bool
WorkerPermissionOperationCompleted::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->AssertIsOnWorkerThread();
mChallenge->OperationCompleted();
return true;
}
bool
WorkerPermissionRequestChildProcessActor::Recv__delete__(
const uint32_t& /* aPermission */)
{
MOZ_ASSERT(NS_IsMainThread());
mChallenge->OperationCompleted();
return true;
}
} // anonymous namespace
/*******************************************************************************
@ -1124,7 +1361,23 @@ BackgroundFactoryRequestChild::RecvPermissionChallenge(
MaybeCollectGarbageOnIPCMessage();
if (!NS_IsMainThread()) {
MOZ_CRASH("Implement me for workers!");
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<WorkerPermissionChallenge> challenge =
new WorkerPermissionChallenge(workerPrivate, this, mFactory,
aPrincipalInfo);
JSContext* cx = workerPrivate->GetJSContext();
MOZ_ASSERT(cx);
if (!workerPrivate->AddFeature(cx, challenge)) {
return false;
}
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(challenge)));
return true;
}
nsresult rv;

Просмотреть файл

@ -6,11 +6,16 @@ support-files =
browser_forgetThisSiteGet.html
browserHelpers.js
browser_permissionsPrompt.html
browser_permissionsSharedWorker.html
browser_permissionsSharedWorker.js
browser_permissionsWorker.html
browser_permissionsWorker.js
bug839193.js
bug839193.xul
[browser_forgetThisSite.js]
[browser_permissionsPromptAllow.js]
[browser_permissionsPromptDeny.js]
[browser_permissionsPromptWorker.js]
[browser_perwindow_privateBrowsing.js]
[browser_bug839193.js]

Просмотреть файл

@ -0,0 +1,91 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const testWorkerURL = "http://mochi.test:8888/browser/" +
"dom/indexedDB/test/browser_permissionsWorker.html";
const testSharedWorkerURL = "http://mochi.test:8888/browser/" +
"dom/indexedDB/test/browser_permissionsSharedWorker.html";
const notificationID = "indexedDB-permissions-prompt";
function test()
{
waitForExplicitFinish();
executeSoon(test1);
}
function test1()
{
// We want a prompt.
removePermission(testWorkerURL, "indexedDB");
info("creating tab");
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function () {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
setFinishedCallback(function(isIDBDatabase, exception) {
ok(isIDBDatabase, "First database creation was successful");
ok(!exception, "No exception");
is(getPermission(testWorkerURL, "indexedDB"),
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
"Correct permission set");
gBrowser.removeCurrentTab();
executeSoon(test2);
});
registerPopupEventHandler("popupshowing", function () {
ok(true, "prompt showing");
});
registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown");
triggerMainCommand(this);
});
registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden");
});
}, true);
info("loading test page: " + testWorkerURL);
content.location = testWorkerURL;
}
function test2()
{
// We want a prompt.
removePermission(testSharedWorkerURL, "indexedDB");
info("creating tab");
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function () {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
setFinishedCallback(function(isIDBDatabase, exception) {
ok(!isIDBDatabase, "First database creation was successful");
ok(exception, "No exception");
is(getPermission(testSharedWorkerURL, "indexedDB"),
Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION,
"Correct permission set");
gBrowser.removeCurrentTab();
executeSoon(finish);
});
registerPopupEventHandler("popupshowing", function () {
ok(false, "prompt showing");
});
registerPopupEventHandler("popupshown", function () {
ok(false, "prompt shown");
});
registerPopupEventHandler("popuphidden", function () {
ok(false, "prompt hidden");
});
}, true);
info("loading test page: " + testSharedWorkerURL);
content.location = testSharedWorkerURL;
}

Просмотреть файл

@ -0,0 +1,34 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Test</title>
<script type="text/javascript;version=1.7">
let testIsIDBDatabase;
let testException;
function runTest() {
let w = new SharedWorker('browser_permissionsSharedWorker.js');
w.port.onmessage = function(e) {
if (e.data.status == 'success') {
testIsIDBDatabase = e.data.isIDBDatabase;
} else {
testException = e.data.error;
}
setTimeout(testFinishedCallback, 0, testIsIDBDatabase, testException);
}
const name = window.location.pathname + "_sharedWorker";
w.port.postMessage(name);
}
</script>
</head>
<body onload="runTest();"></body>
</html>

Просмотреть файл

@ -0,0 +1,14 @@
onconnect = function(e) {
e.ports[0].onmessage = function(e) {
var request = indexedDB.open(e.data, { version: 1,
storage: "persistent" });
request.onsuccess = function(event) {
e.target.postMessage({ status: 'success',
isIDBDatabase: (event.target.result instanceof IDBDatabase) });
}
request.onerror = function(event) {
e.target.postMessage({ status: 'error', error: event.target.error.name });
}
}
}

Просмотреть файл

@ -0,0 +1,34 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Test</title>
<script type="text/javascript;version=1.7">
let testIsIDBDatabase;
let testException;
function runTest() {
let w = new Worker('browser_permissionsWorker.js');
w.onmessage = function(e) {
if (e.data.status == 'success') {
testIsIDBDatabase = e.data.isIDBDatabase;
} else {
testException = e.data.error;
}
setTimeout(testFinishedCallback, 0, testIsIDBDatabase, testException);
}
const name = window.location.pathname;
w.postMessage(name);
}
</script>
</head>
<body onload="runTest();"></body>
</html>

Просмотреть файл

@ -0,0 +1,12 @@
onmessage = function(e) {
var request = indexedDB.open(e.data, { version: 1,
storage: "persistent" });
request.onsuccess = function(event) {
postMessage({ status: 'success',
isIDBDatabase: (event.target.result instanceof IDBDatabase) });
}
request.onerror = function(event) {
postMessage({ status: 'error', error: event.target.error.name });
}
}