зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1151495
- Support permission prompting from workers for IDB, r=bent
This commit is contained in:
Родитель
65f9cd87c9
Коммит
f2ba43b3fd
|
@ -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 });
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче