зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1535221 - Dead lock with a11y's mscom and mozilla::dom::LSObject; r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D27463
This commit is contained in:
Родитель
d50956f778
Коммит
f968189930
|
@ -14,20 +14,87 @@ namespace dom {
|
|||
|
||||
namespace {
|
||||
|
||||
class RequestResolver final : public LSRequestChildCallback {
|
||||
class AsyncRequestHelper final : public Runnable,
|
||||
public LSRequestChildCallback {
|
||||
enum class State {
|
||||
/**
|
||||
* The AsyncRequestHelper has been created and dispatched to the DOM File
|
||||
* Thread.
|
||||
*/
|
||||
Initial,
|
||||
/**
|
||||
* Start() has been invoked on the DOM File Thread and
|
||||
* LocalStorageManager2::StartRequest has been invoked from there, sending
|
||||
* an IPC message to PBackground to service the request. We stay in this
|
||||
* state until a response is received.
|
||||
*/
|
||||
ResponsePending,
|
||||
/**
|
||||
* A response has been received and AsyncRequestHelper has been dispatched
|
||||
* back to the owning event target to call Finish().
|
||||
*/
|
||||
Finishing,
|
||||
/**
|
||||
* Finish() has been called on the main thread. The promise will be resolved
|
||||
* according to the received response.
|
||||
*/
|
||||
Complete
|
||||
};
|
||||
|
||||
// The object we are issuing a request on behalf of. Present because of the
|
||||
// need to invoke LocalStorageManager2::StartRequest off the main thread.
|
||||
// Dropped on return to the main-thread in Finish().
|
||||
RefPtr<LocalStorageManager2> mManager;
|
||||
// The thread the AsyncRequestHelper was created on. This should be the main
|
||||
// thread.
|
||||
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
||||
// The IPC actor handling the request with standard IPC allocation rules.
|
||||
// Our reference is nulled in OnResponse which corresponds to the actor's
|
||||
// __destroy__ method.
|
||||
LSRequestChild* mActor;
|
||||
RefPtr<Promise> mPromise;
|
||||
const LSRequestParams mParams;
|
||||
LSRequestResponse mResponse;
|
||||
nsresult mResultCode;
|
||||
State mState;
|
||||
|
||||
public:
|
||||
explicit RequestResolver(Promise* aPromise) : mPromise(aPromise) {}
|
||||
AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise,
|
||||
const LSRequestParams& aParams)
|
||||
: Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
|
||||
mManager(aManager),
|
||||
mOwningEventTarget(GetCurrentThreadEventTarget()),
|
||||
mActor(nullptr),
|
||||
mPromise(aPromise),
|
||||
mParams(aParams),
|
||||
mResultCode(NS_OK),
|
||||
mState(State::Initial) {}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::RequestResolver, override);
|
||||
bool IsOnOwningThread() const {
|
||||
MOZ_ASSERT(mOwningEventTarget);
|
||||
|
||||
bool current;
|
||||
return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) &&
|
||||
current;
|
||||
}
|
||||
|
||||
void AssertIsOnOwningThread() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsOnOwningThread());
|
||||
}
|
||||
|
||||
nsresult Dispatch();
|
||||
|
||||
private:
|
||||
~RequestResolver() = default;
|
||||
~AsyncRequestHelper() = default;
|
||||
|
||||
void HandleResponse(nsresult aResponse);
|
||||
nsresult Start();
|
||||
|
||||
void HandleResponse();
|
||||
void Finish();
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
||||
// LSRequestChildCallback
|
||||
void OnResponse(const LSRequestResponse& aResponse) override;
|
||||
|
@ -220,7 +287,20 @@ LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
|
|||
|
||||
LSRequestPreloadDatastoreParams params(commonParams);
|
||||
|
||||
rv = StartRequest(promise, params);
|
||||
RefPtr<AsyncRequestHelper> helper =
|
||||
new AsyncRequestHelper(this, promise, params);
|
||||
|
||||
// This will start and finish the async request on the DOM File thread.
|
||||
// This must be done on DOM File Thread because it's very likely that a
|
||||
// content process will issue a prepare datastore request for the same
|
||||
// principal while blocking the content process on the main thread.
|
||||
// There would be a potential for deadlock if the preloading was initialized
|
||||
// from the main thread of the parent process and a11y issued a synchronous
|
||||
// message from the parent process to the content process (approximately at
|
||||
// the same time) because the preload request wouldn't be able to respond
|
||||
// to the Ready message by sending the Finish message which is needed to
|
||||
// finish the preload request and unblock the prepare datastore request.
|
||||
rv = helper->Dispatch();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -258,25 +338,23 @@ LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult LocalStorageManager2::StartRequest(Promise* aPromise,
|
||||
const LSRequestParams& aParams) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LSRequestChild* LocalStorageManager2::StartRequest(
|
||||
const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
|
||||
AssertIsOnDOMFileThread();
|
||||
|
||||
PBackgroundChild* backgroundActor =
|
||||
BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (NS_WARN_IF(!backgroundActor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<RequestResolver> resolver = new RequestResolver(aPromise);
|
||||
|
||||
auto actor = new LSRequestChild(resolver);
|
||||
auto actor = new LSRequestChild(aCallback);
|
||||
|
||||
if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return actor;
|
||||
}
|
||||
|
||||
nsresult LocalStorageManager2::StartSimpleRequest(
|
||||
|
@ -302,40 +380,118 @@ nsresult LocalStorageManager2::StartSimpleRequest(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void RequestResolver::HandleResponse(nsresult aResponse) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult AsyncRequestHelper::Dispatch() {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (!mPromise) {
|
||||
return;
|
||||
nsCOMPtr<nsIEventTarget> domFileThread =
|
||||
IPCBlobInputStreamThread::GetOrCreate();
|
||||
if (NS_WARN_IF(!domFileThread)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mPromise->MaybeReject(aResponse);
|
||||
}
|
||||
|
||||
void RequestResolver::HandleResponse() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mPromise) {
|
||||
return;
|
||||
nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolveWithUndefined();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void RequestResolver::OnResponse(const LSRequestResponse& aResponse) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult AsyncRequestHelper::Start() {
|
||||
AssertIsOnDOMFileThread();
|
||||
MOZ_ASSERT(mState == State::Initial);
|
||||
|
||||
switch (aResponse.type()) {
|
||||
case LSRequestResponse::Tnsresult:
|
||||
HandleResponse(aResponse.get_nsresult());
|
||||
mState = State::ResponsePending;
|
||||
|
||||
LSRequestChild* actor = mManager->StartRequest(mParams, this);
|
||||
if (NS_WARN_IF(!actor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mActor = actor;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AsyncRequestHelper::Finish() {
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mState == State::Finishing);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(mResultCode))) {
|
||||
if (mPromise) {
|
||||
mPromise->MaybeReject(mResultCode);
|
||||
}
|
||||
} else {
|
||||
switch (mResponse.type()) {
|
||||
case LSRequestResponse::Tnsresult:
|
||||
if (mPromise) {
|
||||
mPromise->MaybeReject(mResponse.get_nsresult());
|
||||
}
|
||||
break;
|
||||
|
||||
case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
|
||||
if (mPromise) {
|
||||
mPromise->MaybeResolveWithUndefined();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unknown response type!");
|
||||
}
|
||||
}
|
||||
|
||||
mManager = nullptr;
|
||||
|
||||
mState = State::Complete;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncRequestHelper::Run() {
|
||||
nsresult rv;
|
||||
|
||||
switch (mState) {
|
||||
case State::Initial:
|
||||
rv = Start();
|
||||
break;
|
||||
|
||||
case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
|
||||
HandleResponse();
|
||||
break;
|
||||
case State::Finishing:
|
||||
Finish();
|
||||
return NS_OK;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unknown response type!");
|
||||
MOZ_CRASH("Bad state!");
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
|
||||
if (NS_SUCCEEDED(mResultCode)) {
|
||||
mResultCode = rv;
|
||||
}
|
||||
|
||||
mState = State::Finishing;
|
||||
|
||||
if (IsOnOwningThread()) {
|
||||
Finish();
|
||||
} else {
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void AsyncRequestHelper::OnResponse(const LSRequestResponse& aResponse) {
|
||||
AssertIsOnDOMFileThread();
|
||||
MOZ_ASSERT(mState == State::ResponsePending);
|
||||
|
||||
mActor = nullptr;
|
||||
|
||||
mResponse = aResponse;
|
||||
|
||||
mState = State::Finishing;
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void SimpleRequestResolver::HandleResponse(nsresult aResponse) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class LSRequestChild;
|
||||
class LSRequestChildCallback;
|
||||
class LSRequestParams;
|
||||
class LSSimpleRequestParams;
|
||||
class Promise;
|
||||
|
@ -39,16 +41,17 @@ class LocalStorageManager2 final : public nsIDOMStorageManager,
|
|||
NS_DECL_NSIDOMSTORAGEMANAGER
|
||||
NS_DECL_NSILOCALSTORAGEMANAGER
|
||||
|
||||
private:
|
||||
~LocalStorageManager2();
|
||||
|
||||
/**
|
||||
* Helper to trigger an LSRequest and resolve/reject the provided promise when
|
||||
* the result comes in. This routine is notable because the LSRequest
|
||||
* mechanism is normally used synchronously from content, but here it's
|
||||
* exposed asynchronously.
|
||||
*/
|
||||
nsresult StartRequest(Promise* aPromise, const LSRequestParams& aParams);
|
||||
LSRequestChild* StartRequest(const LSRequestParams& aParams,
|
||||
LSRequestChildCallback* aCallback);
|
||||
|
||||
private:
|
||||
~LocalStorageManager2();
|
||||
|
||||
/**
|
||||
* Helper to trigger an LSSimpleRequst and resolve/reject the provided promise
|
||||
|
|
Загрузка…
Ссылка в новой задаче