Backed out 2 changesets (bug 1871376, bug 1871377) for causing bp-nu bustages related to RefPtr.h. CLOSED TREE.

Backed out changeset b2f28e905212 (bug 1871377)
Backed out changeset 46f26b4f249d (bug 1871376)
This commit is contained in:
Tamas Szentpeteri 2024-06-11 01:00:02 +03:00
parent 34fe24e2a4
commit 41bd2a4380
15 changed files with 161 additions and 425 deletions

View file

@ -503,7 +503,7 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
return nullptr;
}
SafeRefPtr<InternalRequest> internalRequest = request->GetInternalRequest();
SafeRefPtr<InternalRequest> r = request->GetInternalRequest();
// Restore information of InterceptedHttpChannel if they are passed with the
// Request. Since Request::Constructor would not copy these members.
@ -511,16 +511,16 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
RefPtr<Request> inputReq = &aInput.GetAsRequest();
SafeRefPtr<InternalRequest> inputInReq = inputReq->GetInternalRequest();
if (inputInReq->GetInterceptionTriggeringPrincipalInfo()) {
internalRequest->SetInterceptionContentPolicyType(
r->SetInterceptionContentPolicyType(
inputInReq->InterceptionContentPolicyType());
internalRequest->SetInterceptionTriggeringPrincipalInfo(
r->SetInterceptionTriggeringPrincipalInfo(
MakeUnique<mozilla::ipc::PrincipalInfo>(
*(inputInReq->GetInterceptionTriggeringPrincipalInfo().get())));
if (!inputInReq->InterceptionRedirectChain().IsEmpty()) {
internalRequest->SetInterceptionRedirectChain(
r->SetInterceptionRedirectChain(
inputInReq->InterceptionRedirectChain());
}
internalRequest->SetInterceptionFromThirdParty(
r->SetInterceptionFromThirdParty(
inputInReq->InterceptionFromThirdParty());
}
}
@ -541,7 +541,7 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
if (realm && JS::GetDebuggerObservesWasm(realm)) {
internalRequest->SetSkipWasmCaching();
r->SetSkipWasmCaching();
}
RefPtr<FetchObserver> observer;
@ -550,7 +550,7 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
aInit.mObserve.Value().HandleEvent(*observer);
}
if (NS_IsMainThread() && !internalRequest->GetKeepalive()) {
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
nsCOMPtr<Document> doc;
nsCOMPtr<nsILoadGroup> loadGroup;
@ -588,92 +588,22 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
RefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(
p, observer, signalImpl, request->MozErrors());
RefPtr<FetchDriver> fetch =
new FetchDriver(std::move(internalRequest), principal, loadGroup,
aGlobal->SerialEventTarget(), cookieJarSettings,
nullptr, // PerformanceStorage
isTrackingFetch);
RefPtr<FetchDriver> fetch = new FetchDriver(
std::move(r), principal, loadGroup, aGlobal->SerialEventTarget(),
cookieJarSettings, nullptr, // PerformanceStorage
isTrackingFetch);
fetch->SetDocument(doc);
resolver->SetLoadGroup(loadGroup);
aRv = fetch->Fetch(signalImpl, resolver);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else if (NS_IsMainThread() && internalRequest->GetKeepalive()) {
// keepalive is set to true, route the request through PFetch
// We plan to route all main-thread fetch request through PFetch.
// See Bug 1897129.
RefPtr<FetchChild> actor =
FetchChild::CreateForMainThread(p, signalImpl, observer);
if (!actor) {
NS_WARNING("Could not start keepalive request.");
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
Maybe<ClientInfo> clientInfo(aGlobal->GetClientInfo());
if (clientInfo.isNothing()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
auto* backgroundChild =
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
Unused << NS_WARN_IF(!backgroundChild->SendPFetchConstructor(actor));
FetchOpArgs ipcArgs;
ipcArgs.request() = IPCInternalRequest();
internalRequest->ToIPCInternalRequest(&(ipcArgs.request()),
backgroundChild);
ipcArgs.clientInfo() = clientInfo.ref().ToIPC();
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
nsCOMPtr<Document> doc;
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
nsIPrincipal* principal;
// we don't check if we this request is invoked from a tracking script
// we might add this capability in future.
// See Bug 1892406
if (window) {
doc = window->GetExtantDoc();
if (!doc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
principal = doc->NodePrincipal();
cookieJarSettings = doc->CookieJarSettings();
} else {
principal = aGlobal->PrincipalOrNull();
if (NS_WARN_IF(!principal)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
cookieJarSettings = mozilla::net::CookieJarSettings::Create(principal);
}
if (cookieJarSettings) {
net::CookieJarSettingsArgs csArgs;
net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(csArgs);
ipcArgs.cookieJarSettings() = Some(csArgs);
}
nsresult rv = PrincipalToPrincipalInfo(principal, &ipcArgs.principalInfo());
NS_ENSURE_SUCCESS(rv, nullptr);
ipcArgs.hasCSPEventListener() = false;
ipcArgs.isWorkerRequest() = false;
actor->DoFetchOp(ipcArgs);
return p.forget();
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
if (worker->IsServiceWorker()) {
internalRequest->SetSkipServiceWorker();
r->SetSkipServiceWorker();
}
// PFetch gives no benefit for the fetch in the parent process.
@ -681,7 +611,7 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
// For child process, dispatch fetch op to the parent.
if (StaticPrefs::dom_workers_pFetch_enabled() && !XRE_IsParentProcess()) {
RefPtr<FetchChild> actor =
FetchChild::CreateForWorker(worker, p, signalImpl, observer);
FetchChild::Create(worker, p, signalImpl, observer);
if (!actor) {
NS_WARNING("Could not keep the worker alive.");
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
@ -699,9 +629,9 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
Unused << NS_WARN_IF(!backgroundChild->SendPFetchConstructor(actor));
FetchOpArgs ipcArgs;
ipcArgs.request() = IPCInternalRequest();
internalRequest->ToIPCInternalRequest(&(ipcArgs.request()),
backgroundChild);
r->ToIPCInternalRequest(&(ipcArgs.request()), backgroundChild);
ipcArgs.principalInfo() = worker->GetPrincipalInfo();
ipcArgs.clientInfo() = clientInfo.ref().ToIPC();
@ -733,15 +663,11 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
ipcArgs.isThirdPartyContext() = worker->IsThirdPartyContext();
ipcArgs.isWorkerRequest() = true;
actor->DoFetchOp(ipcArgs);
return p.forget();
}
// Dispatch worker fetch to the main thread
// We do not check if keepalive flag is set for ChromeWorkers
// See Bug 1898664
RefPtr<WorkerFetchResolver> resolver =
WorkerFetchResolver::Create(worker, p, signalImpl, observer);
if (!resolver) {
@ -763,8 +689,7 @@ already_AddRefed<Promise> FetchRequest(nsIGlobalObject* aGlobal,
RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(
resolver, clientInfo.ref(), worker->GlobalScope()->GetController(),
worker->CSPEventListener(), std::move(internalRequest),
std::move(stack));
worker->CSPEventListener(), std::move(r), std::move(stack));
worker->DispatchToMainThread(run.forget());
}

View file

@ -38,12 +38,8 @@ mozilla::ipc::IPCResult FetchChild::Recv__delete__(const nsresult&& aResult) {
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
} else {
MOZ_ASSERT(mIsKeepAliveRequest);
}
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
if (mPromise->State() == Promise::PromiseState::Pending) {
if (NS_FAILED(aResult)) {
@ -69,11 +65,8 @@ mozilla::ipc::IPCResult FetchChild::RecvOnResponseAvailableInternal(
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
}
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
SafeRefPtr<InternalResponse> internalResponse =
InternalResponse::FromIPC(aResponse);
IgnoredErrorResult result;
@ -89,8 +82,7 @@ mozilla::ipc::IPCResult FetchChild::RecvOnResponseAvailableInternal(
mFetchObserver->SetState(FetchState::Complete);
}
nsCOMPtr<nsIGlobalObject> global;
// global = mWorkerRef->Private()->GlobalScope();
global = mPromise->GetGlobalObject();
global = mWorkerRef->Private()->GlobalScope();
RefPtr<Response> response =
new Response(global, internalResponse.clonePtr(), mSignalImpl);
mPromise->MaybeResolve(response);
@ -116,10 +108,8 @@ mozilla::ipc::IPCResult FetchChild::RecvOnResponseEnd(ResponseEndArgs&& aArgs) {
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
}
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
if (aArgs.endReason() == FetchDriverObserver::eAborted) {
FETCH_LOG(
@ -141,10 +131,8 @@ mozilla::ipc::IPCResult FetchChild::RecvOnDataAvailable() {
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
}
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
if (mFetchObserver && mFetchObserver->State() == FetchState::Requesting) {
mFetchObserver->SetState(FetchState::Responding);
@ -158,36 +146,11 @@ mozilla::ipc::IPCResult FetchChild::RecvOnFlushConsoleReport(
if (mIsShutdown) {
return IPC_OK();
}
MOZ_ASSERT(mReporter);
if (NS_IsMainThread()) {
MOZ_ASSERT(mIsKeepAliveRequest);
// extract doc object to flush the console report
for (const auto& report : aReports) {
mReporter->AddConsoleReport(
report.errorFlags(), report.category(),
static_cast<nsContentUtils::PropertiesFile>(report.propertiesFile()),
report.sourceFileURI(), report.lineNumber(), report.columnNumber(),
report.messageName(), report.stringParams());
}
MOZ_ASSERT(mPromise);
nsCOMPtr<nsPIDOMWindowInner> window =
do_QueryInterface(mPromise->GetGlobalObject());
if (window) {
RefPtr<Document> doc = window->GetExtantDoc();
mReporter->FlushConsoleReports(doc);
} else {
mReporter->FlushReportsToConsole(0);
}
return IPC_OK();
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
}
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
MOZ_ASSERT(mReporter);
RefPtr<ThreadSafeWorkerRef> workerRef = mWorkerRef;
nsCOMPtr<nsIConsoleReportCollector> reporter = mReporter;
@ -223,9 +186,10 @@ mozilla::ipc::IPCResult FetchChild::RecvOnFlushConsoleReport(
return IPC_OK();
}
RefPtr<FetchChild> FetchChild::CreateForWorker(
WorkerPrivate* aWorkerPrivate, RefPtr<Promise> aPromise,
RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) {
RefPtr<FetchChild> FetchChild::Create(WorkerPrivate* aWorkerPrivate,
RefPtr<Promise> aPromise,
RefPtr<AbortSignalImpl> aSignalImpl,
RefPtr<FetchObserver> aObserver) {
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
@ -248,15 +212,6 @@ RefPtr<FetchChild> FetchChild::CreateForWorker(
return actor;
}
RefPtr<FetchChild> FetchChild::CreateForMainThread(
RefPtr<Promise> aPromise, RefPtr<AbortSignalImpl> aSignalImpl,
RefPtr<FetchObserver> aObserver) {
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
actor->mIsKeepAliveRequest = true;
return actor;
}
mozilla::ipc::IPCResult FetchChild::RecvOnCSPViolationEvent(
const nsAString& aJSON) {
FETCH_LOG(("FetchChild::RecvOnCSPViolationEvent [%p] aJSON: %s\n", this,
@ -306,28 +261,15 @@ mozilla::ipc::IPCResult FetchChild::RecvOnReportPerformanceTiming(
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
RefPtr<PerformanceStorage> performanceStorage =
mWorkerRef->Private()->GetPerformanceStorage();
if (performanceStorage) {
performanceStorage->AddEntry(
aTiming.entryName(), aTiming.initiatorType(),
MakeUnique<PerformanceTimingData>(aTiming.timingData()));
}
} else if (mIsKeepAliveRequest) {
MOZ_ASSERT(mPromise->GetGlobalObject());
auto* innerWindow = mPromise->GetGlobalObject()->GetAsInnerWindow();
if (innerWindow) {
mozilla::dom::Performance* performance = innerWindow->GetPerformance();
if (performance) {
performance->AsPerformanceStorage()->AddEntry(
aTiming.entryName(), aTiming.initiatorType(),
MakeUnique<PerformanceTimingData>(aTiming.timingData()));
}
}
RefPtr<PerformanceStorage> performanceStorage =
mWorkerRef->Private()->GetPerformanceStorage();
if (performanceStorage) {
performanceStorage->AddEntry(
aTiming.entryName(), aTiming.initiatorType(),
MakeUnique<PerformanceTimingData>(aTiming.timingData()));
}
return IPC_OK();
}
@ -341,32 +283,27 @@ mozilla::ipc::IPCResult FetchChild::RecvOnNotifyNetworkMonitorAlternateStack(
}
// Shutdown has not been called, so mWorkerRef->Private() should be still
// alive.
if (mWorkerRef) {
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerRef->Private());
mWorkerRef->Private()->AssertIsOnWorkerThread();
if (!mOriginStack) {
return IPC_OK();
}
if (!mWorkerChannelInfo) {
mWorkerChannelInfo = MakeRefPtr<WorkerChannelInfo>(
aChannelID, mWorkerRef->Private()->AssociatedBrowsingContextID());
}
// Unfortunately, SerializedStackHolder can only be read on the main thread.
// However, it doesn't block the fetch execution.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [channel = mWorkerChannelInfo,
stack = std::move(mOriginStack)]() mutable {
NotifyNetworkMonitorAlternateStack(channel, std::move(stack));
});
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
if (!mOriginStack) {
return IPC_OK();
}
// Currently we only support sending notifications for worker-thread initiated
// Fetch requests. We need to extend this to main-thread fetch requests as
// well. See Bug 1897424.
if (!mWorkerChannelInfo) {
mWorkerChannelInfo = MakeRefPtr<WorkerChannelInfo>(
aChannelID, mWorkerRef->Private()->AssociatedBrowsingContextID());
}
// Unfortunately, SerializedStackHolder can only be read on the main thread.
// However, it doesn't block the fetch execution.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [channel = mWorkerChannelInfo,
stack = std::move(mOriginStack)]() mutable {
NotifyNetworkMonitorAlternateStack(channel, std::move(stack));
});
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
return IPC_OK();
}
@ -391,7 +328,7 @@ void FetchChild::RunAbortAlgorithm() {
if (mIsShutdown) {
return;
}
if (mWorkerRef || mIsKeepAliveRequest) {
if (mWorkerRef) {
Unused << SendAbortFetchOp();
}
}
@ -424,25 +361,8 @@ void FetchChild::Shutdown() {
Unfollow();
mSignalImpl = nullptr;
mCSPEventListener = nullptr;
// TODO
// For workers we need to skip aborting the fetch requests if keepalive is set
// This is just a quick fix for Worker.
// Usually, we want FetchChild to get destroyed while FetchParent calls
// Senddelete(). When Worker shutdown, FetchChild must call
// FetchChild::SendAbortFetchOp() to parent, and let FetchParent decide if
// canceling the underlying fetch() or not. But currently, we have no good way
// to distinguish whether the abort is intent by script or by Worker/Window
// shutdown. So, we provide a quick fix here, which makes
// FetchChild/FetchParent live a bit longer, but corresponding resources are
// released in FetchChild::Shutdown(), so this quick fix should not cause any
// leaking.
// And we will fix it in Bug 1901082
if (!mIsKeepAliveRequest) {
Unused << SendAbortFetchOp();
}
Unused << SendAbortFetchOp();
mWorkerRef = nullptr;
mIsKeepAliveRequest = false;
}
void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
@ -452,7 +372,6 @@ void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
mSignalImpl = nullptr;
mCSPEventListener = nullptr;
mWorkerRef = nullptr;
mIsKeepAliveRequest = false;
}
} // namespace mozilla::dom

View file

@ -50,16 +50,10 @@ class FetchChild final : public PFetchChild, public AbortFollower {
void SetCSPEventListener(nsICSPEventListener* aListener);
// Creates the actor for worker fetch requests
static RefPtr<FetchChild> CreateForWorker(WorkerPrivate* aWorkerPrivate,
RefPtr<Promise> aPromise,
RefPtr<AbortSignalImpl> aSignalImpl,
RefPtr<FetchObserver> aObserver);
// Creates the actor for main thread fetch requests
static RefPtr<FetchChild> CreateForMainThread(
RefPtr<Promise> aPromise, RefPtr<AbortSignalImpl> aSignalImpl,
RefPtr<FetchObserver> aObserver);
static RefPtr<FetchChild> Create(WorkerPrivate* aWorkerPrivate,
RefPtr<Promise> aPromise,
RefPtr<AbortSignalImpl> aSignalImpl,
RefPtr<FetchObserver> aObserver);
FetchChild(RefPtr<Promise>&& aPromise, RefPtr<AbortSignalImpl>&& aSignalImpl,
RefPtr<FetchObserver>&& aObserver);
@ -82,7 +76,6 @@ class FetchChild final : public PFetchChild, public AbortFollower {
void ActorDestroy(ActorDestroyReason aReason) override;
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
bool mIsKeepAliveRequest;
RefPtr<Promise> mPromise;
RefPtr<AbortSignalImpl> mSignalImpl;
RefPtr<FetchObserver> mFetchObserver;

View file

@ -94,7 +94,6 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
}
mRequest = MakeSafeRefPtr<InternalRequest>(std::move(aArgs.request()));
mIsWorkerFetch = aArgs.isWorkerRequest();
mPrincipalInfo = std::move(aArgs.principalInfo());
mWorkerScript = aArgs.workerScript();
mClientInfo = Some(ClientInfo(aArgs.clientInfo()));
@ -168,26 +167,15 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
}
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
MOZ_ASSERT(self->mRequest);
MOZ_ASSERT(!self->mResponsePromises);
if (self->mIsWorkerFetch) {
self->mResponsePromises =
fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs(
{self->mRequest.clonePtr(), self->mPrincipalInfo,
self->mWorkerScript, self->mClientInfo, self->mController,
self->mCookieJarSettings, self->mNeedOnDataAvailable,
self->mCSPEventListener, self->mAssociatedBrowsingContextID,
self->mBackgroundEventTarget, self->mID,
self->mIsThirdPartyContext})));
} else {
MOZ_ASSERT(self->mRequest->GetKeepalive());
self->mResponsePromises =
fetchService->Fetch(AsVariant(FetchService::MainThreadFetchArgs(
{self->mRequest.clonePtr(), self->mPrincipalInfo,
self->mCookieJarSettings, self->mNeedOnDataAvailable,
self->mCSPEventListener, self->mAssociatedBrowsingContextID,
self->mBackgroundEventTarget, self->mID})));
}
self->mResponsePromises =
fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs(
{self->mRequest.clonePtr(), self->mPrincipalInfo,
self->mWorkerScript, self->mClientInfo, self->mController,
self->mCookieJarSettings, self->mNeedOnDataAvailable,
self->mCSPEventListener, self->mAssociatedBrowsingContextID,
self->mBackgroundEventTarget, self->mID,
self->mIsThirdPartyContext})));
self->mResponsePromises->GetResponseEndPromise()->Then(
GetMainThreadSerialEventTarget(), __func__,
@ -332,17 +320,9 @@ void FetchParent::ActorDestroy(ActorDestroyReason aReason) {
entry.Remove();
FETCH_LOG(("FetchParent::ActorDestroy entry [%p] removed", this));
}
// mRequest can be null when FetchParent has not yet received RecvFetchOp()
if (!mRequest) {
return;
}
// Force to abort the existing fetch.
// Actor can be destoried by shutdown when still fetching.
if (mRequest->GetKeepalive()) {
FETCH_LOG(("Skip aborting fetch as the request is marked keepalive"));
} else {
RecvAbortFetchOp();
}
RecvAbortFetchOp();
// mBackgroundEventTarget = nullptr;
}

View file

@ -98,7 +98,6 @@ class FetchParent final : public PFetchParent {
bool mExtendForCSPEventListener{false};
uint64_t mAssociatedBrowsingContextID{0};
bool mIsThirdPartyContext{true};
bool mIsWorkerFetch{false};
Atomic<bool> mIsDone{false};
Atomic<bool> mActorDestroyed{false};

View file

@ -118,7 +118,6 @@ nsresult FetchService::FetchInstance::Initialize(FetchArgs&& aArgs) {
// Get needed information for FetchDriver from passed-in channel.
if (mArgs.is<NavigationPreloadArgs>()) {
mRequest = mArgs.as<NavigationPreloadArgs>().mRequest.clonePtr();
mArgsType = FetchArgsType::NavigationPreload;
nsIChannel* channel = mArgs.as<NavigationPreloadArgs>().mChannel;
FETCH_LOG(("FetchInstance::Initialize [%p] request[%p], channel[%p]", this,
mRequest.unsafeGetRawPtr(), channel));
@ -164,36 +163,9 @@ nsresult FetchService::FetchInstance::Initialize(FetchArgs&& aArgs) {
// Get PerformanceStorage from channel
mPerformanceStorage = loadInfo->GetPerformanceStorage();
} else if (mArgs.is<MainThreadFetchArgs>()) {
mArgsType = FetchArgsType::MainThreadFetch;
mRequest = mArgs.as<MainThreadFetchArgs>().mRequest.clonePtr();
FETCH_LOG(("FetchInstance::Initialize [%p] request[%p]", this,
mRequest.unsafeGetRawPtr()));
auto principalOrErr = PrincipalInfoToPrincipal(
mArgs.as<MainThreadFetchArgs>().mPrincipalInfo);
if (principalOrErr.isErr()) {
return principalOrErr.unwrapErr();
}
mPrincipal = principalOrErr.unwrap();
nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mArgs.as<MainThreadFetchArgs>().mCookieJarSettings.isSome()) {
net::CookieJarSettings::Deserialize(
mArgs.as<MainThreadFetchArgs>().mCookieJarSettings.ref(),
getter_AddRefs(mCookieJarSettings));
}
return NS_OK;
} else {
mIsWorkerFetch = true;
mRequest = mArgs.as<WorkerFetchArgs>().mRequest.clonePtr();
mArgsType = FetchArgsType::WorkerFetch;
FETCH_LOG(("FetchInstance::Initialize [%p] request[%p]", this,
mRequest.unsafeGetRawPtr()));
@ -243,14 +215,10 @@ RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
GetMainThreadSerialEventTarget(), // MainThreadEventTarget
mCookieJarSettings, // CookieJarSettings
mPerformanceStorage, // PerformanceStorage
// For service workers we set
// tracking fetch to false, but for Keepalive
// requests from main thread this needs to be
// changed. See Bug 1892406
false // IsTrackingFetch
false // IsTrackingFetch
);
if (mArgsType == FetchArgsType::WorkerFetch) {
if (mIsWorkerFetch) {
auto& args = mArgs.as<WorkerFetchArgs>();
mFetchDriver->SetWorkerScript(args.mWorkerScript);
MOZ_ASSERT(args.mClientInfo.isSome());
@ -265,6 +233,7 @@ RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
}
mFetchDriver->EnableNetworkInterceptControl();
mPromises = MakeRefPtr<FetchServicePromises>();
// Call FetchDriver::Fetch to start fetching.
@ -314,20 +283,19 @@ void FetchService::FetchInstance::OnResponseEnd(
FETCH_LOG(("FetchInstance::OnResponseEnd [%p] %s", this,
aReason == eAborted ? "eAborted" : "eNetworking"));
MOZ_ASSERT(mRequest);
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mIsWorkerFetch) {
FlushConsoleReport();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[endArgs = ResponseEndArgs(aReason), actorID = GetActorID()]() {
__func__, [endArgs = ResponseEndArgs(aReason),
actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
FETCH_LOG(("FetchInstance::OnResponseEnd, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnResponseEnd(std::move(endArgs));
}
});
MOZ_ALWAYS_SUCCEEDS(
GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
MOZ_ASSERT(mPromises);
@ -379,20 +347,19 @@ void FetchService::FetchInstance::OnResponseAvailableInternal(
FETCH_LOG(
("FetchInstance::OnResponseAvailableInternal [%p] response body: %p",
this, body.get()));
MOZ_ASSERT(mRequest);
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mIsWorkerFetch) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[response = mResponse.clonePtr(), actorID = GetActorID()]() mutable {
__func__, [response = mResponse.clonePtr(),
actorID = mArgs.as<WorkerFetchArgs>().mActorID]() mutable {
FETCH_LOG(("FetchInstance::OnResponseAvailableInternal Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnResponseAvailableInternal(std::move(response));
}
});
MOZ_ALWAYS_SUCCEEDS(
GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
MOZ_ASSERT(mPromises);
@ -405,11 +372,6 @@ bool FetchService::FetchInstance::NeedOnDataAvailable() {
if (mArgs.is<WorkerFetchArgs>()) {
return mArgs.as<WorkerFetchArgs>().mNeedOnDataAvailable;
}
if (mArgs.is<MainThreadFetchArgs>()) {
return mArgs.as<MainThreadFetchArgs>().mNeedOnDataAvailable;
}
return false;
}
@ -420,42 +382,40 @@ void FetchService::FetchInstance::OnDataAvailable() {
return;
}
MOZ_ASSERT(mRequest);
if (mArgsType != FetchArgsType::NavigationPreload) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction(__func__, [actorID = GetActorID()]() {
if (mIsWorkerFetch) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
FETCH_LOG(("FetchInstance::OnDataAvailable, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnDataAvailable();
}
});
MOZ_ALWAYS_SUCCEEDS(
GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
}
void FetchService::FetchInstance::FlushConsoleReport() {
FETCH_LOG(("FetchInstance::FlushConsoleReport [%p]", this));
if (mArgsType != FetchArgsType::NavigationPreload) {
if (mIsWorkerFetch) {
if (!mReporter) {
return;
}
nsTArray<net::ConsoleReportCollected> reports;
mReporter->StealConsoleReports(reports);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[actorID = GetActorID(), consoleReports = std::move(reports)]() {
__func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID,
consoleReports = std::move(reports)]() {
FETCH_LOG(("FetchInstance::FlushConsolReport, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnFlushConsoleReport(std::move(consoleReports));
}
});
MOZ_ALWAYS_SUCCEEDS(
GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
}
@ -480,19 +440,20 @@ void FetchService::FetchInstance::OnReportPerformanceTiming() {
}
timing.timingData() = performanceTiming->ToIPC();
// Force replace initiatorType for ServiceWorkerNavgationPreload.
if (mArgsType == FetchArgsType::NavigationPreload) {
if (!mIsWorkerFetch) {
timing.initiatorType() = u"navigation"_ns;
} else {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [actorID = GetActorID(), timing = timing]() {
__func__,
[actorID = mArgs.as<WorkerFetchArgs>().mActorID, timing = timing]() {
FETCH_LOG(("FetchInstance::OnReportPerformanceTiming, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnReportPerformanceTiming(std::move(timing));
}
});
MOZ_ALWAYS_SUCCEEDS(
GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
mPromises->ResolveResponseTimingPromise(std::move(timing), __func__);
@ -503,9 +464,7 @@ void FetchService::FetchInstance::OnNotifyNetworkMonitorAlternateStack(
FETCH_LOG(("FetchInstance::OnNotifyNetworkMonitorAlternateStack [%p]", this));
MOZ_ASSERT(mFetchDriver);
MOZ_ASSERT(mPromises);
if (mArgsType != FetchArgsType::WorkerFetch) {
// We need to support this for Main thread fetch requests as well
// See Bug 1897129
if (!mIsWorkerFetch) {
return;
}
@ -524,36 +483,6 @@ void FetchService::FetchInstance::OnNotifyNetworkMonitorAlternateStack(
r, nsIThread::DISPATCH_NORMAL));
}
nsID FetchService::FetchInstance::GetActorID() {
if (mArgsType == FetchArgsType::WorkerFetch) {
return mArgs.as<WorkerFetchArgs>().mActorID;
}
if (mArgsType == FetchArgsType::MainThreadFetch) {
return mArgs.as<MainThreadFetchArgs>().mActorID;
}
MOZ_ASSERT_UNREACHABLE("GetActorID called for unexpected mArgsType");
return {};
}
nsCOMPtr<nsISerialEventTarget>
FetchService::FetchInstance::GetBackgroundEventTarget() {
if (mArgsType == FetchArgsType::WorkerFetch) {
return mArgs.as<WorkerFetchArgs>().mEventTarget;
}
if (mArgsType == FetchArgsType::MainThreadFetch) {
return mArgs.as<MainThreadFetchArgs>().mEventTarget;
}
MOZ_ASSERT_UNREACHABLE(
"GetBackgroundEventTarget called for unexpected mArgsType");
return {};
}
// FetchService
NS_IMPL_ISUPPORTS(FetchService, nsIObserver)
@ -598,23 +527,6 @@ RefPtr<FetchServicePromises> FetchService::NetworkErrorResponse(
});
MOZ_ALWAYS_SUCCEEDS(
args.mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
} else if (aArgs.is<MainThreadFetchArgs>()) {
const MainThreadFetchArgs& args = aArgs.as<MainThreadFetchArgs>();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [aRv, actorID = args.mActorID]() mutable {
FETCH_LOG(
("FetchService::PropagateErrorResponse runnable aError: 0x%X",
(uint32_t)aRv));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnResponseAvailableInternal(
InternalResponse::NetworkError(aRv));
actor->OnResponseEnd(
ResponseEndArgs(FetchDriverObserver::eAborted));
}
});
MOZ_ALWAYS_SUCCEEDS(
args.mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
}
RefPtr<FetchServicePromises> promises = MakeRefPtr<FetchServicePromises>();
@ -723,8 +635,7 @@ RefPtr<FetchServicePromises> FetchService::Fetch(FetchArgs&& aArgs) {
// Create FetchInstance
RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>();
// Call FetchInstance::Initialize() to get needed information for
// FetchDriver
// Call FetchInstance::Initialize() to get needed information for FetchDriver
nsresult rv = fetch->Initialize(std::move(aArgs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NetworkErrorResponse(rv, fetch->Args());

View file

@ -84,13 +84,11 @@ class FetchService final : public nsIObserver {
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Used for ServiceWorkerNavigationPreload
struct NavigationPreloadArgs {
SafeRefPtr<InternalRequest> mRequest;
nsCOMPtr<nsIChannel> mChannel;
};
// Used for content process worker thread fetch()
struct WorkerFetchArgs {
SafeRefPtr<InternalRequest> mRequest;
mozilla::ipc::PrincipalInfo mPrincipalInfo;
@ -106,33 +104,11 @@ class FetchService final : public nsIObserver {
bool mIsThirdPartyContext;
};
// Used for content process main thread fetch()
// Currently this is just used for keepalive request
// This would be further used for sending all main thread fetch requests
// through PFetch
// See Bug 1897129.
struct MainThreadFetchArgs {
SafeRefPtr<InternalRequest> mRequest;
mozilla::ipc::PrincipalInfo mPrincipalInfo;
Maybe<net::CookieJarSettingsArgs> mCookieJarSettings;
bool mNeedOnDataAvailable;
nsCOMPtr<nsICSPEventListener> mCSPEventListener;
uint64_t mAssociatedBrowsingContextID;
nsCOMPtr<nsISerialEventTarget> mEventTarget;
nsID mActorID;
};
struct UnknownArgs {};
using FetchArgs = Variant<NavigationPreloadArgs, WorkerFetchArgs,
MainThreadFetchArgs, UnknownArgs>;
using FetchArgs =
Variant<NavigationPreloadArgs, WorkerFetchArgs, UnknownArgs>;
enum class FetchArgsType {
NavigationPreload,
WorkerFetch,
MainThreadFetch,
Unknown,
};
static already_AddRefed<FetchService> GetInstance();
static RefPtr<FetchServicePromises> NetworkErrorResponse(
@ -184,8 +160,6 @@ class FetchService final : public nsIObserver {
private:
~FetchInstance() = default;
nsCOMPtr<nsISerialEventTarget> GetBackgroundEventTarget();
nsID GetActorID();
SafeRefPtr<InternalRequest> mRequest;
nsCOMPtr<nsIPrincipal> mPrincipal;
@ -196,8 +170,7 @@ class FetchService final : public nsIObserver {
RefPtr<FetchDriver> mFetchDriver;
SafeRefPtr<InternalResponse> mResponse;
RefPtr<FetchServicePromises> mPromises;
FetchArgsType mArgsType;
bool mIsWorkerFetch{false};
};
~FetchService();

View file

@ -67,7 +67,6 @@ struct IPCInternalRequest {
RequestCache cacheMode;
RequestRedirect requestRedirect;
nsString integrity;
bool keepalive;
nsCString fragment;
PrincipalInfo? principalInfo;
PrincipalInfo? interceptionTriggeringPrincipalInfo;

View file

@ -104,7 +104,7 @@ InternalRequest::InternalRequest(
RequestRedirect aRequestRedirect, RequestCredentials aRequestCredentials,
const nsACString& aReferrer, ReferrerPolicy aReferrerPolicy,
RequestPriority aPriority, nsContentPolicyType aContentPolicyType,
const nsAString& aIntegrity, const bool& aKeepAlive)
const nsAString& aIntegrity)
: mMethod(aMethod),
mHeaders(aHeaders),
mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE),
@ -117,8 +117,7 @@ InternalRequest::InternalRequest(
mCacheMode(aCacheMode),
mRedirectMode(aRequestRedirect),
mPriorityMode(aPriority),
mIntegrity(aIntegrity),
mKeepalive(aKeepAlive) {
mIntegrity(aIntegrity) {
MOZ_ASSERT(!aURL.IsEmpty());
AddURL(aURL, aFragment);
}
@ -139,7 +138,6 @@ InternalRequest::InternalRequest(const InternalRequest& aOther,
mRedirectMode(aOther.mRedirectMode),
mPriorityMode(aOther.mPriorityMode),
mIntegrity(aOther.mIntegrity),
mKeepalive(aOther.mKeepalive),
mMozErrors(aOther.mMozErrors),
mFragment(aOther.mFragment),
mSkipServiceWorker(aOther.mSkipServiceWorker),
@ -178,7 +176,6 @@ InternalRequest::InternalRequest(const IPCInternalRequest& aIPCRequest)
mCacheMode(aIPCRequest.cacheMode()),
mRedirectMode(aIPCRequest.requestRedirect()),
mIntegrity(aIPCRequest.integrity()),
mKeepalive(aIPCRequest.keepalive()),
mFragment(aIPCRequest.fragment()),
mEmbedderPolicy(aIPCRequest.embedderPolicy()),
mInterceptionContentPolicyType(static_cast<nsContentPolicyType>(
@ -227,7 +224,6 @@ void InternalRequest::ToIPCInternalRequest(
aIPCRequest->cacheMode() = mCacheMode;
aIPCRequest->requestRedirect() = mRedirectMode;
aIPCRequest->integrity() = mIntegrity;
aIPCRequest->keepalive() = mKeepalive;
aIPCRequest->fragment() = mFragment;
aIPCRequest->embedderPolicy() = mEmbedderPolicy;

View file

@ -95,7 +95,7 @@ class InternalRequest final : public AtomicSafeRefCounted<InternalRequest> {
const nsACString& aReferrer, ReferrerPolicy aReferrerPolicy,
RequestPriority aPriority,
nsContentPolicyType aContentPolicyType,
const nsAString& aIntegrity, const bool& aKeepAlive);
const nsAString& aIntegrity);
explicit InternalRequest(const IPCInternalRequest& aIPCRequest);

View file

@ -25,7 +25,6 @@ struct FetchOpArgs{
bool hasCSPEventListener;
uint64_t associatedBrowsingContextID;
bool isThirdPartyContext;
bool isWorkerRequest;
};
protocol PFetch {

View file

@ -405,7 +405,7 @@ Result<IPCInternalRequest, nsresult> GetIPCInternalRequest(
method, {spec}, ipcHeadersGuard, ipcHeaders, Nothing(), -1,
alternativeDataType, contentPolicyType, referrer, referrerPolicy,
environmentReferrerPolicy, requestMode, requestCredentials, cacheMode,
requestRedirect, integrity, false, fragment, principalInfo,
requestRedirect, integrity, fragment, principalInfo,
interceptionPrincipalInfo, contentPolicyType, redirectChain,
isThirdPartyChannel, embedderPolicy);
}

View file

@ -2,9 +2,15 @@
[Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type]
expected: FAIL
[Request interface: attribute keepalive]
expected: FAIL
[Request interface: attribute isHistoryNavigation]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
[Request interface: attribute isReloadNavigation]
expected: FAIL
@ -28,9 +34,15 @@
[Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type]
expected: FAIL
[Request interface: attribute keepalive]
expected: FAIL
[Request interface: attribute isHistoryNavigation]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
[Request interface: attribute isReloadNavigation]
expected: FAIL
@ -54,9 +66,15 @@
[Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type]
expected: FAIL
[Request interface: attribute keepalive]
expected: FAIL
[Request interface: attribute isHistoryNavigation]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
[Request interface: attribute isReloadNavigation]
expected: FAIL
@ -80,9 +98,15 @@
[Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type]
expected: FAIL
[Request interface: attribute keepalive]
expected: FAIL
[Request interface: attribute isHistoryNavigation]
expected: FAIL
[Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type]
expected: FAIL
[Request interface: attribute isReloadNavigation]
expected: FAIL

View file

@ -0,0 +1,18 @@
[redirect-keepalive.https.any.html]
expected:
if (os == "linux") and fission and not debug and asan: [TIMEOUT, OK]
if (os == "win") and not debug and (processor == "x86"): OK
if (os == "linux") and fission and debug: [TIMEOUT, OK]
if (os == "mac") and debug: TIMEOUT
if (os == "linux") and not fission: OK
if os == "android": OK
[OK, TIMEOUT]
[[keepalive\][iframe\][load\] mixed content redirect; setting up]
expected:
if (os == "linux") and fission and not debug and asan: [TIMEOUT, PASS]
if (os == "win") and not debug and (processor == "x86"): PASS
if (os == "linux") and fission and debug: [TIMEOUT, PASS]
if (os == "mac") and debug: TIMEOUT
if (os == "linux") and not fission: PASS
if os == "android": PASS
[PASS, TIMEOUT]

View file

@ -6,7 +6,7 @@
[A same-origin window[target=''\][features='0'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target=''\][features='0'\] can trigger fetchLater.]
expected: TIMEOUT
@ -17,7 +17,7 @@
[A same-origin window[target=''\][features='1'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target=''\][features='1'\] can trigger fetchLater.]
expected: TIMEOUT
@ -28,7 +28,7 @@
[A same-origin window[target=''\][features='2'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target=''\][features='2'\] can trigger fetchLater.]
expected: TIMEOUT
@ -39,7 +39,7 @@
[A same-origin window[target='_blank'\][features='0'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target='_blank'\][features='0'\] can trigger fetchLater.]
expected: TIMEOUT
@ -50,7 +50,7 @@
[A same-origin window[target='_blank'\][features='1'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target='_blank'\][features='1'\] can trigger fetchLater.]
expected: TIMEOUT
@ -61,7 +61,7 @@
[A same-origin window[target='_blank'\][features='2'\] can trigger fetchLater.]
expected:
if os == "linux": [TIMEOUT, FAIL]
FAIL
TIMEOUT
[A cross-origin window[target='_blank'\][features='2'\] can trigger fetchLater.]
expected: TIMEOUT