mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
9ad680cfc4
We're currently fairly vague and inconsistent about the values we provide to content policy implementations for requestOrigin and requestPrincipal. In some cases they're the triggering principal, sometimes the loading principal, sometimes the channel principal. Our existing content policy implementations which require or expect a loading principal currently retrieve it from the context node. Since no current callers require the principal to be the loading principal, and some already expect it to be the triggering principal (which there's currently no other way to retrieve), a choice was made to pass the triggering principal whenever possible, but use the loading principal to determine the origin URL.
1258 lines
40 KiB
C++
1258 lines
40 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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 "ServiceWorkerEvents.h"
|
|
|
|
#include "nsAutoPtr.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIConsoleReportCollector.h"
|
|
#include "nsIHttpChannelInternal.h"
|
|
#include "nsINetworkInterceptController.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsITimedChannel.h"
|
|
#include "nsIUnicodeDecoder.h"
|
|
#include "nsIUnicodeEncoder.h"
|
|
#include "nsContentPolicyUtils.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsSerializationHelper.h"
|
|
#include "nsQueryObject.h"
|
|
#include "ServiceWorkerClient.h"
|
|
#include "ServiceWorkerManager.h"
|
|
|
|
#include "mozilla/ErrorResult.h"
|
|
#include "mozilla/Move.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/dom/BodyUtil.h"
|
|
#include "mozilla/dom/EncodingUtils.h"
|
|
#include "mozilla/dom/FetchEventBinding.h"
|
|
#include "mozilla/dom/MessagePort.h"
|
|
#include "mozilla/dom/PromiseNativeHandler.h"
|
|
#include "mozilla/dom/PushEventBinding.h"
|
|
#include "mozilla/dom/PushMessageDataBinding.h"
|
|
#include "mozilla/dom/PushUtil.h"
|
|
#include "mozilla/dom/Request.h"
|
|
#include "mozilla/dom/TypedArray.h"
|
|
#include "mozilla/dom/Response.h"
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
|
|
|
|
#include "js/Conversions.h"
|
|
#include "js/TypeDecls.h"
|
|
#include "WorkerPrivate.h"
|
|
#include "xpcpublic.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::dom::workers;
|
|
|
|
namespace {
|
|
|
|
void
|
|
AsyncLog(nsIInterceptedChannel *aInterceptedChannel,
|
|
const nsACString& aRespondWithScriptSpec,
|
|
uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber,
|
|
const nsACString& aMessageName, const nsTArray<nsString>& aParams)
|
|
{
|
|
MOZ_ASSERT(aInterceptedChannel);
|
|
nsCOMPtr<nsIConsoleReportCollector> reporter =
|
|
aInterceptedChannel->GetConsoleReportCollector();
|
|
if (reporter) {
|
|
reporter->AddConsoleReport(nsIScriptError::errorFlag,
|
|
NS_LITERAL_CSTRING("Service Worker Interception"),
|
|
nsContentUtils::eDOM_PROPERTIES,
|
|
aRespondWithScriptSpec,
|
|
aRespondWithLineNumber,
|
|
aRespondWithColumnNumber,
|
|
aMessageName, aParams);
|
|
}
|
|
}
|
|
|
|
template<typename... Params>
|
|
void
|
|
AsyncLog(nsIInterceptedChannel* aInterceptedChannel,
|
|
const nsACString& aRespondWithScriptSpec,
|
|
uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber,
|
|
// We have to list one explicit string so that calls with an
|
|
// nsTArray of params won't end up in here.
|
|
const nsACString& aMessageName, const nsAString& aFirstParam,
|
|
Params&&... aParams)
|
|
{
|
|
nsTArray<nsString> paramsList(sizeof...(Params) + 1);
|
|
StringArrayAppender::Append(paramsList, sizeof...(Params) + 1,
|
|
aFirstParam, Forward<Params>(aParams)...);
|
|
AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber,
|
|
aRespondWithColumnNumber, aMessageName, paramsList);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
BEGIN_WORKERS_NAMESPACE
|
|
|
|
CancelChannelRunnable::CancelChannelRunnable(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
|
nsresult aStatus)
|
|
: mChannel(aChannel)
|
|
, mRegistration(aRegistration)
|
|
, mStatus(aStatus)
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CancelChannelRunnable::Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// TODO: When bug 1204254 is implemented, this time marker should be moved to
|
|
// the point where the body of the network request is complete.
|
|
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
|
|
mChannel->SaveTimeStampsToUnderlyingChannel();
|
|
|
|
mChannel->Cancel(mStatus);
|
|
mRegistration->MaybeScheduleUpdate();
|
|
return NS_OK;
|
|
}
|
|
|
|
FetchEvent::FetchEvent(EventTarget* aOwner)
|
|
: ExtendableEvent(aOwner)
|
|
, mPreventDefaultLineNumber(0)
|
|
, mPreventDefaultColumnNumber(0)
|
|
, mIsReload(false)
|
|
, mWaitToRespond(false)
|
|
{
|
|
}
|
|
|
|
FetchEvent::~FetchEvent()
|
|
{
|
|
}
|
|
|
|
void
|
|
FetchEvent::PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
|
const nsACString& aScriptSpec)
|
|
{
|
|
mChannel = aChannel;
|
|
mRegistration = aRegistration;
|
|
mScriptSpec.Assign(aScriptSpec);
|
|
}
|
|
|
|
/*static*/ already_AddRefed<FetchEvent>
|
|
FetchEvent::Constructor(const GlobalObject& aGlobal,
|
|
const nsAString& aType,
|
|
const FetchEventInit& aOptions,
|
|
ErrorResult& aRv)
|
|
{
|
|
RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports());
|
|
MOZ_ASSERT(owner);
|
|
RefPtr<FetchEvent> e = new FetchEvent(owner);
|
|
bool trusted = e->Init(owner);
|
|
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
|
|
e->SetTrusted(trusted);
|
|
e->SetComposed(aOptions.mComposed);
|
|
e->mRequest = aOptions.mRequest;
|
|
e->mClientId = aOptions.mClientId;
|
|
e->mIsReload = aOptions.mIsReload;
|
|
return e.forget();
|
|
}
|
|
|
|
namespace {
|
|
|
|
class FinishResponse final : public Runnable
|
|
{
|
|
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
|
RefPtr<InternalResponse> mInternalResponse;
|
|
ChannelInfo mWorkerChannelInfo;
|
|
const nsCString mScriptSpec;
|
|
const nsCString mResponseURLSpec;
|
|
|
|
public:
|
|
FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
|
InternalResponse* aInternalResponse,
|
|
const ChannelInfo& aWorkerChannelInfo,
|
|
const nsACString& aScriptSpec,
|
|
const nsACString& aResponseURLSpec)
|
|
: mChannel(aChannel)
|
|
, mInternalResponse(aInternalResponse)
|
|
, mWorkerChannelInfo(aWorkerChannelInfo)
|
|
, mScriptSpec(aScriptSpec)
|
|
, mResponseURLSpec(aResponseURLSpec)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override
|
|
{
|
|
AssertIsOnMainThread();
|
|
|
|
nsCOMPtr<nsIChannel> underlyingChannel;
|
|
nsresult rv = mChannel->GetChannel(getter_AddRefs(underlyingChannel));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ENSURE_TRUE(underlyingChannel, NS_ERROR_UNEXPECTED);
|
|
nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->GetLoadInfo();
|
|
|
|
if (!CSPPermitsResponse(loadInfo)) {
|
|
mChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
|
|
return NS_OK;
|
|
}
|
|
|
|
ChannelInfo channelInfo;
|
|
if (mInternalResponse->GetChannelInfo().IsInitialized()) {
|
|
channelInfo = mInternalResponse->GetChannelInfo();
|
|
} else {
|
|
// We are dealing with a synthesized response here, so fall back to the
|
|
// channel info for the worker script.
|
|
channelInfo = mWorkerChannelInfo;
|
|
}
|
|
rv = mChannel->SetChannelInfo(&channelInfo);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
|
|
return NS_OK;
|
|
}
|
|
|
|
rv = mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(),
|
|
mInternalResponse->GetUnfilteredStatusText());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
|
|
return NS_OK;
|
|
}
|
|
|
|
AutoTArray<InternalHeaders::Entry, 5> entries;
|
|
mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
|
|
for (uint32_t i = 0; i < entries.Length(); ++i) {
|
|
mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
|
|
}
|
|
|
|
loadInfo->MaybeIncreaseTainting(mInternalResponse->GetTainting());
|
|
|
|
rv = mChannel->FinishSynthesizedResponse(mResponseURLSpec);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
|
|
return NS_OK;
|
|
}
|
|
|
|
mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
|
|
mChannel->SaveTimeStampsToUnderlyingChannel();
|
|
|
|
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
|
if (obsService) {
|
|
obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
bool CSPPermitsResponse(nsILoadInfo* aLoadInfo)
|
|
{
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(aLoadInfo);
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsCString url = mInternalResponse->GetUnfilteredURL();
|
|
if (url.IsEmpty()) {
|
|
// Synthetic response. The buck stops at the worker script.
|
|
url = mScriptSpec;
|
|
}
|
|
rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
int16_t decision = nsIContentPolicy::ACCEPT;
|
|
rv = NS_CheckContentLoadPolicy(aLoadInfo->InternalContentPolicyType(),
|
|
uri,
|
|
aLoadInfo->LoadingPrincipal(),
|
|
aLoadInfo->TriggeringPrincipal(),
|
|
aLoadInfo->LoadingNode(),
|
|
EmptyCString(),
|
|
nullptr,
|
|
&decision);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
return decision == nsIContentPolicy::ACCEPT;
|
|
}
|
|
};
|
|
|
|
class RespondWithHandler final : public PromiseNativeHandler
|
|
{
|
|
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
|
|
const RequestMode mRequestMode;
|
|
const RequestRedirect mRequestRedirectMode;
|
|
#ifdef DEBUG
|
|
const bool mIsClientRequest;
|
|
#endif
|
|
const nsCString mScriptSpec;
|
|
const nsString mRequestURL;
|
|
const nsCString mRespondWithScriptSpec;
|
|
const uint32_t mRespondWithLineNumber;
|
|
const uint32_t mRespondWithColumnNumber;
|
|
bool mRequestWasHandled;
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
|
RequestMode aRequestMode, bool aIsClientRequest,
|
|
RequestRedirect aRedirectMode,
|
|
const nsACString& aScriptSpec,
|
|
const nsAString& aRequestURL,
|
|
const nsACString& aRespondWithScriptSpec,
|
|
uint32_t aRespondWithLineNumber,
|
|
uint32_t aRespondWithColumnNumber)
|
|
: mInterceptedChannel(aChannel)
|
|
, mRegistration(aRegistration)
|
|
, mRequestMode(aRequestMode)
|
|
, mRequestRedirectMode(aRedirectMode)
|
|
#ifdef DEBUG
|
|
, mIsClientRequest(aIsClientRequest)
|
|
#endif
|
|
, mScriptSpec(aScriptSpec)
|
|
, mRequestURL(aRequestURL)
|
|
, mRespondWithScriptSpec(aRespondWithScriptSpec)
|
|
, mRespondWithLineNumber(aRespondWithLineNumber)
|
|
, mRespondWithColumnNumber(aRespondWithColumnNumber)
|
|
, mRequestWasHandled(false)
|
|
{
|
|
}
|
|
|
|
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
|
|
|
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
|
|
|
void CancelRequest(nsresult aStatus);
|
|
|
|
void AsyncLog(const nsACString& aMessageName, const nsTArray<nsString>& aParams)
|
|
{
|
|
::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec, mRespondWithLineNumber,
|
|
mRespondWithColumnNumber, aMessageName, aParams);
|
|
}
|
|
|
|
void AsyncLog(const nsACString& aSourceSpec, uint32_t aLine, uint32_t aColumn,
|
|
const nsACString& aMessageName, const nsTArray<nsString>& aParams)
|
|
{
|
|
::AsyncLog(mInterceptedChannel, aSourceSpec, aLine, aColumn, aMessageName,
|
|
aParams);
|
|
}
|
|
|
|
private:
|
|
~RespondWithHandler()
|
|
{
|
|
if (!mRequestWasHandled) {
|
|
::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec,
|
|
mRespondWithLineNumber, mRespondWithColumnNumber,
|
|
NS_LITERAL_CSTRING("InterceptionFailedWithURL"), mRequestURL);
|
|
CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct RespondWithClosure
|
|
{
|
|
nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
|
|
RefPtr<InternalResponse> mInternalResponse;
|
|
ChannelInfo mWorkerChannelInfo;
|
|
const nsCString mScriptSpec;
|
|
const nsCString mResponseURLSpec;
|
|
const nsString mRequestURL;
|
|
const nsCString mRespondWithScriptSpec;
|
|
const uint32_t mRespondWithLineNumber;
|
|
const uint32_t mRespondWithColumnNumber;
|
|
|
|
RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
|
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
|
InternalResponse* aInternalResponse,
|
|
const ChannelInfo& aWorkerChannelInfo,
|
|
const nsCString& aScriptSpec,
|
|
const nsACString& aResponseURLSpec,
|
|
const nsAString& aRequestURL,
|
|
const nsACString& aRespondWithScriptSpec,
|
|
uint32_t aRespondWithLineNumber,
|
|
uint32_t aRespondWithColumnNumber)
|
|
: mInterceptedChannel(aChannel)
|
|
, mRegistration(aRegistration)
|
|
, mInternalResponse(aInternalResponse)
|
|
, mWorkerChannelInfo(aWorkerChannelInfo)
|
|
, mScriptSpec(aScriptSpec)
|
|
, mResponseURLSpec(aResponseURLSpec)
|
|
, mRequestURL(aRequestURL)
|
|
, mRespondWithScriptSpec(aRespondWithScriptSpec)
|
|
, mRespondWithLineNumber(aRespondWithLineNumber)
|
|
, mRespondWithColumnNumber(aRespondWithColumnNumber)
|
|
{
|
|
}
|
|
};
|
|
|
|
void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
|
|
{
|
|
nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
|
|
nsCOMPtr<nsIRunnable> event;
|
|
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
|
AsyncLog(data->mInterceptedChannel, data->mRespondWithScriptSpec,
|
|
data->mRespondWithLineNumber, data->mRespondWithColumnNumber,
|
|
NS_LITERAL_CSTRING("InterceptionFailedWithURL"),
|
|
data->mRequestURL);
|
|
event = new CancelChannelRunnable(data->mInterceptedChannel,
|
|
data->mRegistration,
|
|
NS_ERROR_INTERCEPTION_FAILED);
|
|
} else {
|
|
event = new FinishResponse(data->mInterceptedChannel,
|
|
data->mInternalResponse,
|
|
data->mWorkerChannelInfo,
|
|
data->mScriptSpec,
|
|
data->mResponseURLSpec);
|
|
}
|
|
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
|
|
}
|
|
|
|
class MOZ_STACK_CLASS AutoCancel
|
|
{
|
|
RefPtr<RespondWithHandler> mOwner;
|
|
nsCString mSourceSpec;
|
|
uint32_t mLine;
|
|
uint32_t mColumn;
|
|
nsCString mMessageName;
|
|
nsTArray<nsString> mParams;
|
|
|
|
public:
|
|
AutoCancel(RespondWithHandler* aOwner, const nsString& aRequestURL)
|
|
: mOwner(aOwner)
|
|
, mLine(0)
|
|
, mColumn(0)
|
|
, mMessageName(NS_LITERAL_CSTRING("InterceptionFailedWithURL"))
|
|
{
|
|
mParams.AppendElement(aRequestURL);
|
|
}
|
|
|
|
~AutoCancel()
|
|
{
|
|
if (mOwner) {
|
|
if (mSourceSpec.IsEmpty()) {
|
|
mOwner->AsyncLog(mMessageName, mParams);
|
|
} else {
|
|
mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName, mParams);
|
|
}
|
|
mOwner->CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
|
}
|
|
}
|
|
|
|
// This function steals the error message from a ErrorResult.
|
|
void
|
|
SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv)
|
|
{
|
|
MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
|
|
MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
|
|
|
|
// Storing the error as exception in the JSContext.
|
|
if (!aRv.MaybeSetPendingException(aCx)) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(!aRv.Failed());
|
|
|
|
// Let's take the pending exception.
|
|
JS::Rooted<JS::Value> exn(aCx);
|
|
if (!JS_GetPendingException(aCx, &exn)) {
|
|
return;
|
|
}
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
// Converting the exception in a js::ErrorReport.
|
|
js::ErrorReport report(aCx);
|
|
if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
|
|
JS_ClearPendingException(aCx);
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(mOwner);
|
|
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
|
|
MOZ_ASSERT(mParams.Length() == 1);
|
|
|
|
// Let's store the error message here.
|
|
mMessageName.Assign(report.toStringResult().c_str());
|
|
mParams.Clear();
|
|
}
|
|
|
|
template<typename... Params>
|
|
void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams)
|
|
{
|
|
MOZ_ASSERT(mOwner);
|
|
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
|
|
MOZ_ASSERT(mParams.Length() == 1);
|
|
mMessageName = aMessageName;
|
|
mParams.Clear();
|
|
StringArrayAppender::Append(mParams, sizeof...(Params),
|
|
Forward<Params>(aParams)...);
|
|
}
|
|
|
|
template<typename... Params>
|
|
void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
|
|
uint32_t aLine, uint32_t aColumn,
|
|
const nsACString& aMessageName,
|
|
Params&&... aParams)
|
|
{
|
|
MOZ_ASSERT(mOwner);
|
|
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
|
|
MOZ_ASSERT(mParams.Length() == 1);
|
|
|
|
mSourceSpec = aSourceSpec;
|
|
mLine = aLine;
|
|
mColumn = aColumn;
|
|
|
|
mMessageName = aMessageName;
|
|
mParams.Clear();
|
|
StringArrayAppender::Append(mParams, sizeof...(Params),
|
|
Forward<Params>(aParams)...);
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
mOwner = nullptr;
|
|
}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS0(RespondWithHandler)
|
|
|
|
void
|
|
RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
|
{
|
|
AutoCancel autoCancel(this, mRequestURL);
|
|
|
|
if (!aValue.isObject()) {
|
|
NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value");
|
|
|
|
nsCString sourceSpec;
|
|
uint32_t line = 0;
|
|
uint32_t column = 0;
|
|
nsString valueString;
|
|
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
|
valueString);
|
|
|
|
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
|
|
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
|
|
mRequestURL, valueString);
|
|
return;
|
|
}
|
|
|
|
RefPtr<Response> response;
|
|
nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
|
|
if (NS_FAILED(rv)) {
|
|
nsCString sourceSpec;
|
|
uint32_t line = 0;
|
|
uint32_t column = 0;
|
|
nsString valueString;
|
|
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
|
valueString);
|
|
|
|
autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
|
|
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"),
|
|
mRequestURL, valueString);
|
|
return;
|
|
}
|
|
|
|
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(worker);
|
|
worker->AssertIsOnWorkerThread();
|
|
|
|
// Section "HTTP Fetch", step 3.3:
|
|
// If one of the following conditions is true, return a network error:
|
|
// * response's type is "error".
|
|
// * request's mode is not "no-cors" and response's type is "opaque".
|
|
// * request's redirect mode is not "manual" and response's type is
|
|
// "opaqueredirect".
|
|
// * request's redirect mode is not "follow" and response's url list
|
|
// has more than one item.
|
|
|
|
if (response->Type() == ResponseType::Error) {
|
|
autoCancel.SetCancelMessage(
|
|
NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), mRequestURL);
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin ||
|
|
mRequestMode == RequestMode::Navigate);
|
|
|
|
if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) {
|
|
uint32_t mode = static_cast<uint32_t>(mRequestMode);
|
|
NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value,
|
|
RequestModeValues::strings[mode].length);
|
|
|
|
autoCancel.SetCancelMessage(
|
|
NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"),
|
|
mRequestURL, modeString);
|
|
return;
|
|
}
|
|
|
|
if (mRequestRedirectMode != RequestRedirect::Manual &&
|
|
response->Type() == ResponseType::Opaqueredirect) {
|
|
autoCancel.SetCancelMessage(
|
|
NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), mRequestURL);
|
|
return;
|
|
}
|
|
|
|
if (mRequestRedirectMode != RequestRedirect::Follow && response->Redirected()) {
|
|
autoCancel.SetCancelMessage(
|
|
NS_LITERAL_CSTRING("BadRedirectModeInterceptionWithURL"), mRequestURL);
|
|
return;
|
|
}
|
|
|
|
if (NS_WARN_IF(response->BodyUsed())) {
|
|
autoCancel.SetCancelMessage(
|
|
NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL);
|
|
return;
|
|
}
|
|
|
|
RefPtr<InternalResponse> ir = response->GetInternalResponse();
|
|
if (NS_WARN_IF(!ir)) {
|
|
return;
|
|
}
|
|
// When an opaque response is encountered, we need the original channel's principal
|
|
// to reflect the final URL. Non-opaque responses are either same-origin or CORS-enabled
|
|
// cross-origin responses, which are treated as same-origin by consumers.
|
|
nsCString responseURL;
|
|
if (response->Type() == ResponseType::Opaque) {
|
|
responseURL = ir->GetUnfilteredURL();
|
|
if (NS_WARN_IF(responseURL.IsEmpty())) {
|
|
return;
|
|
}
|
|
}
|
|
nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel,
|
|
mRegistration, ir,
|
|
worker->GetChannelInfo(),
|
|
mScriptSpec,
|
|
responseURL,
|
|
mRequestURL,
|
|
mRespondWithScriptSpec,
|
|
mRespondWithLineNumber,
|
|
mRespondWithColumnNumber));
|
|
nsCOMPtr<nsIInputStream> body;
|
|
ir->GetUnfilteredBody(getter_AddRefs(body));
|
|
// Errors and redirects may not have a body.
|
|
if (body) {
|
|
IgnoredErrorResult error;
|
|
response->SetBodyUsed(aCx, error);
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
autoCancel.SetCancelErrorResult(aCx, error);
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIOutputStream> responseBody;
|
|
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
const uint32_t kCopySegmentSize = 4096;
|
|
|
|
// Depending on how the Response passed to .respondWith() was created, we may
|
|
// get a non-buffered input stream. In addition, in some configurations the
|
|
// destination channel's output stream can be unbuffered. We wrap the output
|
|
// stream side here so that NS_AsyncCopy() works. Wrapping the output side
|
|
// provides the most consistent operation since there are fewer stream types
|
|
// we are writing to. The input stream can be a wide variety of concrete
|
|
// objects which may or many not play well with NS_InputStreamIsBuffered().
|
|
if (!NS_OutputStreamIsBuffered(responseBody)) {
|
|
nsCOMPtr<nsIOutputStream> buffered;
|
|
rv = NS_NewBufferedOutputStream(getter_AddRefs(buffered), responseBody,
|
|
kCopySegmentSize);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
responseBody = buffered;
|
|
}
|
|
|
|
nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
|
|
if (NS_WARN_IF(!stsThread)) {
|
|
return;
|
|
}
|
|
|
|
// XXXnsm, Fix for Bug 1141332 means that if we decide to make this
|
|
// streaming at some point, we'll need a different solution to that bug.
|
|
rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
|
|
kCopySegmentSize, RespondWithCopyComplete, closure.forget());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
} else {
|
|
RespondWithCopyComplete(closure.forget(), NS_OK);
|
|
}
|
|
|
|
MOZ_ASSERT(!closure);
|
|
autoCancel.Reset();
|
|
mRequestWasHandled = true;
|
|
}
|
|
|
|
void
|
|
RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
|
{
|
|
nsCString sourceSpec = mRespondWithScriptSpec;
|
|
uint32_t line = mRespondWithLineNumber;
|
|
uint32_t column = mRespondWithColumnNumber;
|
|
nsString valueString;
|
|
|
|
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
|
|
valueString);
|
|
|
|
::AsyncLog(mInterceptedChannel, sourceSpec, line, column,
|
|
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
|
|
mRequestURL, valueString);
|
|
|
|
CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
|
|
}
|
|
|
|
void
|
|
RespondWithHandler::CancelRequest(nsresult aStatus)
|
|
{
|
|
nsCOMPtr<nsIRunnable> runnable =
|
|
new CancelChannelRunnable(mInterceptedChannel, mRegistration, aStatus);
|
|
NS_DispatchToMainThread(runnable);
|
|
mRequestWasHandled = true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void
|
|
FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv)
|
|
{
|
|
if (EventPhase() == nsIDOMEvent::NONE || mWaitToRespond) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
|
|
// Record where respondWith() was called in the script so we can include the
|
|
// information in any error reporting. We should be guaranteed not to get
|
|
// a file:// string here because service workers require http/https.
|
|
nsCString spec;
|
|
uint32_t line = 0;
|
|
uint32_t column = 0;
|
|
nsJSUtils::GetCallingLocation(aCx, spec, &line, &column);
|
|
|
|
RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
|
|
|
|
nsAutoCString requestURL;
|
|
ir->GetURL(requestURL);
|
|
|
|
StopImmediatePropagation();
|
|
mWaitToRespond = true;
|
|
RefPtr<RespondWithHandler> handler =
|
|
new RespondWithHandler(mChannel, mRegistration, mRequest->Mode(),
|
|
ir->IsClientRequest(), mRequest->Redirect(),
|
|
mScriptSpec, NS_ConvertUTF8toUTF16(requestURL),
|
|
spec, line, column);
|
|
aArg.AppendNativeHandler(handler);
|
|
|
|
// Append directly to the lifecycle promises array. Don't call WaitUntil()
|
|
// because that will lead to double-reporting any errors.
|
|
mPromises.AppendElement(&aArg);
|
|
}
|
|
|
|
void
|
|
FetchEvent::PreventDefault(JSContext* aCx)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
|
|
if (mPreventDefaultScriptSpec.IsEmpty()) {
|
|
// Note when the FetchEvent might have been canceled by script, but don't
|
|
// actually log the location until we are sure it matters. This is
|
|
// determined in ServiceWorkerPrivate.cpp. We only remember the first
|
|
// call to preventDefault() as its the most likely to have actually canceled
|
|
// the event.
|
|
nsJSUtils::GetCallingLocation(aCx, mPreventDefaultScriptSpec,
|
|
&mPreventDefaultLineNumber,
|
|
&mPreventDefaultColumnNumber);
|
|
}
|
|
|
|
Event::PreventDefault(aCx);
|
|
}
|
|
|
|
void
|
|
FetchEvent::ReportCanceled()
|
|
{
|
|
MOZ_ASSERT(!mPreventDefaultScriptSpec.IsEmpty());
|
|
|
|
RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
|
|
nsAutoCString url;
|
|
ir->GetURL(url);
|
|
|
|
// The variadic template provided by StringArrayAppender requires exactly
|
|
// an nsString.
|
|
NS_ConvertUTF8toUTF16 requestURL(url);
|
|
//nsString requestURL;
|
|
//CopyUTF8toUTF16(url, requestURL);
|
|
|
|
::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec,
|
|
mPreventDefaultLineNumber, mPreventDefaultColumnNumber,
|
|
NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), requestURL);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class WaitUntilHandler final : public PromiseNativeHandler
|
|
{
|
|
WorkerPrivate* mWorkerPrivate;
|
|
const nsCString mScope;
|
|
nsCString mSourceSpec;
|
|
uint32_t mLine;
|
|
uint32_t mColumn;
|
|
nsString mRejectValue;
|
|
|
|
~WaitUntilHandler()
|
|
{
|
|
}
|
|
|
|
public:
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
WaitUntilHandler(WorkerPrivate* aWorkerPrivate, JSContext* aCx)
|
|
: mWorkerPrivate(aWorkerPrivate)
|
|
, mScope(mWorkerPrivate->ServiceWorkerScope())
|
|
, mLine(0)
|
|
, mColumn(0)
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
// Save the location of the waitUntil() call itself as a fallback
|
|
// in case the rejection value does not contain any location info.
|
|
nsJSUtils::GetCallingLocation(aCx, mSourceSpec, &mLine, &mColumn);
|
|
}
|
|
|
|
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
|
{
|
|
// do nothing, we are only here to report errors
|
|
}
|
|
|
|
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
|
{
|
|
mWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
nsCString spec;
|
|
uint32_t line = 0;
|
|
uint32_t column = 0;
|
|
nsContentUtils::ExtractErrorValues(aCx, aValue, spec, &line, &column,
|
|
mRejectValue);
|
|
|
|
// only use the extracted location if we found one
|
|
if (!spec.IsEmpty()) {
|
|
mSourceSpec = spec;
|
|
mLine = line;
|
|
mColumn = column;
|
|
}
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
NS_DispatchToMainThread(NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread)));
|
|
}
|
|
|
|
void
|
|
ReportOnMainThread()
|
|
{
|
|
AssertIsOnMainThread();
|
|
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
|
if (!swm) {
|
|
// browser shutdown
|
|
return;
|
|
}
|
|
|
|
// TODO: Make the error message a localized string. (bug 1222720)
|
|
nsString message;
|
|
message.AppendLiteral("Service worker event waitUntil() was passed a "
|
|
"promise that rejected with '");
|
|
message.Append(mRejectValue);
|
|
message.AppendLiteral("'.");
|
|
|
|
// Note, there is a corner case where this won't report to the window
|
|
// that triggered the error. Consider a navigation fetch event that
|
|
// rejects waitUntil() without holding respondWith() open. In this case
|
|
// there is no controlling document yet, the window did call .register()
|
|
// because there is no documeny yet, and the navigation is no longer
|
|
// being intercepted.
|
|
|
|
swm->ReportToAllClients(mScope, message, NS_ConvertUTF8toUTF16(mSourceSpec),
|
|
EmptyString(), mLine, mColumn,
|
|
nsIScriptError::errorFlag);
|
|
}
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS0(WaitUntilHandler)
|
|
|
|
} // anonymous namespace
|
|
|
|
NS_IMPL_ADDREF_INHERITED(FetchEvent, ExtendableEvent)
|
|
NS_IMPL_RELEASE_INHERITED(FetchEvent, ExtendableEvent)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchEvent)
|
|
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent, mRequest)
|
|
|
|
ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
|
|
: Event(aOwner, nullptr, nullptr)
|
|
{
|
|
}
|
|
|
|
void
|
|
ExtendableEvent::WaitUntil(JSContext* aCx, Promise& aPromise, ErrorResult& aRv)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
if (EventPhase() == nsIDOMEvent::NONE) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return;
|
|
}
|
|
|
|
// Append our handler to each waitUntil promise separately so we
|
|
// can record the location in script where waitUntil was called.
|
|
RefPtr<WaitUntilHandler> handler =
|
|
new WaitUntilHandler(GetCurrentThreadWorkerPrivate(), aCx);
|
|
aPromise.AppendNativeHandler(handler);
|
|
|
|
mPromises.AppendElement(&aPromise);
|
|
}
|
|
|
|
already_AddRefed<Promise>
|
|
ExtendableEvent::GetPromise()
|
|
{
|
|
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(worker);
|
|
worker->AssertIsOnWorkerThread();
|
|
|
|
nsIGlobalObject* globalObj = worker->GlobalScope();
|
|
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(globalObj)) {
|
|
return nullptr;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
ErrorResult result;
|
|
RefPtr<Promise> p = Promise::All(cx, Move(mPromises), result);
|
|
if (NS_WARN_IF(result.MaybeSetPendingException(cx))) {
|
|
return nullptr;
|
|
}
|
|
|
|
return p.forget();
|
|
}
|
|
|
|
NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event)
|
|
NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableEvent)
|
|
NS_INTERFACE_MAP_END_INHERITING(Event)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(ExtendableEvent, Event, mPromises)
|
|
|
|
namespace {
|
|
nsresult
|
|
ExtractBytesFromUSVString(const nsAString& aStr, nsTArray<uint8_t>& aBytes)
|
|
{
|
|
MOZ_ASSERT(aBytes.IsEmpty());
|
|
nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
|
|
if (NS_WARN_IF(!encoder)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
int32_t srcLen = aStr.Length();
|
|
int32_t destBufferLen;
|
|
nsresult rv = encoder->GetMaxLength(aStr.BeginReading(), srcLen, &destBufferLen);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (NS_WARN_IF(!aBytes.SetLength(destBufferLen, fallible))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
char* destBuffer = reinterpret_cast<char*>(aBytes.Elements());
|
|
int32_t outLen = destBufferLen;
|
|
rv = encoder->Convert(aStr.BeginReading(), &srcLen, destBuffer, &outLen);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
aBytes.Clear();
|
|
return rv;
|
|
}
|
|
|
|
aBytes.TruncateLength(outLen);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
ExtractBytesFromData(const OwningArrayBufferViewOrArrayBufferOrUSVString& aDataInit, nsTArray<uint8_t>& aBytes)
|
|
{
|
|
if (aDataInit.IsArrayBufferView()) {
|
|
const ArrayBufferView& view = aDataInit.GetAsArrayBufferView();
|
|
if (NS_WARN_IF(!PushUtil::CopyArrayBufferViewToArray(view, aBytes))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
if (aDataInit.IsArrayBuffer()) {
|
|
const ArrayBuffer& buffer = aDataInit.GetAsArrayBuffer();
|
|
if (NS_WARN_IF(!PushUtil::CopyArrayBufferToArray(buffer, aBytes))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
if (aDataInit.IsUSVString()) {
|
|
return ExtractBytesFromUSVString(aDataInit.GetAsUSVString(), aBytes);
|
|
}
|
|
NS_NOTREACHED("Unexpected push message data");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
PushMessageData::PushMessageData(nsISupports* aOwner,
|
|
nsTArray<uint8_t>&& aBytes)
|
|
: mOwner(aOwner), mBytes(Move(aBytes)) {}
|
|
|
|
PushMessageData::~PushMessageData()
|
|
{
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData, mOwner)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessageData)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessageData)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
JSObject*
|
|
PushMessageData::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return mozilla::dom::PushMessageDataBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
void
|
|
PushMessageData::Json(JSContext* cx, JS::MutableHandle<JS::Value> aRetval,
|
|
ErrorResult& aRv)
|
|
{
|
|
if (NS_FAILED(EnsureDecodedText())) {
|
|
aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR);
|
|
return;
|
|
}
|
|
BodyUtil::ConsumeJson(cx, aRetval, mDecodedText, aRv);
|
|
}
|
|
|
|
void
|
|
PushMessageData::Text(nsAString& aData)
|
|
{
|
|
if (NS_SUCCEEDED(EnsureDecodedText())) {
|
|
aData = mDecodedText;
|
|
}
|
|
}
|
|
|
|
void
|
|
PushMessageData::ArrayBuffer(JSContext* cx,
|
|
JS::MutableHandle<JSObject*> aRetval,
|
|
ErrorResult& aRv)
|
|
{
|
|
uint8_t* data = GetContentsCopy();
|
|
if (data) {
|
|
BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv);
|
|
}
|
|
}
|
|
|
|
already_AddRefed<mozilla::dom::Blob>
|
|
PushMessageData::Blob(ErrorResult& aRv)
|
|
{
|
|
uint8_t* data = GetContentsCopy();
|
|
if (data) {
|
|
RefPtr<mozilla::dom::Blob> blob = BodyUtil::ConsumeBlob(
|
|
mOwner, EmptyString(), mBytes.Length(), data, aRv);
|
|
if (blob) {
|
|
return blob.forget();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
nsresult
|
|
PushMessageData::EnsureDecodedText()
|
|
{
|
|
if (mBytes.IsEmpty() || !mDecodedText.IsEmpty()) {
|
|
return NS_OK;
|
|
}
|
|
nsresult rv = BodyUtil::ConsumeText(
|
|
mBytes.Length(),
|
|
reinterpret_cast<uint8_t*>(mBytes.Elements()),
|
|
mDecodedText
|
|
);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
mDecodedText.Truncate();
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
uint8_t*
|
|
PushMessageData::GetContentsCopy()
|
|
{
|
|
uint32_t length = mBytes.Length();
|
|
void* data = malloc(length);
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
memcpy(data, mBytes.Elements(), length);
|
|
return reinterpret_cast<uint8_t*>(data);
|
|
}
|
|
|
|
PushEvent::PushEvent(EventTarget* aOwner)
|
|
: ExtendableEvent(aOwner)
|
|
{
|
|
}
|
|
|
|
already_AddRefed<PushEvent>
|
|
PushEvent::Constructor(mozilla::dom::EventTarget* aOwner,
|
|
const nsAString& aType,
|
|
const PushEventInit& aOptions,
|
|
ErrorResult& aRv)
|
|
{
|
|
RefPtr<PushEvent> e = new PushEvent(aOwner);
|
|
bool trusted = e->Init(aOwner);
|
|
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
|
|
e->SetTrusted(trusted);
|
|
e->SetComposed(aOptions.mComposed);
|
|
if(aOptions.mData.WasPassed()){
|
|
nsTArray<uint8_t> bytes;
|
|
nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return nullptr;
|
|
}
|
|
e->mData = new PushMessageData(aOwner, Move(bytes));
|
|
}
|
|
return e.forget();
|
|
}
|
|
|
|
NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent)
|
|
NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushEvent)
|
|
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(PushEvent, ExtendableEvent, mData)
|
|
|
|
JSObject*
|
|
PushEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return mozilla::dom::PushEventBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
ExtendableMessageEvent::ExtendableMessageEvent(EventTarget* aOwner)
|
|
: ExtendableEvent(aOwner)
|
|
, mData(JS::UndefinedValue())
|
|
{
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
|
|
ExtendableMessageEvent::~ExtendableMessageEvent()
|
|
{
|
|
mData.setUndefined();
|
|
DropJSObjects(this);
|
|
}
|
|
|
|
void
|
|
ExtendableMessageEvent::GetData(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aData,
|
|
ErrorResult& aRv)
|
|
{
|
|
aData.set(mData);
|
|
if (!JS_WrapValue(aCx, aData)) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
}
|
|
}
|
|
|
|
void
|
|
ExtendableMessageEvent::GetSource(Nullable<OwningClientOrServiceWorkerOrMessagePort>& aValue) const
|
|
{
|
|
if (mClient) {
|
|
aValue.SetValue().SetAsClient() = mClient;
|
|
} else if (mServiceWorker) {
|
|
aValue.SetValue().SetAsServiceWorker() = mServiceWorker;
|
|
} else if (mMessagePort) {
|
|
aValue.SetValue().SetAsMessagePort() = mMessagePort;
|
|
} else {
|
|
MOZ_CRASH("Unexpected source value");
|
|
}
|
|
}
|
|
|
|
/* static */ already_AddRefed<ExtendableMessageEvent>
|
|
ExtendableMessageEvent::Constructor(const GlobalObject& aGlobal,
|
|
const nsAString& aType,
|
|
const ExtendableMessageEventInit& aOptions,
|
|
ErrorResult& aRv)
|
|
{
|
|
nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
|
|
return Constructor(t, aType, aOptions, aRv);
|
|
}
|
|
|
|
/* static */ already_AddRefed<ExtendableMessageEvent>
|
|
ExtendableMessageEvent::Constructor(mozilla::dom::EventTarget* aEventTarget,
|
|
const nsAString& aType,
|
|
const ExtendableMessageEventInit& aOptions,
|
|
ErrorResult& aRv)
|
|
{
|
|
RefPtr<ExtendableMessageEvent> event = new ExtendableMessageEvent(aEventTarget);
|
|
|
|
event->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
|
|
bool trusted = event->Init(aEventTarget);
|
|
event->SetTrusted(trusted);
|
|
|
|
event->mData = aOptions.mData;
|
|
event->mOrigin = aOptions.mOrigin;
|
|
event->mLastEventId = aOptions.mLastEventId;
|
|
|
|
if (!aOptions.mSource.IsNull()) {
|
|
if (aOptions.mSource.Value().IsClient()) {
|
|
event->mClient = aOptions.mSource.Value().GetAsClient();
|
|
} else if (aOptions.mSource.Value().IsServiceWorker()){
|
|
event->mServiceWorker = aOptions.mSource.Value().GetAsServiceWorker();
|
|
} else if (aOptions.mSource.Value().IsMessagePort()){
|
|
event->mMessagePort = aOptions.mSource.Value().GetAsMessagePort();
|
|
}
|
|
}
|
|
|
|
event->mPorts.AppendElements(aOptions.mPorts);
|
|
return event.forget();
|
|
}
|
|
|
|
void
|
|
ExtendableMessageEvent::GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts)
|
|
{
|
|
aPorts = mPorts;
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ExtendableMessageEvent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ExtendableMessageEvent, Event)
|
|
tmp->mData.setUndefined();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mClient)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorker)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClient)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableMessageEvent)
|
|
NS_INTERFACE_MAP_END_INHERITING(Event)
|
|
|
|
NS_IMPL_ADDREF_INHERITED(ExtendableMessageEvent, Event)
|
|
NS_IMPL_RELEASE_INHERITED(ExtendableMessageEvent, Event)
|
|
|
|
END_WORKERS_NAMESPACE
|