mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-22 15:17:29 +09:00
Issue #1587 - Part 5: Hook FetchObserver up to the Fetch API
This commit is contained in:
parent
6722d7f4b4
commit
3d2ca85edd
11 changed files with 399 additions and 75 deletions
|
|
@ -39,6 +39,7 @@
|
||||||
#include "mozilla/dom/URLSearchParams.h"
|
#include "mozilla/dom/URLSearchParams.h"
|
||||||
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
#include "mozilla/dom/workers/ServiceWorkerManager.h"
|
||||||
|
|
||||||
|
#include "FetchObserver.h"
|
||||||
#include "InternalRequest.h"
|
#include "InternalRequest.h"
|
||||||
#include "InternalResponse.h"
|
#include "InternalResponse.h"
|
||||||
|
|
||||||
|
|
@ -126,18 +127,20 @@ private:
|
||||||
class WorkerFetchResolver final : public FetchDriverObserver
|
class WorkerFetchResolver final : public FetchDriverObserver
|
||||||
{
|
{
|
||||||
friend class MainThreadFetchRunnable;
|
friend class MainThreadFetchRunnable;
|
||||||
|
friend class WorkerDataAvailableRunnable;
|
||||||
friend class WorkerFetchResponseEndBase;
|
friend class WorkerFetchResponseEndBase;
|
||||||
friend class WorkerFetchResponseEndRunnable;
|
friend class WorkerFetchResponseEndRunnable;
|
||||||
friend class WorkerFetchResponseRunnable;
|
friend class WorkerFetchResponseRunnable;
|
||||||
|
|
||||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||||
RefPtr<FetchSignalProxy> mSignalProxy;
|
RefPtr<FetchSignalProxy> mSignalProxy;
|
||||||
|
RefPtr<FetchObserver> mFetchObserver;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Returns null if worker is shutting down.
|
// Returns null if worker is shutting down.
|
||||||
static already_AddRefed<WorkerFetchResolver>
|
static already_AddRefed<WorkerFetchResolver>
|
||||||
Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise,
|
Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise,
|
||||||
FetchSignal* aSignal)
|
FetchSignal* aSignal, FetchObserver* aObserver)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aWorkerPrivate);
|
MOZ_ASSERT(aWorkerPrivate);
|
||||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
@ -152,7 +155,8 @@ public:
|
||||||
signalProxy = new FetchSignalProxy(aSignal);
|
signalProxy = new FetchSignalProxy(aSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<WorkerFetchResolver> r = new WorkerFetchResolver(proxy, signalProxy);
|
RefPtr<WorkerFetchResolver> r =
|
||||||
|
new WorkerFetchResolver(proxy, signalProxy, aObserver);
|
||||||
return r.forget();
|
return r.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,15 +178,19 @@ public:
|
||||||
void
|
void
|
||||||
OnResponseEnd(FetchDriverObserver::EndReason eReason) override;
|
OnResponseEnd(FetchDriverObserver::EndReason eReason) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
OnDataAvailable() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WorkerFetchResolver(PromiseWorkerProxy* aProxy,
|
WorkerFetchResolver(PromiseWorkerProxy* aProxy,
|
||||||
FetchSignalProxy* aSignalProxy)
|
FetchSignalProxy* aSignalProxy,
|
||||||
|
FetchObserver* aObserver)
|
||||||
: mPromiseProxy(aProxy)
|
: mPromiseProxy(aProxy)
|
||||||
, mSignalProxy(aSignalProxy)
|
, mSignalProxy(aSignalProxy)
|
||||||
|
, mFetchObserver(aObserver)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!NS_IsMainThread());
|
MOZ_ASSERT(!NS_IsMainThread());
|
||||||
MOZ_ASSERT(mPromiseProxy);
|
MOZ_ASSERT(mPromiseProxy);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~WorkerFetchResolver()
|
~WorkerFetchResolver()
|
||||||
|
|
@ -196,12 +204,16 @@ class MainThreadFetchResolver final : public FetchDriverObserver
|
||||||
{
|
{
|
||||||
RefPtr<Promise> mPromise;
|
RefPtr<Promise> mPromise;
|
||||||
RefPtr<Response> mResponse;
|
RefPtr<Response> mResponse;
|
||||||
|
RefPtr<FetchObserver> mFetchObserver;
|
||||||
|
|
||||||
nsCOMPtr<nsIDocument> mDocument;
|
nsCOMPtr<nsIDocument> mDocument;
|
||||||
|
|
||||||
NS_DECL_OWNINGTHREAD
|
NS_DECL_OWNINGTHREAD
|
||||||
public:
|
public:
|
||||||
explicit MainThreadFetchResolver(Promise* aPromise);
|
MainThreadFetchResolver(Promise* aPromise, FetchObserver* aObserver)
|
||||||
|
: mPromise(aPromise)
|
||||||
|
, mFetchObserver(aObserver)
|
||||||
|
{}
|
||||||
|
|
||||||
void
|
void
|
||||||
OnResponseAvailableInternal(InternalResponse* aResponse) override;
|
OnResponseAvailableInternal(InternalResponse* aResponse) override;
|
||||||
|
|
@ -217,9 +229,14 @@ public:
|
||||||
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mFetchObserver = nullptr;
|
||||||
|
|
||||||
FlushConsoleReport();
|
FlushConsoleReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OnDataAvailable() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~MainThreadFetchResolver();
|
~MainThreadFetchResolver();
|
||||||
|
|
||||||
|
|
@ -318,6 +335,12 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||||
// Let's FetchDriver to deal with an already aborted signal.
|
// Let's FetchDriver to deal with an already aborted signal.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefPtr<FetchObserver> observer;
|
||||||
|
if (aInit.mObserve.WasPassed()) {
|
||||||
|
observer = new FetchObserver(aGlobal, signal);
|
||||||
|
aInit.mObserve.Value().HandleEvent(*observer);
|
||||||
|
}
|
||||||
|
|
||||||
if (NS_IsMainThread()) {
|
if (NS_IsMainThread()) {
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
|
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
|
||||||
nsCOMPtr<nsIDocument> doc;
|
nsCOMPtr<nsIDocument> doc;
|
||||||
|
|
@ -344,7 +367,8 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
|
RefPtr<MainThreadFetchResolver> resolver =
|
||||||
|
new MainThreadFetchResolver(p, observer);
|
||||||
RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
|
RefPtr<FetchDriver> fetch = new FetchDriver(r, principal, loadGroup);
|
||||||
fetch->SetDocument(doc);
|
fetch->SetDocument(doc);
|
||||||
resolver->SetDocument(doc);
|
resolver->SetDocument(doc);
|
||||||
|
|
@ -360,7 +384,8 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||||
r->SetSkipServiceWorker();
|
r->SetSkipServiceWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p, signal);
|
RefPtr<WorkerFetchResolver> resolver =
|
||||||
|
WorkerFetchResolver::Create(worker, p, signal, observer);
|
||||||
if (!resolver) {
|
if (!resolver) {
|
||||||
NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
|
NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
|
||||||
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
||||||
|
|
@ -374,11 +399,6 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
|
||||||
return p.forget();
|
return p.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
|
|
||||||
: mPromise(aPromise)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
|
MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
|
||||||
{
|
{
|
||||||
|
|
@ -386,16 +406,39 @@ MainThreadFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
if (aResponse->Type() != ResponseType::Error) {
|
if (aResponse->Type() != ResponseType::Error) {
|
||||||
|
if (mFetchObserver) {
|
||||||
|
mFetchObserver->SetState(FetchState::Complete);
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
|
nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
|
||||||
mResponse = new Response(go, aResponse);
|
mResponse = new Response(go, aResponse);
|
||||||
mPromise->MaybeResolve(mResponse);
|
mPromise->MaybeResolve(mResponse);
|
||||||
} else {
|
} else {
|
||||||
|
if (mFetchObserver) {
|
||||||
|
mFetchObserver->SetState(FetchState::Errored);
|
||||||
|
}
|
||||||
|
|
||||||
ErrorResult result;
|
ErrorResult result;
|
||||||
result.ThrowTypeError<MSG_FETCH_FAILED>();
|
result.ThrowTypeError<MSG_FETCH_FAILED>();
|
||||||
mPromise->MaybeReject(result);
|
mPromise->MaybeReject(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MainThreadFetchResolver::OnDataAvailable()
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
|
if (!mFetchObserver) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFetchObserver->State() == FetchState::Requesting) {
|
||||||
|
mFetchObserver->SetState(FetchState::Responding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MainThreadFetchResolver::~MainThreadFetchResolver()
|
MainThreadFetchResolver::~MainThreadFetchResolver()
|
||||||
{
|
{
|
||||||
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
|
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
|
||||||
|
|
@ -426,10 +469,18 @@ public:
|
||||||
RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise();
|
RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise();
|
||||||
|
|
||||||
if (mInternalResponse->Type() != ResponseType::Error) {
|
if (mInternalResponse->Type() != ResponseType::Error) {
|
||||||
|
if (mResolver->mFetchObserver) {
|
||||||
|
mResolver->mFetchObserver->SetState(FetchState::Complete);
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
|
RefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
|
||||||
RefPtr<Response> response = new Response(global, mInternalResponse);
|
RefPtr<Response> response = new Response(global, mInternalResponse);
|
||||||
promise->MaybeResolve(response);
|
promise->MaybeResolve(response);
|
||||||
} else {
|
} else {
|
||||||
|
if (mResolver->mFetchObserver) {
|
||||||
|
mResolver->mFetchObserver->SetState(FetchState::Errored);
|
||||||
|
}
|
||||||
|
|
||||||
ErrorResult result;
|
ErrorResult result;
|
||||||
result.ThrowTypeError<MSG_FETCH_FAILED>();
|
result.ThrowTypeError<MSG_FETCH_FAILED>();
|
||||||
promise->MaybeReject(result);
|
promise->MaybeReject(result);
|
||||||
|
|
@ -438,18 +489,42 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WorkerDataAvailableRunnable final : public MainThreadWorkerRunnable
|
||||||
|
{
|
||||||
|
RefPtr<WorkerFetchResolver> mResolver;
|
||||||
|
public:
|
||||||
|
WorkerDataAvailableRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
|
WorkerFetchResolver* aResolver)
|
||||||
|
: MainThreadWorkerRunnable(aWorkerPrivate)
|
||||||
|
, mResolver(aResolver)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aWorkerPrivate);
|
||||||
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
|
if (mResolver->mFetchObserver &&
|
||||||
|
mResolver->mFetchObserver->State() == FetchState::Requesting) {
|
||||||
|
mResolver->mFetchObserver->SetState(FetchState::Responding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class WorkerFetchResponseEndBase
|
class WorkerFetchResponseEndBase
|
||||||
{
|
{
|
||||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
protected:
|
||||||
RefPtr<FetchSignalProxy> mSignalProxy;
|
RefPtr<WorkerFetchResolver> mResolver;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WorkerFetchResponseEndBase(PromiseWorkerProxy* aPromiseProxy,
|
explicit WorkerFetchResponseEndBase(WorkerFetchResolver* aResolver)
|
||||||
FetchSignalProxy* aSignalProxy)
|
: mResolver(aResolver)
|
||||||
: mPromiseProxy(aPromiseProxy)
|
|
||||||
, mSignalProxy(aSignalProxy)
|
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(mPromiseProxy);
|
MOZ_ASSERT(aResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -458,14 +533,13 @@ public:
|
||||||
MOZ_ASSERT(aWorkerPrivate);
|
MOZ_ASSERT(aWorkerPrivate);
|
||||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||||
|
|
||||||
RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
mResolver->mPromiseProxy->CleanUp();
|
||||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
||||||
|
|
||||||
mPromiseProxy->CleanUp();
|
mResolver->mFetchObserver = nullptr;
|
||||||
|
|
||||||
if (mSignalProxy) {
|
if (mResolver->mSignalProxy) {
|
||||||
mSignalProxy->Shutdown();
|
mResolver->mSignalProxy->Shutdown();
|
||||||
mSignalProxy = nullptr;
|
mResolver->mSignalProxy = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -473,17 +547,26 @@ public:
|
||||||
class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable
|
class WorkerFetchResponseEndRunnable final : public MainThreadWorkerRunnable
|
||||||
, public WorkerFetchResponseEndBase
|
, public WorkerFetchResponseEndBase
|
||||||
{
|
{
|
||||||
|
FetchDriverObserver::EndReason mReason;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WorkerFetchResponseEndRunnable(PromiseWorkerProxy* aPromiseProxy,
|
WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
FetchSignalProxy* aSignalProxy)
|
WorkerFetchResolver* aResolver,
|
||||||
: MainThreadWorkerRunnable(aPromiseProxy->GetWorkerPrivate())
|
FetchDriverObserver::EndReason aReason)
|
||||||
, WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy)
|
: MainThreadWorkerRunnable(aWorkerPrivate)
|
||||||
|
, WorkerFetchResponseEndBase(aResolver)
|
||||||
|
, mReason(aReason)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||||
{
|
{
|
||||||
|
if (mReason == FetchDriverObserver::eAborted) {
|
||||||
|
RefPtr<Promise> promise = mResolver->mPromiseProxy->WorkerPromise();
|
||||||
|
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
WorkerRunInternal(aWorkerPrivate);
|
WorkerRunInternal(aWorkerPrivate);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -502,10 +585,10 @@ class WorkerFetchResponseEndControlRunnable final : public MainThreadWorkerContr
|
||||||
, public WorkerFetchResponseEndBase
|
, public WorkerFetchResponseEndBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WorkerFetchResponseEndControlRunnable(PromiseWorkerProxy* aPromiseProxy,
|
WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
FetchSignalProxy* aSignalProxy)
|
WorkerFetchResolver* aResolver)
|
||||||
: MainThreadWorkerControlRunnable(aPromiseProxy->GetWorkerPrivate())
|
: MainThreadWorkerControlRunnable(aWorkerPrivate)
|
||||||
, WorkerFetchResponseEndBase(aPromiseProxy, aSignalProxy)
|
, WorkerFetchResponseEndBase(aResolver)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,6 +621,21 @@ WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WorkerFetchResolver::OnDataAvailable()
|
||||||
|
{
|
||||||
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
|
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||||
|
if (mPromiseProxy->CleanedUp()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<WorkerDataAvailableRunnable> r =
|
||||||
|
new WorkerDataAvailableRunnable(mPromiseProxy->GetWorkerPrivate(), this);
|
||||||
|
Unused << r->Dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason)
|
WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason)
|
||||||
{
|
{
|
||||||
|
|
@ -550,11 +648,13 @@ WorkerFetchResolver::OnResponseEnd(FetchDriverObserver::EndReason aReason)
|
||||||
FlushConsoleReport();
|
FlushConsoleReport();
|
||||||
|
|
||||||
RefPtr<WorkerFetchResponseEndRunnable> r =
|
RefPtr<WorkerFetchResponseEndRunnable> r =
|
||||||
new WorkerFetchResponseEndRunnable(mPromiseProxy, mSignalProxy);
|
new WorkerFetchResponseEndRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||||
|
this, aReason);
|
||||||
|
|
||||||
if (!r->Dispatch()) {
|
if (!r->Dispatch()) {
|
||||||
RefPtr<WorkerFetchResponseEndControlRunnable> cr =
|
RefPtr<WorkerFetchResponseEndControlRunnable> cr =
|
||||||
new WorkerFetchResponseEndControlRunnable(mPromiseProxy, mSignalProxy);
|
new WorkerFetchResponseEndControlRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||||
|
this);
|
||||||
// This can fail if the worker thread is canceled or killed causing
|
// This can fail if the worker thread is canceled or killed causing
|
||||||
// the PromiseWorkerProxy to give up its WorkerHolder immediately,
|
// the PromiseWorkerProxy to give up its WorkerHolder immediately,
|
||||||
// allowing the worker thread to become Dead.
|
// allowing the worker thread to become Dead.
|
||||||
|
|
|
||||||
|
|
@ -667,6 +667,31 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Runnable to call the observer OnDataAvailable on the main-thread.
|
||||||
|
class DataAvailableRunnable final : public Runnable
|
||||||
|
{
|
||||||
|
RefPtr<FetchDriverObserver> mObserver;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
|
||||||
|
: mObserver(aObserver)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aObserver);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD
|
||||||
|
Run() override
|
||||||
|
{
|
||||||
|
mObserver->OnDataAvailable();
|
||||||
|
mObserver = nullptr;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
FetchDriver::OnDataAvailable(nsIRequest* aRequest,
|
FetchDriver::OnDataAvailable(nsIRequest* aRequest,
|
||||||
nsISupports* aContext,
|
nsISupports* aContext,
|
||||||
|
|
@ -678,6 +703,18 @@ FetchDriver::OnDataAvailable(nsIRequest* aRequest,
|
||||||
// called between OnStartRequest and OnStopRequest, so we don't need to worry
|
// called between OnStartRequest and OnStopRequest, so we don't need to worry
|
||||||
// about races.
|
// about races.
|
||||||
|
|
||||||
|
if (mObserver) {
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
mObserver->OnDataAvailable();
|
||||||
|
} else {
|
||||||
|
RefPtr<Runnable> runnable = new DataAvailableRunnable(mObserver);
|
||||||
|
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t aRead;
|
uint32_t aRead;
|
||||||
MOZ_ASSERT(mResponse);
|
MOZ_ASSERT(mResponse);
|
||||||
MOZ_ASSERT(mPipeOutputStream);
|
MOZ_ASSERT(mPipeOutputStream);
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ public:
|
||||||
|
|
||||||
virtual void FlushConsoleReport() = 0;
|
virtual void FlushConsoleReport() = 0;
|
||||||
|
|
||||||
|
virtual void OnDataAvailable() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~FetchDriverObserver()
|
virtual ~FetchDriverObserver()
|
||||||
{ };
|
{ };
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "FetchObserver.h"
|
#include "FetchObserver.h"
|
||||||
#include "WorkerPrivate.h"
|
#include "WorkerPrivate.h"
|
||||||
|
#include "mozilla/dom/Event.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
@ -45,10 +46,14 @@ FetchObserver::IsEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchObserver::FetchObserver(nsIGlobalObject* aGlobal,
|
FetchObserver::FetchObserver(nsIGlobalObject* aGlobal,
|
||||||
FetchState aState)
|
FetchSignal* aSignal)
|
||||||
: DOMEventTargetHelper(aGlobal)
|
: DOMEventTargetHelper(aGlobal)
|
||||||
, mState(aState)
|
, mState(FetchState::Requesting)
|
||||||
{}
|
{
|
||||||
|
if (aSignal) {
|
||||||
|
Follow(aSignal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
FetchObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
FetchObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||||
|
|
@ -62,5 +67,51 @@ FetchObserver::State() const
|
||||||
return mState;
|
return mState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FetchObserver::Aborted()
|
||||||
|
{
|
||||||
|
SetState(FetchState::Aborted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FetchObserver::SetState(FetchState aState)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mState < aState);
|
||||||
|
|
||||||
|
if (mState == FetchState::Aborted ||
|
||||||
|
mState == FetchState::Errored ||
|
||||||
|
mState == FetchState::Complete) {
|
||||||
|
// We are already in a final state.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cannot pass from Requesting to Complete directly.
|
||||||
|
if (mState == FetchState::Requesting &&
|
||||||
|
aState == FetchState::Complete) {
|
||||||
|
SetState(FetchState::Responding);
|
||||||
|
}
|
||||||
|
|
||||||
|
mState = aState;
|
||||||
|
|
||||||
|
if (mState == FetchState::Aborted ||
|
||||||
|
mState == FetchState::Errored ||
|
||||||
|
mState == FetchState::Complete) {
|
||||||
|
Unfollow();
|
||||||
|
}
|
||||||
|
|
||||||
|
EventInit init;
|
||||||
|
init.mBubbles = false;
|
||||||
|
init.mCancelable = false;
|
||||||
|
|
||||||
|
// TODO which kind of event should we dispatch here?
|
||||||
|
|
||||||
|
RefPtr<Event> event =
|
||||||
|
Event::Constructor(this, NS_LITERAL_STRING("statechange"), init);
|
||||||
|
event->SetTrusted(true);
|
||||||
|
|
||||||
|
bool dummy;
|
||||||
|
DispatchEvent(event, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
} // dom namespace
|
} // dom namespace
|
||||||
} // mozilla namespace
|
} // mozilla namespace
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@
|
||||||
|
|
||||||
#include "mozilla/DOMEventTargetHelper.h"
|
#include "mozilla/DOMEventTargetHelper.h"
|
||||||
#include "mozilla/dom/FetchObserverBinding.h"
|
#include "mozilla/dom/FetchObserverBinding.h"
|
||||||
|
#include "mozilla/dom/FetchSignal.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
class FetchObserver final : public DOMEventTargetHelper
|
class FetchObserver final : public DOMEventTargetHelper
|
||||||
|
, public FetchSignal::Follower
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
|
@ -22,7 +24,7 @@ public:
|
||||||
static bool
|
static bool
|
||||||
IsEnabled(JSContext* aCx, JSObject* aGlobal);
|
IsEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||||
|
|
||||||
FetchObserver(nsIGlobalObject* aGlobal, FetchState aState);
|
FetchObserver(nsIGlobalObject* aGlobal, FetchSignal* aSignal);
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
|
@ -34,6 +36,12 @@ public:
|
||||||
IMPL_EVENT_HANDLER(requestprogress);
|
IMPL_EVENT_HANDLER(requestprogress);
|
||||||
IMPL_EVENT_HANDLER(responseprogress);
|
IMPL_EVENT_HANDLER(responseprogress);
|
||||||
|
|
||||||
|
void
|
||||||
|
Aborted() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
SetState(FetchState aState);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~FetchObserver() = default;
|
~FetchObserver() = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ function testAbortedFetch() {
|
||||||
var fc = new FetchController();
|
var fc = new FetchController();
|
||||||
fc.abort();
|
fc.abort();
|
||||||
|
|
||||||
fetch('data:,foo', { signal: fc.signal }).then(() => {
|
fetch('slow.sjs', { signal: fc.signal }).then(() => {
|
||||||
ok(false, "Fetch should not return a resolved promise");
|
ok(false, "Fetch should not return a resolved promise");
|
||||||
}, e => {
|
}, e => {
|
||||||
is(e.name, "AbortError", "We have an abort error");
|
is(e.name, "AbortError", "We have an abort error");
|
||||||
|
|
@ -98,7 +98,7 @@ function testAbortedFetch() {
|
||||||
function testFetchAndAbort() {
|
function testFetchAndAbort() {
|
||||||
var fc = new FetchController();
|
var fc = new FetchController();
|
||||||
|
|
||||||
var p = fetch('data:,foo', { signal: fc.signal });
|
var p = fetch('slow.sjs', { signal: fc.signal });
|
||||||
fc.abort();
|
fc.abort();
|
||||||
|
|
||||||
p.then(() => {
|
p.then(() => {
|
||||||
|
|
@ -109,49 +109,21 @@ function testFetchAndAbort() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testWorkerAbortedFetch() {
|
function testWorkerAbortedFetch() {
|
||||||
function worker() {
|
var w = new Worker('worker_fetch_controller.js');
|
||||||
var fc = new FetchController();
|
|
||||||
fc.abort();
|
|
||||||
|
|
||||||
fetch('data:,foo', { signal: fc.signal }).then(() => {
|
|
||||||
postMessage(false);
|
|
||||||
}, e => {
|
|
||||||
postMessage(e.name == "AbortError");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var str = worker.toString();
|
|
||||||
var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' ');
|
|
||||||
var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" }));
|
|
||||||
var w = new Worker(url);
|
|
||||||
w.onmessage = function(e) {
|
w.onmessage = function(e) {
|
||||||
ok(e.data, "Abort + Fetch works in workers");
|
ok(e.data, "Abort + Fetch works in workers");
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
w.postMessage('testWorkerAbortedFetch');
|
||||||
}
|
}
|
||||||
|
|
||||||
function testWorkerFetchAndAbort() {
|
function testWorkerFetchAndAbort() {
|
||||||
function worker() {
|
var w = new Worker('worker_fetch_controller.js');
|
||||||
var fc = new FetchController();
|
|
||||||
|
|
||||||
var p = fetch('data:,foo', { signal: fc.signal });
|
|
||||||
fc.abort();
|
|
||||||
|
|
||||||
p.then(() => {
|
|
||||||
postMessage(false);
|
|
||||||
}, e => {
|
|
||||||
postMessage(e.name == "AbortError");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var str = worker.toString();
|
|
||||||
var content = str.substring(0, str.length - 1).split('\n').splice(1).join(' ');
|
|
||||||
var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" }));
|
|
||||||
var w = new Worker(url);
|
|
||||||
w.onmessage = function(e) {
|
w.onmessage = function(e) {
|
||||||
ok(e.data, "Abort + Fetch works in workers");
|
ok(e.data, "Abort + Fetch works in workers");
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
w.postMessage('testWorkerFetchAndAbort');
|
||||||
}
|
}
|
||||||
|
|
||||||
var steps = [
|
var steps = [
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,125 @@ function is(a, b, msg) {
|
||||||
function testObserver() {
|
function testObserver() {
|
||||||
ok("FetchObserver" in self, "We have a FetchObserver prototype");
|
ok("FetchObserver" in self, "We have a FetchObserver prototype");
|
||||||
|
|
||||||
fetch('data:,foo', { observe: o => {
|
fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', { observe: o => {
|
||||||
|
ok(!!o, "We have an observer");
|
||||||
|
ok(o instanceof FetchObserver, "The correct object has been passed");
|
||||||
|
is(o.state, "requesting", "By default the state is requesting");
|
||||||
|
next();
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testObserveAbort() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
|
||||||
|
fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', {
|
||||||
|
signal: fc.signal,
|
||||||
|
observe: o => {
|
||||||
|
o.onstatechange = () => {
|
||||||
|
ok(true, "StateChange event dispatched");
|
||||||
|
if (o.state == "aborted") {
|
||||||
|
ok(true, "Aborted!");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fc.abort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testObserveComplete() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
|
||||||
|
fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', {
|
||||||
|
signal: fc.signal,
|
||||||
|
observe: o => {
|
||||||
|
o.onstatechange = () => {
|
||||||
|
ok(true, "StateChange event dispatched");
|
||||||
|
if (o.state == "complete") {
|
||||||
|
ok(true, "Operation completed");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testObserveErrored() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
|
||||||
|
fetch('foo: bar', {
|
||||||
|
signal: fc.signal,
|
||||||
|
observe: o => {
|
||||||
|
o.onstatechange = () => {
|
||||||
|
ok(true, "StateChange event dispatched");
|
||||||
|
if (o.state == "errored") {
|
||||||
|
ok(true, "Operation completed");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testObserveResponding() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
|
||||||
|
fetch('http://mochi.test:8888/tests/dom/tests/mochitest/fetch/slow.sjs', {
|
||||||
|
signal: fc.signal,
|
||||||
|
observe: o => {
|
||||||
|
o.onstatechange = () => {
|
||||||
|
if (o.state == "responding") {
|
||||||
|
ok(true, "We have responding events");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function workify(worker) {
|
||||||
|
function methods() {
|
||||||
|
function ok(a, msg) {
|
||||||
|
postMessage( { type: 'check', state: !!a, message: msg });
|
||||||
|
};
|
||||||
|
function is(a, b, msg) {
|
||||||
|
postMessage( { type: 'check', state: a === b, message: msg });
|
||||||
|
};
|
||||||
|
function next() {
|
||||||
|
postMessage( { type: 'finish' });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = methods.toString();
|
||||||
|
var methodsContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n');
|
||||||
|
|
||||||
|
str = worker.toString();
|
||||||
|
var workerContent = str.substring(0, str.length - 1).split('\n').splice(1).join('\n');
|
||||||
|
|
||||||
|
var content = methodsContent + workerContent;
|
||||||
|
var url = URL.createObjectURL(new Blob([content], { type: "application/javascript" }));
|
||||||
|
var w = new Worker(url);
|
||||||
|
w.onmessage = e => {
|
||||||
|
if (e.data.type == 'check') {
|
||||||
|
ok(e.data.state, "WORKER: " + e.data.message);
|
||||||
|
} else if (e.data.type == 'finish') {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
ok(false, "Something went wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var steps = [
|
var steps = [
|
||||||
testObserver,
|
testObserver,
|
||||||
|
testObserveAbort,
|
||||||
|
function() { workify(testObserveAbort); },
|
||||||
|
testObserveComplete,
|
||||||
|
function() { workify(testObserveComplete); },
|
||||||
|
testObserveErrored,
|
||||||
|
function() { workify(testObserveErrored); },
|
||||||
|
testObserveResponding,
|
||||||
|
function() { workify(testObserveResponding); },
|
||||||
];
|
];
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,13 @@ support-files =
|
||||||
reroute.html
|
reroute.html
|
||||||
reroute.js
|
reroute.js
|
||||||
reroute.js^headers^
|
reroute.js^headers^
|
||||||
|
slow.sjs
|
||||||
sw_reroute.js
|
sw_reroute.js
|
||||||
empty.js
|
empty.js
|
||||||
empty.js^headers^
|
empty.js^headers^
|
||||||
worker_temporaryFileBlob.js
|
worker_temporaryFileBlob.js
|
||||||
common_temporaryFileBlob.js
|
common_temporaryFileBlob.js
|
||||||
|
worker_fetch_controller.js
|
||||||
!/dom/xhr/tests/file_XHR_binary1.bin
|
!/dom/xhr/tests/file_XHR_binary1.bin
|
||||||
!/dom/xhr/tests/file_XHR_binary1.bin^headers^
|
!/dom/xhr/tests/file_XHR_binary1.bin^headers^
|
||||||
!/dom/xhr/tests/file_XHR_binary2.bin
|
!/dom/xhr/tests/file_XHR_binary2.bin
|
||||||
|
|
|
||||||
11
dom/tests/mochitest/fetch/slow.sjs
Normal file
11
dom/tests/mochitest/fetch/slow.sjs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
function handleRequest(request, response)
|
||||||
|
{
|
||||||
|
response.processAsync();
|
||||||
|
|
||||||
|
timer = Components.classes["@mozilla.org/timer;1"].
|
||||||
|
createInstance(Components.interfaces.nsITimer);
|
||||||
|
timer.init(function() {
|
||||||
|
response.write("Here the content. But slowly.");
|
||||||
|
response.finish();
|
||||||
|
}, 1000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
|
@ -12,7 +12,8 @@
|
||||||
<body>
|
<body>
|
||||||
<script class="testbody" type="text/javascript">
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
SpecialPowers.pushPrefEnv({"set": [["dom.fetchObserver.enabled", true ]]}, () => {
|
SpecialPowers.pushPrefEnv({"set": [["dom.fetchObserver.enabled", true ],
|
||||||
|
["dom.fetchController.enabled", true ]]}, () => {
|
||||||
let ifr = document.createElement('iframe');
|
let ifr = document.createElement('iframe');
|
||||||
ifr.src = "file_fetch_observer.html";
|
ifr.src = "file_fetch_observer.html";
|
||||||
document.body.appendChild(ifr);
|
document.body.appendChild(ifr);
|
||||||
|
|
|
||||||
27
dom/tests/mochitest/fetch/worker_fetch_controller.js
Normal file
27
dom/tests/mochitest/fetch/worker_fetch_controller.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
function testWorkerAbortedFetch() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
fc.abort();
|
||||||
|
|
||||||
|
fetch('slow.sjs', { signal: fc.signal }).then(() => {
|
||||||
|
postMessage(false);
|
||||||
|
}, e => {
|
||||||
|
postMessage(e.name == "AbortError");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function testWorkerFetchAndAbort() {
|
||||||
|
var fc = new FetchController();
|
||||||
|
|
||||||
|
var p = fetch('slow.sjs', { signal: fc.signal });
|
||||||
|
fc.abort();
|
||||||
|
|
||||||
|
p.then(() => {
|
||||||
|
postMessage(false);
|
||||||
|
}, e => {
|
||||||
|
postMessage(e.name == "AbortError");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onmessage = function(e) {
|
||||||
|
self[e.data]();
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue