mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1354500 - Part 3: Remove indexedDB/PermissionRequestBase r=dom-storage-reviewers,asuth
Differential Revision: https://phabricator.services.mozilla.com/D151418
This commit is contained in:
parent
cdb7d00f1e
commit
17bf285cb0
@ -52,7 +52,6 @@
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "PermissionRequestBase.h"
|
||||
#include "ProfilerHelpers.h"
|
||||
#include "ReportInternalError.h"
|
||||
#include "ThreadLocal.h"
|
||||
@ -328,30 +327,6 @@ nsresult GetResult(JSContext* aCx, const nsTArray<Key>* aKeys,
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
class PermissionRequestMainProcessHelper final : public PermissionRequestBase {
|
||||
BackgroundFactoryRequestChild* mActor;
|
||||
SafeRefPtr<IDBFactory> mFactory;
|
||||
|
||||
public:
|
||||
PermissionRequestMainProcessHelper(BackgroundFactoryRequestChild* aActor,
|
||||
SafeRefPtr<IDBFactory> aFactory,
|
||||
Element* aOwnerElement,
|
||||
nsIPrincipal* aPrincipal)
|
||||
: PermissionRequestBase(aOwnerElement, aPrincipal),
|
||||
mActor(aActor),
|
||||
mFactory(std::move(aFactory)) {
|
||||
MOZ_ASSERT(aActor);
|
||||
MOZ_ASSERT(mFactory);
|
||||
aActor->AssertIsOnOwningThread();
|
||||
}
|
||||
|
||||
protected:
|
||||
~PermissionRequestMainProcessHelper() = default;
|
||||
|
||||
private:
|
||||
virtual void OnPromptComplete(PermissionValue aPermissionValue) override;
|
||||
};
|
||||
|
||||
auto DeserializeStructuredCloneFiles(
|
||||
IDBDatabase* aDatabase,
|
||||
const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles,
|
||||
@ -654,178 +629,6 @@ PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) {
|
||||
return fileDesc;
|
||||
}
|
||||
|
||||
class WorkerPermissionChallenge;
|
||||
|
||||
// This class calles WorkerPermissionChallenge::OperationCompleted() in the
|
||||
// worker thread.
|
||||
class WorkerPermissionOperationCompleted final : public WorkerControlRunnable {
|
||||
RefPtr<WorkerPermissionChallenge> mChallenge;
|
||||
|
||||
public:
|
||||
WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate,
|
||||
WorkerPermissionChallenge* aChallenge)
|
||||
: WorkerControlRunnable(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 {
|
||||
RefPtr<WorkerPermissionChallenge> mChallenge;
|
||||
|
||||
public:
|
||||
WorkerPermissionRequest(Element* aElement, nsIPrincipal* aPrincipal,
|
||||
WorkerPermissionChallenge* aChallenge)
|
||||
: PermissionRequestBase(aElement, aPrincipal), mChallenge(aChallenge) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aChallenge);
|
||||
}
|
||||
|
||||
private:
|
||||
~WorkerPermissionRequest() { MOZ_ASSERT(NS_IsMainThread()); }
|
||||
|
||||
virtual void OnPromptComplete(PermissionValue aPermissionValue) override;
|
||||
};
|
||||
|
||||
class WorkerPermissionChallenge final : public Runnable {
|
||||
public:
|
||||
WorkerPermissionChallenge(WorkerPrivate* aWorkerPrivate,
|
||||
BackgroundFactoryRequestChild* aActor,
|
||||
SafeRefPtr<IDBFactory> aFactory,
|
||||
PrincipalInfo&& aPrincipalInfo)
|
||||
: Runnable("indexedDB::WorkerPermissionChallenge"),
|
||||
mWorkerPrivate(aWorkerPrivate),
|
||||
mActor(aActor),
|
||||
mFactory(std::move(aFactory)),
|
||||
mPrincipalInfo(std::move(aPrincipalInfo)) {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(aActor);
|
||||
MOZ_ASSERT(mFactory);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
bool Dispatch() {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
if (NS_WARN_IF(!mWorkerPrivate->ModifyBusyCountFromWorker(true))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
|
||||
mWorkerPrivate->ModifyBusyCountFromWorker(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override {
|
||||
const bool completed = RunInternal();
|
||||
if (completed) {
|
||||
OperationCompleted();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void OperationCompleted() {
|
||||
if (NS_IsMainThread()) {
|
||||
const RefPtr<WorkerPermissionOperationCompleted> runnable =
|
||||
new WorkerPermissionOperationCompleted(mWorkerPrivate, this);
|
||||
|
||||
MOZ_ALWAYS_TRUE(runnable->Dispatch());
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mActor);
|
||||
mActor->AssertIsOnOwningThread();
|
||||
|
||||
MaybeCollectGarbageOnIPCMessage();
|
||||
|
||||
const SafeRefPtr<IDBFactory> factory = std::move(mFactory);
|
||||
Unused << factory; // XXX see Bug 1605075
|
||||
|
||||
mActor->SendPermissionRetry();
|
||||
mActor = nullptr;
|
||||
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->ModifyBusyCountFromWorker(false);
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunInternal() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Walk up to our containing page
|
||||
WorkerPrivate* wp = mWorkerPrivate;
|
||||
while (wp->GetParent()) {
|
||||
wp = wp->GetParent();
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* const window = wp->GetWindow();
|
||||
if (!window) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(auto principal,
|
||||
mozilla::ipc::PrincipalInfoToPrincipal(mPrincipalInfo), true);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
const nsCOMPtr<Element> ownerElement =
|
||||
do_QueryInterface(window->GetChromeEventHandler());
|
||||
if (NS_WARN_IF(!ownerElement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<WorkerPermissionRequest> helper =
|
||||
new WorkerPermissionRequest(ownerElement, principal, this);
|
||||
|
||||
QM_TRY_INSPECT(const PermissionRequestBase::PermissionValue& permission,
|
||||
helper->PromptIfNeeded(), true);
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
|
||||
permission == PermissionRequestBase::kPermissionDenied ||
|
||||
permission == PermissionRequestBase::kPermissionPrompt);
|
||||
|
||||
return permission != PermissionRequestBase::kPermissionPrompt;
|
||||
}
|
||||
|
||||
BrowserChild* browserChild = BrowserChild::GetFrom(window);
|
||||
MOZ_ASSERT(browserChild);
|
||||
|
||||
RefPtr<WorkerPermissionChallenge> self(this);
|
||||
browserChild->SendIndexedDBPermissionRequest(principal)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[self](const uint32_t& aPermission) { self->OperationCompleted(); },
|
||||
[](const mozilla::ipc::ResponseRejectReason) {});
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate* const mWorkerPrivate;
|
||||
BackgroundFactoryRequestChild* mActor;
|
||||
SafeRefPtr<IDBFactory> mFactory;
|
||||
const 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;
|
||||
}
|
||||
|
||||
class MOZ_STACK_CLASS AutoSetCurrentFileHandle final {
|
||||
using BackgroundChildImpl = mozilla::ipc::BackgroundChildImpl;
|
||||
|
||||
@ -1138,23 +941,6 @@ class BackgroundRequestChild::PreprocessHelper final
|
||||
NS_DECL_NSIFILEMETADATACALLBACK
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Local class implementations
|
||||
******************************************************************************/
|
||||
|
||||
void PermissionRequestMainProcessHelper::OnPromptComplete(
|
||||
PermissionValue aPermissionValue) {
|
||||
MOZ_ASSERT(mActor);
|
||||
mActor->AssertIsOnOwningThread();
|
||||
|
||||
MaybeCollectGarbageOnIPCMessage();
|
||||
|
||||
mActor->SendPermissionRetry();
|
||||
|
||||
mActor = nullptr;
|
||||
mFactory = nullptr;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* BackgroundRequestChildBase
|
||||
******************************************************************************/
|
||||
@ -1425,76 +1211,6 @@ mozilla::ipc::IPCResult BackgroundFactoryRequestChild::Recv__delete__(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvPermissionChallenge(
|
||||
PrincipalInfo&& aPrincipalInfo) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
MaybeCollectGarbageOnIPCMessage();
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<WorkerPermissionChallenge> challenge = new WorkerPermissionChallenge(
|
||||
workerPrivate, this, mFactory.clonePtr(), std::move(aPrincipalInfo));
|
||||
if (!challenge->Dispatch()) {
|
||||
QM_WARNONLY_TRY(OkIf(SendPermissionRetry()));
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
QM_TRY_UNWRAP(auto principal,
|
||||
mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo),
|
||||
IPC_FAIL(this, "PrincipalInfoToPrincipal failed!"));
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsCOMPtr<nsIGlobalObject> global = mFactory->GetParentObject();
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
nsCOMPtr<Element> ownerElement =
|
||||
do_QueryInterface(window->GetChromeEventHandler());
|
||||
if (NS_WARN_IF(!ownerElement)) {
|
||||
// If this fails, the page was navigated. Fail the permission check by
|
||||
// forcing an immediate retry.
|
||||
QM_WARNONLY_TRY(OkIf(SendPermissionRetry()));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<PermissionRequestMainProcessHelper> helper =
|
||||
new PermissionRequestMainProcessHelper(this, mFactory.clonePtr(),
|
||||
ownerElement, principal);
|
||||
|
||||
QM_TRY_INSPECT(const PermissionRequestBase::PermissionValue& permission,
|
||||
helper->PromptIfNeeded(),
|
||||
IPC_FAIL(this, "PromptIfNeeded failed!"));
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
|
||||
permission == PermissionRequestBase::kPermissionDenied ||
|
||||
permission == PermissionRequestBase::kPermissionPrompt);
|
||||
|
||||
if (permission != PermissionRequestBase::kPermissionPrompt) {
|
||||
SendPermissionRetry();
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<BrowserChild> browserChild = mFactory->GetBrowserChild();
|
||||
MOZ_ASSERT(browserChild);
|
||||
|
||||
browserChild->SendIndexedDBPermissionRequest(principal)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[this](const uint32_t& aPermission) {
|
||||
this->AssertIsOnOwningThread();
|
||||
MaybeCollectGarbageOnIPCMessage();
|
||||
this->SendPermissionRetry();
|
||||
},
|
||||
[](const mozilla::ipc::ResponseRejectReason) {});
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked(
|
||||
const uint64_t aCurrentVersion) {
|
||||
AssertIsOnOwningThread();
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "IndexedDatabaseManager.h"
|
||||
#include "KeyPath.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "PermissionRequestBase.h"
|
||||
#include "ProfilerHelpers.h"
|
||||
#include "ReportInternalError.h"
|
||||
#include "SafeRefPtr.h"
|
||||
@ -3106,15 +3105,6 @@ class FactoryOp
|
||||
// if permission is granted.
|
||||
Initial,
|
||||
|
||||
// Sending a permission challenge message to the child on the PBackground
|
||||
// thread. Next step is PermissionRetry.
|
||||
PermissionChallenge,
|
||||
|
||||
// Retrying permission check after a challenge on the main thread. Next step
|
||||
// is either SendingResults if permission is denied or FinishOpen
|
||||
// if permission is granted.
|
||||
PermissionRetry,
|
||||
|
||||
// Ensuring quota manager is created and opening directory on the
|
||||
// PBackground thread. Next step is either SendingResults if quota manager
|
||||
// is not available or DirectoryOpenPending if quota manager is available.
|
||||
@ -3236,12 +3226,6 @@ class FactoryOp
|
||||
|
||||
nsresult Open();
|
||||
|
||||
nsresult ChallengePermission();
|
||||
|
||||
void PermissionRetry();
|
||||
|
||||
nsresult RetryCheckPermission();
|
||||
|
||||
nsresult DirectoryOpen();
|
||||
|
||||
nsresult SendToIOThread();
|
||||
@ -3287,13 +3271,11 @@ class FactoryOp
|
||||
// IPDL methods.
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvPermissionRetry() final;
|
||||
|
||||
virtual void SendBlockedNotification() = 0;
|
||||
|
||||
private:
|
||||
mozilla::Result<PermissionRequestBase::PermissionValue, nsresult>
|
||||
CheckPermission(ContentParent* aContentParent);
|
||||
mozilla::Result<PermissionValue, nsresult> CheckPermission(
|
||||
ContentParent* aContentParent);
|
||||
|
||||
static bool CheckAtLeastOneAppHasPermission(
|
||||
ContentParent* aContentParent, const nsACString& aPermissionString);
|
||||
@ -15126,14 +15108,6 @@ void FactoryOp::StringifyState(nsACString& aResult) const {
|
||||
aResult.AppendLiteral("Initial");
|
||||
return;
|
||||
|
||||
case State::PermissionChallenge:
|
||||
aResult.AppendLiteral("PermissionChallenge");
|
||||
return;
|
||||
|
||||
case State::PermissionRetry:
|
||||
aResult.AppendLiteral("PermissionRetry");
|
||||
return;
|
||||
|
||||
case State::FinishOpen:
|
||||
aResult.AppendLiteral("FinishOpen");
|
||||
return;
|
||||
@ -15210,11 +15184,10 @@ nsresult FactoryOp::Open() {
|
||||
|
||||
QM_TRY_INSPECT(const auto& permission, CheckPermission(contentParent));
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
|
||||
permission == PermissionRequestBase::kPermissionDenied ||
|
||||
permission == PermissionRequestBase::kPermissionPrompt);
|
||||
MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed ||
|
||||
permission == PermissionValue::kPermissionDenied);
|
||||
|
||||
if (permission == PermissionRequestBase::kPermissionDenied) {
|
||||
if (permission == PermissionValue::kPermissionDenied) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
@ -15242,13 +15215,7 @@ nsresult FactoryOp::Open() {
|
||||
mDatabaseId.Append('*');
|
||||
mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
|
||||
|
||||
if (permission == PermissionRequestBase::kPermissionPrompt) {
|
||||
mState = State::PermissionChallenge;
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
|
||||
MOZ_ASSERT(permission == PermissionValue::kPermissionAllowed);
|
||||
|
||||
if (mInPrivateBrowsing) {
|
||||
const auto lockedPrivateBrowsingInfoHashtable =
|
||||
@ -15275,69 +15242,6 @@ nsresult FactoryOp::Open() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult FactoryOp::ChallengePermission() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::PermissionChallenge);
|
||||
|
||||
const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
|
||||
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
|
||||
|
||||
if (NS_WARN_IF(!SendPermissionChallenge(principalInfo))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mWaitingForPermissionRetry = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void FactoryOp::PermissionRetry() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::PermissionChallenge);
|
||||
|
||||
mContentParent = BackgroundParent::GetContentParent(Manager()->Manager());
|
||||
|
||||
mState = State::PermissionRetry;
|
||||
|
||||
mWaitingForPermissionRetry = false;
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
|
||||
}
|
||||
|
||||
nsresult FactoryOp::RetryCheckPermission() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mState == State::PermissionRetry);
|
||||
MOZ_ASSERT(mCommonParams.principalInfo().type() ==
|
||||
PrincipalInfo::TContentPrincipalInfo);
|
||||
|
||||
// Move this to the stack now to ensure that we release it on this thread.
|
||||
const RefPtr<ContentParent> contentParent = std::move(mContentParent);
|
||||
|
||||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
|
||||
!OperationMayProceed()) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
QM_TRY_INSPECT(const auto& permission, CheckPermission(contentParent));
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed ||
|
||||
permission == PermissionRequestBase::kPermissionDenied ||
|
||||
permission == PermissionRequestBase::kPermissionPrompt);
|
||||
|
||||
if (permission == PermissionRequestBase::kPermissionDenied ||
|
||||
permission == PermissionRequestBase::kPermissionPrompt) {
|
||||
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(permission == PermissionRequestBase::kPermissionAllowed);
|
||||
|
||||
mState = State::FinishOpen;
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult FactoryOp::DirectoryOpen() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::DirectoryOpenPending);
|
||||
@ -15456,10 +15360,10 @@ void FactoryOp::FinishSendResults() {
|
||||
mFactory = nullptr;
|
||||
}
|
||||
|
||||
Result<PermissionRequestBase::PermissionValue, nsresult>
|
||||
FactoryOp::CheckPermission(ContentParent* aContentParent) {
|
||||
Result<PermissionValue, nsresult> FactoryOp::CheckPermission(
|
||||
ContentParent* aContentParent) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mState == State::Initial || mState == State::PermissionRetry);
|
||||
MOZ_ASSERT(mState == State::Initial);
|
||||
|
||||
const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
|
||||
if (principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||
@ -15545,7 +15449,7 @@ FactoryOp::CheckPermission(ContentParent* aContentParent) {
|
||||
mEnforcingQuota = false;
|
||||
}
|
||||
|
||||
return PermissionRequestBase::kPermissionAllowed;
|
||||
return PermissionValue::kPermissionAllowed;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
|
||||
@ -15556,26 +15460,21 @@ FactoryOp::CheckPermission(ContentParent* aContentParent) {
|
||||
QM_TRY_UNWRAP(auto principalMetadata,
|
||||
QuotaManager::GetInfoFromPrincipal(principal));
|
||||
|
||||
QM_TRY_INSPECT(const auto& permission,
|
||||
([persistenceType, &origin = principalMetadata.mOrigin,
|
||||
&principal = *principal]()
|
||||
-> mozilla::Result<PermissionRequestBase::PermissionValue,
|
||||
nsresult> {
|
||||
if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
|
||||
if (QuotaManager::IsOriginInternal(origin)) {
|
||||
return PermissionRequestBase::kPermissionAllowed;
|
||||
}
|
||||
#ifdef IDB_MOBILE
|
||||
return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
#else
|
||||
return PermissionRequestBase::GetCurrentPermission(
|
||||
principal);
|
||||
#endif
|
||||
}
|
||||
return PermissionRequestBase::kPermissionAllowed;
|
||||
})());
|
||||
QM_TRY_INSPECT(
|
||||
const auto& permission,
|
||||
([persistenceType, &origin = principalMetadata.mOrigin,
|
||||
&principal =
|
||||
*principal]() -> mozilla::Result<PermissionValue, nsresult> {
|
||||
if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
|
||||
if (QuotaManager::IsOriginInternal(origin)) {
|
||||
return PermissionValue::kPermissionAllowed;
|
||||
}
|
||||
return Err(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
||||
}
|
||||
return PermissionValue::kPermissionAllowed;
|
||||
})());
|
||||
|
||||
if (permission != PermissionRequestBase::kPermissionDenied &&
|
||||
if (permission != PermissionValue::kPermissionDenied &&
|
||||
State::Initial == mState) {
|
||||
mOriginMetadata = {std::move(principalMetadata), persistenceType};
|
||||
|
||||
@ -15730,14 +15629,6 @@ FactoryOp::Run() {
|
||||
QM_TRY(MOZ_TO_RESULT(Open()), NS_OK, handleError);
|
||||
break;
|
||||
|
||||
case State::PermissionChallenge:
|
||||
QM_TRY(MOZ_TO_RESULT(ChallengePermission()), NS_OK, handleError);
|
||||
break;
|
||||
|
||||
case State::PermissionRetry:
|
||||
QM_TRY(MOZ_TO_RESULT(RetryCheckPermission()), NS_OK, handleError);
|
||||
break;
|
||||
|
||||
case State::FinishOpen:
|
||||
QM_TRY(MOZ_TO_RESULT(FinishOpen()), NS_OK, handleError);
|
||||
break;
|
||||
@ -15812,32 +15703,6 @@ void FactoryOp::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
NoteActorDestroyed();
|
||||
|
||||
// Assume ActorDestroy can happen at any time, so we can't probe the current
|
||||
// state since mState can be modified on any thread (only one thread at a time
|
||||
// based on the state machine). However we can use mWaitingForPermissionRetry
|
||||
// which is only touched on the owning thread. If mWaitingForPermissionRetry
|
||||
// is true, we can also modify mState since we are guaranteed that there are
|
||||
// no pending runnables which would probe mState to decide what code needs to
|
||||
// run (there shouldn't be any running runnables on other threads either).
|
||||
|
||||
if (mWaitingForPermissionRetry) {
|
||||
PermissionRetry();
|
||||
}
|
||||
|
||||
// We don't have to handle the case when mWaitingForPermissionRetry is not
|
||||
// true since it means that either nothing has been initialized yet, so
|
||||
// nothing to cleanup or there are pending runnables that will detect that the
|
||||
// actor has been destroyed and cleanup accordingly.
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult FactoryOp::RecvPermissionRetry() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(!IsActorDestroyed());
|
||||
|
||||
PermissionRetry();
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr<Factory> aFactory,
|
||||
@ -21475,13 +21340,6 @@ mozilla::ipc::IPCResult Utils::RecvGetFileReferences(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void PermissionRequestHelper::OnPromptComplete(
|
||||
PermissionValue aPermissionValue) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mResolver(aPermissionValue);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver)
|
||||
|
@ -8,9 +8,9 @@
|
||||
#define mozilla_dom_indexeddb_actorsparent_h__
|
||||
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/indexedDB/PermissionRequestBase.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
@ -27,6 +27,12 @@ class Client;
|
||||
|
||||
namespace indexedDB {
|
||||
|
||||
enum class PermissionValue {
|
||||
kPermissionAllowed = nsIPermissionManager::ALLOW_ACTION,
|
||||
kPermissionDenied = nsIPermissionManager::DENY_ACTION,
|
||||
kPermissionPrompt = nsIPermissionManager::PROMPT_ACTION
|
||||
};
|
||||
|
||||
class LoggingInfo;
|
||||
class PBackgroundIDBFactoryParent;
|
||||
class PBackgroundIndexedDBUtilsParent;
|
||||
@ -50,23 +56,6 @@ RefPtr<mozilla::dom::quota::Client> CreateQuotaClient();
|
||||
|
||||
FileHandleThreadPool* GetFileHandleThreadPool();
|
||||
|
||||
class PermissionRequestHelper final : public PermissionRequestBase {
|
||||
public:
|
||||
PermissionRequestHelper(
|
||||
Element* aOwnerElement, nsIPrincipal* aPrincipal,
|
||||
PBrowserParent::IndexedDBPermissionRequestResolver& aResolver)
|
||||
: PermissionRequestBase(aOwnerElement, aPrincipal),
|
||||
mResolver(aResolver) {}
|
||||
|
||||
protected:
|
||||
~PermissionRequestHelper() override = default;
|
||||
|
||||
private:
|
||||
PBrowserParent::IndexedDBPermissionRequestResolver mResolver;
|
||||
|
||||
void OnPromptComplete(PermissionValue aPermissionValue) override;
|
||||
};
|
||||
|
||||
} // namespace indexedDB
|
||||
} // namespace mozilla::dom
|
||||
|
||||
|
@ -38,12 +38,7 @@ protocol PBackgroundIDBFactoryRequest
|
||||
child:
|
||||
async __delete__(FactoryRequestResponse response);
|
||||
|
||||
async PermissionChallenge(PrincipalInfo principalInfo);
|
||||
|
||||
async Blocked(uint64_t currentVersion);
|
||||
|
||||
parent:
|
||||
async PermissionRetry();
|
||||
};
|
||||
|
||||
} // namespace indexedDB
|
||||
|
@ -1,222 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "PermissionRequestBase.h"
|
||||
|
||||
#include "IndexedDBCommon.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/quota/ResultExtensions.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla::dom::indexedDB {
|
||||
|
||||
using namespace mozilla::services;
|
||||
|
||||
namespace {
|
||||
|
||||
#define IDB_PREFIX "indexedDB"
|
||||
#define TOPIC_PREFIX IDB_PREFIX "-permissions-"
|
||||
|
||||
const nsLiteralCString kPermissionString = nsLiteralCString(IDB_PREFIX);
|
||||
|
||||
const char kPermissionPromptTopic[] = TOPIC_PREFIX "prompt";
|
||||
|
||||
#ifdef DEBUG
|
||||
const char kPermissionResponseTopic[] = TOPIC_PREFIX "response";
|
||||
#endif
|
||||
|
||||
#undef TOPIC_PREFIX
|
||||
#undef IDB_PREFIX
|
||||
|
||||
const uint32_t kPermissionDefault = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
|
||||
void AssertSanity() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PermissionRequestBase::PermissionRequestBase(Element* aOwnerElement,
|
||||
nsIPrincipal* aPrincipal)
|
||||
: mOwnerElement(aOwnerElement), mPrincipal(aPrincipal) {
|
||||
AssertSanity();
|
||||
MOZ_ASSERT(aOwnerElement);
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
}
|
||||
|
||||
PermissionRequestBase::~PermissionRequestBase() { AssertSanity(); }
|
||||
|
||||
// static
|
||||
Result<PermissionRequestBase::PermissionValue, nsresult>
|
||||
PermissionRequestBase::GetCurrentPermission(nsIPrincipal& aPrincipal) {
|
||||
AssertSanity();
|
||||
|
||||
const nsCOMPtr<nsIPermissionManager> permMan = GetPermissionManager();
|
||||
QM_TRY(OkIf(permMan), Err(NS_ERROR_FAILURE));
|
||||
|
||||
QM_TRY_INSPECT(
|
||||
const uint32_t& intPermission,
|
||||
MOZ_TO_RESULT_INVOKE_MEMBER(permMan, TestExactPermissionFromPrincipal,
|
||||
&aPrincipal, kPermissionString));
|
||||
|
||||
const PermissionValue permission =
|
||||
PermissionValueForIntPermission(intPermission);
|
||||
|
||||
MOZ_ASSERT(permission == kPermissionAllowed ||
|
||||
permission == kPermissionDenied ||
|
||||
permission == kPermissionPrompt);
|
||||
|
||||
return permission;
|
||||
}
|
||||
|
||||
// static
|
||||
auto PermissionRequestBase::PermissionValueForIntPermission(
|
||||
uint32_t aIntPermission) -> PermissionValue {
|
||||
AssertSanity();
|
||||
|
||||
switch (aIntPermission) {
|
||||
case kPermissionDefault:
|
||||
return kPermissionPrompt;
|
||||
case kPermissionAllowed:
|
||||
return kPermissionAllowed;
|
||||
case kPermissionDenied:
|
||||
return kPermissionDenied;
|
||||
default:
|
||||
MOZ_CRASH("Bad permission!");
|
||||
}
|
||||
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
||||
Result<PermissionRequestBase::PermissionValue, nsresult>
|
||||
PermissionRequestBase::PromptIfNeeded() {
|
||||
AssertSanity();
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
|
||||
// Tricky, we want to release the window and principal in all cases except
|
||||
// when we successfully prompt.
|
||||
nsCOMPtr<Element> element = std::move(mOwnerElement);
|
||||
nsCOMPtr<nsIPrincipal> principal = std::move(mPrincipal);
|
||||
|
||||
QM_TRY_INSPECT(const PermissionValue& currentValue,
|
||||
GetCurrentPermission(*principal));
|
||||
MOZ_ASSERT(currentValue != kPermissionDefault);
|
||||
|
||||
if (currentValue == kPermissionPrompt) {
|
||||
nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
|
||||
QM_TRY(OkIf(obsSvc), Err(NS_ERROR_FAILURE));
|
||||
|
||||
// We're about to prompt so move the members back.
|
||||
mOwnerElement = std::move(element);
|
||||
mPrincipal = std::move(principal);
|
||||
|
||||
QM_TRY(
|
||||
MOZ_TO_RESULT(obsSvc->NotifyObservers(static_cast<nsIObserver*>(this),
|
||||
kPermissionPromptTopic, nullptr)),
|
||||
QM_PROPAGATE, [this](const auto&) {
|
||||
// Finally release if we failed the prompt.
|
||||
mOwnerElement = nullptr;
|
||||
mPrincipal = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
void PermissionRequestBase::SetExplicitPermission(nsIPrincipal* aPrincipal,
|
||||
uint32_t aIntPermission) {
|
||||
AssertSanity();
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(aIntPermission == kPermissionAllowed ||
|
||||
aIntPermission == kPermissionDenied);
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMan = GetPermissionManager();
|
||||
if (NS_WARN_IF(!permMan)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv =
|
||||
permMan->AddFromPrincipal(aPrincipal, kPermissionString, aIntPermission,
|
||||
nsIPermissionManager::EXPIRE_NEVER,
|
||||
/* aExpireTime */ 0);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PermissionRequestBase, nsIObserver, nsIIDBPermissionsRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
PermissionRequestBase::GetBrowserElement(Element** aElement) {
|
||||
AssertSanity();
|
||||
*aElement = do_AddRef(mOwnerElement).take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PermissionRequestBase::GetResponseObserver(nsIObserver** aObserver) {
|
||||
AssertSanity();
|
||||
*aObserver = do_AddRef(this).take();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PermissionRequestBase::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
AssertSanity();
|
||||
MOZ_ASSERT(!strcmp(aTopic, kPermissionResponseTopic));
|
||||
MOZ_ASSERT(mOwnerElement);
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
|
||||
const nsCOMPtr<Element> element = std::move(mOwnerElement);
|
||||
Unused << element;
|
||||
const nsCOMPtr<nsIPrincipal> principal = std::move(mPrincipal);
|
||||
|
||||
nsresult rv;
|
||||
uint32_t promptResult = nsDependentString(aData).ToInteger(&rv);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
|
||||
// The UI prompt code will only return one of these three values. We have to
|
||||
// transform it to our values.
|
||||
MOZ_ASSERT(promptResult == kPermissionDefault ||
|
||||
promptResult == kPermissionAllowed ||
|
||||
promptResult == kPermissionDenied);
|
||||
|
||||
if (promptResult != kPermissionDefault) {
|
||||
// Save explicitly allowed or denied permissions now.
|
||||
SetExplicitPermission(principal, promptResult);
|
||||
}
|
||||
|
||||
PermissionValue permission;
|
||||
switch (promptResult) {
|
||||
case kPermissionDefault:
|
||||
permission = kPermissionPrompt;
|
||||
break;
|
||||
|
||||
case kPermissionAllowed:
|
||||
permission = kPermissionAllowed;
|
||||
break;
|
||||
|
||||
case kPermissionDenied:
|
||||
permission = kPermissionDenied;
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Bad prompt result!");
|
||||
}
|
||||
|
||||
OnPromptComplete(permission);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom::indexedDB
|
@ -1,70 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 mozilla_dom_indexeddb_permissionrequestbase_h__
|
||||
#define mozilla_dom_indexeddb_permissionrequestbase_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIIDBPermissionsRequest.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class Element;
|
||||
|
||||
namespace indexedDB {
|
||||
|
||||
class PermissionRequestBase : public nsIObserver,
|
||||
public nsIIDBPermissionsRequest {
|
||||
nsCOMPtr<Element> mOwnerElement;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
public:
|
||||
enum PermissionValue {
|
||||
kPermissionAllowed = nsIPermissionManager::ALLOW_ACTION,
|
||||
kPermissionDenied = nsIPermissionManager::DENY_ACTION,
|
||||
kPermissionPrompt = nsIPermissionManager::PROMPT_ACTION
|
||||
};
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// This function will not actually prompt. It will never return
|
||||
// kPermissionDefault but will instead translate the permission manager value
|
||||
// into the correct value for the given type.
|
||||
static Result<PermissionValue, nsresult> GetCurrentPermission(
|
||||
nsIPrincipal& aPrincipal);
|
||||
|
||||
static PermissionValue PermissionValueForIntPermission(
|
||||
uint32_t aIntPermission);
|
||||
|
||||
// This function will prompt if needed. It may only be called once.
|
||||
Result<PermissionValue, nsresult> PromptIfNeeded();
|
||||
|
||||
protected:
|
||||
PermissionRequestBase(Element* aOwnerElement, nsIPrincipal* aPrincipal);
|
||||
|
||||
// Reference counted.
|
||||
virtual ~PermissionRequestBase();
|
||||
|
||||
virtual void OnPromptComplete(PermissionValue aPermissionValue) = 0;
|
||||
|
||||
private:
|
||||
void SetExplicitPermission(nsIPrincipal* aPrincipal, uint32_t aIntPermission);
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIIDBPERMISSIONSREQUEST
|
||||
};
|
||||
|
||||
} // namespace indexedDB
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_indexeddb_permissionrequestbase_h__
|
@ -50,7 +50,6 @@ EXPORTS.mozilla.dom.indexedDB += [
|
||||
"IDBResult.h",
|
||||
"Key.h",
|
||||
"KeyPath.h",
|
||||
"PermissionRequestBase.h",
|
||||
"SerializationHelpers.h",
|
||||
"ThreadLocal.h",
|
||||
]
|
||||
@ -77,7 +76,6 @@ UNIFIED_SOURCES += [
|
||||
"IndexedDatabaseManager.cpp",
|
||||
"IndexedDBCommon.cpp",
|
||||
"KeyPath.cpp",
|
||||
"PermissionRequestBase.cpp",
|
||||
"ProfilerHelpers.cpp",
|
||||
"ReportInternalError.cpp",
|
||||
"SchemaUpgrades.cpp",
|
||||
|
@ -1361,36 +1361,6 @@ bool BrowserParent::DeallocPFilePickerParent(PFilePickerParent* actor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IPCResult BrowserParent::RecvIndexedDBPermissionRequest(
|
||||
nsIPrincipal* aPrincipal, IndexedDBPermissionRequestResolver&& aResolve) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal(aPrincipal);
|
||||
if (!principal) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mFrameElement)) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
RefPtr<indexedDB::PermissionRequestHelper> actor =
|
||||
new indexedDB::PermissionRequestHelper(mFrameElement, principal,
|
||||
aResolve);
|
||||
|
||||
mozilla::Result permissionOrErr = actor->PromptIfNeeded();
|
||||
if (permissionOrErr.isErr()) {
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
if (permissionOrErr.inspect() !=
|
||||
indexedDB::PermissionRequestBase::kPermissionPrompt) {
|
||||
aResolve(permissionOrErr.inspect());
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
already_AddRefed<PSessionStoreParent>
|
||||
BrowserParent::AllocPSessionStoreParent() {
|
||||
RefPtr<BrowserSessionStore> sessionStore =
|
||||
|
@ -604,9 +604,6 @@ class BrowserParent final : public PBrowserParent,
|
||||
|
||||
bool DeallocPFilePickerParent(PFilePickerParent* actor);
|
||||
|
||||
mozilla::ipc::IPCResult RecvIndexedDBPermissionRequest(
|
||||
nsIPrincipal* aPrincipal, IndexedDBPermissionRequestResolver&& aResolve);
|
||||
|
||||
bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
|
||||
|
||||
void StartPersistence(CanonicalBrowsingContext* aContext,
|
||||
|
@ -441,19 +441,6 @@ parent:
|
||||
|
||||
async PFilePicker(nsString aTitle, int16_t aMode);
|
||||
|
||||
/**
|
||||
* Initiates an asynchronous request for one of the special indexedDB
|
||||
* permissions for the provided principal.
|
||||
*
|
||||
* @param principal
|
||||
* The principal of the request.
|
||||
*
|
||||
* NOTE: The principal is untrusted in the parent process. Only
|
||||
* principals that can live in the content process should
|
||||
* provided.
|
||||
*/
|
||||
async IndexedDBPermissionRequest(nsIPrincipal aPrincipal) returns (uint32_t permission);
|
||||
|
||||
/**
|
||||
* Tells the containing widget whether the given input block results in a
|
||||
* swipe. Should be called in response to a WidgetWheelEvent that has
|
||||
|
Loading…
Reference in New Issue
Block a user