mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-07 03:38:51 +02:00
Backed out changeset e9ef32fa2f2e (bug 1542807) Backed out changeset 8fa0cb199975 (bug 1542807) Backed out changeset 38daf64afe59 (bug 1542807) Backed out changeset e3aee052c495 (bug 1542807) Backed out changeset a71056d4c7cc (bug 1542807) Backed out changeset cf91e7d0a37f (bug 1542807) Backed out changeset eee949e5fd67 (bug 1542807)
1206 lines
38 KiB
C++
1206 lines
38 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ChannelWrapper.h"
|
|
|
|
#include "jsapi.h"
|
|
#include "xpcpublic.h"
|
|
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "mozilla/SystemPrincipal.h"
|
|
|
|
#include "NSSErrorsService.h"
|
|
#include "nsITransportSecurityInfo.h"
|
|
|
|
#include "mozilla/AddonManagerWebAPI.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/Components.h"
|
|
#include "mozilla/ErrorNames.h"
|
|
#include "mozilla/ResultExtensions.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "mozilla/dom/EventBinding.h"
|
|
#include "mozilla/dom/BrowserHost.h"
|
|
#include "mozIThirdPartyUtil.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIContentPolicy.h"
|
|
#include "nsIClassifiedChannel.h"
|
|
#include "nsIHttpChannelInternal.h"
|
|
#include "nsIHttpHeaderVisitor.h"
|
|
#include "nsIInterfaceRequestor.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsILoadContext.h"
|
|
#include "nsIProxiedChannel.h"
|
|
#include "nsIProxyInfo.h"
|
|
#include "nsITraceableChannel.h"
|
|
#include "nsIWritablePropertyBag.h"
|
|
#include "nsIWritablePropertyBag2.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsProxyRelease.h"
|
|
#include "nsPrintfCString.h"
|
|
|
|
using namespace mozilla::dom;
|
|
using namespace JS;
|
|
|
|
namespace mozilla {
|
|
namespace extensions {
|
|
|
|
#define CHANNELWRAPPER_PROP_KEY u"ChannelWrapper::CachedInstance"_ns
|
|
|
|
using CF = nsIClassifiedChannel::ClassificationFlags;
|
|
using MUC = MozUrlClassificationFlags;
|
|
|
|
struct ClassificationStruct {
|
|
uint32_t mFlag;
|
|
MozUrlClassificationFlags mValue;
|
|
};
|
|
static const ClassificationStruct classificationArray[] = {
|
|
{CF::CLASSIFIED_FINGERPRINTING, MUC::Fingerprinting},
|
|
{CF::CLASSIFIED_FINGERPRINTING_CONTENT, MUC::Fingerprinting_content},
|
|
{CF::CLASSIFIED_CRYPTOMINING, MUC::Cryptomining},
|
|
{CF::CLASSIFIED_CRYPTOMINING_CONTENT, MUC::Cryptomining_content},
|
|
{CF::CLASSIFIED_TRACKING, MUC::Tracking},
|
|
{CF::CLASSIFIED_TRACKING_AD, MUC::Tracking_ad},
|
|
{CF::CLASSIFIED_TRACKING_ANALYTICS, MUC::Tracking_analytics},
|
|
{CF::CLASSIFIED_TRACKING_SOCIAL, MUC::Tracking_social},
|
|
{CF::CLASSIFIED_TRACKING_CONTENT, MUC::Tracking_content},
|
|
{CF::CLASSIFIED_SOCIALTRACKING, MUC::Socialtracking},
|
|
{CF::CLASSIFIED_SOCIALTRACKING_FACEBOOK, MUC::Socialtracking_facebook},
|
|
{CF::CLASSIFIED_SOCIALTRACKING_LINKEDIN, MUC::Socialtracking_linkedin},
|
|
{CF::CLASSIFIED_SOCIALTRACKING_TWITTER, MUC::Socialtracking_twitter},
|
|
{CF::CLASSIFIED_ANY_BASIC_TRACKING, MUC::Any_basic_tracking},
|
|
{CF::CLASSIFIED_ANY_STRICT_TRACKING, MUC::Any_strict_tracking},
|
|
{CF::CLASSIFIED_ANY_SOCIAL_TRACKING, MUC::Any_social_tracking}};
|
|
|
|
/*****************************************************************************
|
|
* Lifetimes
|
|
*****************************************************************************/
|
|
|
|
namespace {
|
|
class ChannelListHolder : public LinkedList<ChannelWrapper> {
|
|
public:
|
|
ChannelListHolder() : LinkedList<ChannelWrapper>() {}
|
|
|
|
~ChannelListHolder();
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
ChannelListHolder::~ChannelListHolder() {
|
|
while (ChannelWrapper* wrapper = popFirst()) {
|
|
wrapper->Die();
|
|
}
|
|
}
|
|
|
|
static LinkedList<ChannelWrapper>* GetChannelList() {
|
|
static UniquePtr<ChannelListHolder> sChannelList;
|
|
if (!sChannelList && !PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
|
|
sChannelList.reset(new ChannelListHolder());
|
|
ClearOnShutdown(&sChannelList, ShutdownPhase::XPCOMShutdown);
|
|
}
|
|
return sChannelList.get();
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(ChannelWrapper::ChannelWrapperStub)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(ChannelWrapper::ChannelWrapperStub)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(ChannelWrapper::ChannelWrapperStub, mChannelWrapper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChannelWrapper::ChannelWrapperStub)
|
|
NS_INTERFACE_MAP_ENTRY_TEAROFF(ChannelWrapper, mChannelWrapper)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
/*****************************************************************************
|
|
* Initialization
|
|
*****************************************************************************/
|
|
|
|
ChannelWrapper::ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel)
|
|
: ChannelHolder(aChannel), mParent(aParent) {
|
|
mStub = new ChannelWrapperStub(this);
|
|
|
|
if (auto* list = GetChannelList()) {
|
|
list->insertBack(this);
|
|
}
|
|
}
|
|
|
|
ChannelWrapper::~ChannelWrapper() {
|
|
if (LinkedListElement<ChannelWrapper>::isInList()) {
|
|
LinkedListElement<ChannelWrapper>::remove();
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::Die() {
|
|
if (mStub) {
|
|
mStub->mChannelWrapper = nullptr;
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<ChannelWrapper> ChannelWrapper::Get(const GlobalObject& global,
|
|
nsIChannel* channel) {
|
|
RefPtr<ChannelWrapper> wrapper;
|
|
|
|
nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
|
|
if (props) {
|
|
wrapper = do_GetProperty(props, CHANNELWRAPPER_PROP_KEY);
|
|
if (wrapper) {
|
|
// Assume cached attributes may have changed at this point.
|
|
wrapper->ClearCachedAttributes();
|
|
}
|
|
}
|
|
|
|
if (!wrapper) {
|
|
wrapper = new ChannelWrapper(global.GetAsSupports(), channel);
|
|
if (props) {
|
|
Unused << props->SetPropertyAsInterface(CHANNELWRAPPER_PROP_KEY,
|
|
wrapper->mStub);
|
|
}
|
|
}
|
|
|
|
return wrapper.forget();
|
|
}
|
|
|
|
already_AddRefed<ChannelWrapper> ChannelWrapper::GetRegisteredChannel(
|
|
const GlobalObject& global, uint64_t aChannelId,
|
|
const WebExtensionPolicy& aAddon, nsIRemoteTab* aRemoteTab) {
|
|
ContentParent* contentParent = nullptr;
|
|
if (BrowserHost* host = BrowserHost::GetFrom(aRemoteTab)) {
|
|
contentParent = host->GetActor()->Manager();
|
|
}
|
|
|
|
auto& webreq = WebRequestService::GetSingleton();
|
|
|
|
nsCOMPtr<nsITraceableChannel> channel =
|
|
webreq.GetTraceableChannel(aChannelId, aAddon.Id(), contentParent);
|
|
if (!channel) {
|
|
return nullptr;
|
|
}
|
|
nsCOMPtr<nsIChannel> chan(do_QueryInterface(channel));
|
|
return ChannelWrapper::Get(global, chan);
|
|
}
|
|
|
|
void ChannelWrapper::SetChannel(nsIChannel* aChannel) {
|
|
detail::ChannelHolder::SetChannel(aChannel);
|
|
ClearCachedAttributes();
|
|
ChannelWrapper_Binding::ClearCachedFinalURIValue(this);
|
|
ChannelWrapper_Binding::ClearCachedFinalURLValue(this);
|
|
mFinalURLInfo.reset();
|
|
ChannelWrapper_Binding::ClearCachedProxyInfoValue(this);
|
|
}
|
|
|
|
void ChannelWrapper::ClearCachedAttributes() {
|
|
ChannelWrapper_Binding::ClearCachedRemoteAddressValue(this);
|
|
ChannelWrapper_Binding::ClearCachedStatusCodeValue(this);
|
|
ChannelWrapper_Binding::ClearCachedStatusLineValue(this);
|
|
ChannelWrapper_Binding::ClearCachedUrlClassificationValue(this);
|
|
if (!mFiredErrorEvent) {
|
|
ChannelWrapper_Binding::ClearCachedErrorStringValue(this);
|
|
}
|
|
|
|
ChannelWrapper_Binding::ClearCachedRequestSizeValue(this);
|
|
ChannelWrapper_Binding::ClearCachedResponseSizeValue(this);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ...
|
|
*****************************************************************************/
|
|
|
|
void ChannelWrapper::Cancel(uint32_t aResult, uint32_t aReason,
|
|
ErrorResult& aRv) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
|
|
if (aReason > 0 && loadInfo) {
|
|
loadInfo->SetRequestBlockingReason(aReason);
|
|
}
|
|
rv = chan->Cancel(nsresult(aResult));
|
|
ErrorCheck();
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::RedirectTo(nsIURI* aURI, ErrorResult& aRv) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
rv = chan->RedirectTo(aURI);
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::UpgradeToSecure(ErrorResult& aRv) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
rv = chan->UpgradeToSecure();
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::Suspend(ErrorResult& aRv) {
|
|
if (!mSuspended) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
mSuspendTime = mozilla::TimeStamp::NowUnfuzzed();
|
|
rv = chan->Suspend();
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
} else {
|
|
mSuspended = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::Resume(const nsCString& aText, ErrorResult& aRv) {
|
|
if (mSuspended) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
rv = chan->Resume();
|
|
|
|
PROFILER_MARKER_TEXT("Extension Suspend", NETWORK,
|
|
MarkerTiming::IntervalUntilNowFrom(mSuspendTime),
|
|
aText);
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
} else {
|
|
mSuspended = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetContentType(nsCString& aContentType) const {
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetContentType(aContentType);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::SetContentType(const nsACString& aContentType) {
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->SetContentType(aContentType);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Headers
|
|
*****************************************************************************/
|
|
|
|
namespace {
|
|
|
|
class MOZ_STACK_CLASS HeaderVisitor final : public nsIHttpHeaderVisitor {
|
|
public:
|
|
NS_DECL_NSIHTTPHEADERVISITOR
|
|
|
|
explicit HeaderVisitor(nsTArray<dom::MozHTTPHeader>& aHeaders)
|
|
: mHeaders(aHeaders) {}
|
|
|
|
HeaderVisitor(nsTArray<dom::MozHTTPHeader>& aHeaders,
|
|
const nsCString& aContentTypeHdr)
|
|
: mHeaders(aHeaders), mContentTypeHdr(aContentTypeHdr) {}
|
|
|
|
void VisitRequestHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv) {
|
|
CheckResult(aChannel->VisitRequestHeaders(this), aRv);
|
|
}
|
|
|
|
void VisitResponseHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv) {
|
|
CheckResult(aChannel->VisitResponseHeaders(this), aRv);
|
|
}
|
|
|
|
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
|
|
|
|
// Stub AddRef/Release since this is a stack class.
|
|
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override {
|
|
return ++mRefCnt;
|
|
}
|
|
|
|
NS_IMETHOD_(MozExternalRefCountType) Release(void) override {
|
|
return --mRefCnt;
|
|
}
|
|
|
|
virtual ~HeaderVisitor() { MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0); }
|
|
|
|
private:
|
|
bool CheckResult(nsresult aNSRv, ErrorResult& aRv) {
|
|
if (NS_FAILED(aNSRv)) {
|
|
aRv.Throw(aNSRv);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
nsTArray<dom::MozHTTPHeader>& mHeaders;
|
|
nsCString mContentTypeHdr = VoidCString();
|
|
|
|
nsrefcnt mRefCnt = 0;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
HeaderVisitor::VisitHeader(const nsACString& aHeader,
|
|
const nsACString& aValue) {
|
|
auto dict = mHeaders.AppendElement(fallible);
|
|
if (!dict) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
dict->mName = aHeader;
|
|
|
|
if (!mContentTypeHdr.IsVoid() &&
|
|
aHeader.LowerCaseEqualsLiteral("content-type")) {
|
|
dict->mValue = mContentTypeHdr;
|
|
} else {
|
|
dict->mValue = aValue;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_QUERY_INTERFACE(HeaderVisitor, nsIHttpHeaderVisitor)
|
|
|
|
} // anonymous namespace
|
|
|
|
void ChannelWrapper::GetRequestHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
|
|
ErrorResult& aRv) const {
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
HeaderVisitor visitor(aRetVal);
|
|
visitor.VisitRequestHeaders(chan, aRv);
|
|
} else {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetRequestHeader(const nsCString& aHeader,
|
|
nsCString& aResult,
|
|
ErrorResult& aRv) const {
|
|
aResult.SetIsVoid(true);
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetRequestHeader(aHeader, aResult);
|
|
} else {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetResponseHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
|
|
ErrorResult& aRv) const {
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
HeaderVisitor visitor(aRetVal, mContentTypeHdr);
|
|
visitor.VisitResponseHeaders(chan, aRv);
|
|
} else {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::SetRequestHeader(const nsCString& aHeader,
|
|
const nsCString& aValue, bool aMerge,
|
|
ErrorResult& aRv) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
rv = chan->SetRequestHeader(aHeader, aValue, aMerge);
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::SetResponseHeader(const nsCString& aHeader,
|
|
const nsCString& aValue, bool aMerge,
|
|
ErrorResult& aRv) {
|
|
nsresult rv = NS_ERROR_UNEXPECTED;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
if (aHeader.LowerCaseEqualsLiteral("content-type")) {
|
|
rv = chan->SetContentType(aValue);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mContentTypeHdr = aValue;
|
|
}
|
|
} else {
|
|
rv = chan->SetResponseHeader(aHeader, aValue, aMerge);
|
|
}
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* LoadInfo
|
|
*****************************************************************************/
|
|
|
|
already_AddRefed<nsILoadContext> ChannelWrapper::GetLoadContext() const {
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
nsCOMPtr<nsILoadContext> ctxt;
|
|
NS_QueryNotificationCallbacks(chan, ctxt);
|
|
return ctxt.forget();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
already_AddRefed<Element> ChannelWrapper::GetBrowserElement() const {
|
|
if (nsCOMPtr<nsILoadContext> ctxt = GetLoadContext()) {
|
|
RefPtr<Element> elem;
|
|
if (NS_SUCCEEDED(ctxt->GetTopFrameElement(getter_AddRefs(elem)))) {
|
|
return elem.forget();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static inline bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
|
|
return BasePrincipal::Cast(aPrincipal)->Is<SystemPrincipal>();
|
|
}
|
|
|
|
bool ChannelWrapper::IsSystemLoad() const {
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
if (nsIPrincipal* prin = loadInfo->GetLoadingPrincipal()) {
|
|
return IsSystemPrincipal(prin);
|
|
}
|
|
|
|
if (RefPtr<BrowsingContext> bc = loadInfo->GetBrowsingContext();
|
|
!bc || bc->IsTop()) {
|
|
return false;
|
|
}
|
|
|
|
if (nsIPrincipal* prin = loadInfo->PrincipalToInherit()) {
|
|
return IsSystemPrincipal(prin);
|
|
}
|
|
if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
|
|
return IsSystemPrincipal(prin);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ChannelWrapper::CanModify() const {
|
|
if (WebExtensionPolicy::IsRestrictedURI(FinalURLInfo())) {
|
|
return false;
|
|
}
|
|
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
if (nsIPrincipal* prin = loadInfo->GetLoadingPrincipal()) {
|
|
if (IsSystemPrincipal(prin)) {
|
|
return false;
|
|
}
|
|
|
|
auto* docURI = DocumentURLInfo();
|
|
if (docURI && WebExtensionPolicy::IsRestrictedURI(*docURI)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
already_AddRefed<nsIURI> ChannelWrapper::GetOriginURI() const {
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
|
|
if (prin->GetIsContentPrincipal()) {
|
|
auto* basePrin = BasePrincipal::Cast(prin);
|
|
Unused << basePrin->GetURI(getter_AddRefs(uri));
|
|
}
|
|
}
|
|
}
|
|
return uri.forget();
|
|
}
|
|
|
|
already_AddRefed<nsIURI> ChannelWrapper::GetDocumentURI() const {
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
if (nsIPrincipal* prin = loadInfo->GetLoadingPrincipal()) {
|
|
if (prin->GetIsContentPrincipal()) {
|
|
auto* basePrin = BasePrincipal::Cast(prin);
|
|
Unused << basePrin->GetURI(getter_AddRefs(uri));
|
|
}
|
|
}
|
|
}
|
|
return uri.forget();
|
|
}
|
|
|
|
void ChannelWrapper::GetOriginURL(nsCString& aRetVal) const {
|
|
if (nsCOMPtr<nsIURI> uri = GetOriginURI()) {
|
|
Unused << uri->GetSpec(aRetVal);
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetDocumentURL(nsCString& aRetVal) const {
|
|
if (nsCOMPtr<nsIURI> uri = GetDocumentURI()) {
|
|
Unused << uri->GetSpec(aRetVal);
|
|
}
|
|
}
|
|
|
|
const URLInfo& ChannelWrapper::FinalURLInfo() const {
|
|
if (mFinalURLInfo.isNothing()) {
|
|
ErrorResult rv;
|
|
nsCOMPtr<nsIURI> uri = FinalURI();
|
|
MOZ_ASSERT(uri);
|
|
|
|
// If this is a view-source scheme, get the nested uri.
|
|
while (uri && uri->SchemeIs("view-source")) {
|
|
nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
|
|
if (!nested) {
|
|
break;
|
|
}
|
|
nested->GetInnerURI(getter_AddRefs(uri));
|
|
}
|
|
mFinalURLInfo.emplace(uri.get(), true);
|
|
|
|
// If this is a WebSocket request, mangle the URL so that the scheme is
|
|
// ws: or wss:, as appropriate.
|
|
auto& url = mFinalURLInfo.ref();
|
|
if (Type() == MozContentPolicyType::Websocket &&
|
|
(url.Scheme() == nsGkAtoms::http || url.Scheme() == nsGkAtoms::https)) {
|
|
nsAutoCString spec(url.CSpec());
|
|
spec.Replace(0, 4, "ws"_ns);
|
|
|
|
Unused << NS_NewURI(getter_AddRefs(uri), spec);
|
|
MOZ_RELEASE_ASSERT(uri);
|
|
mFinalURLInfo.reset();
|
|
mFinalURLInfo.emplace(uri.get(), true);
|
|
}
|
|
}
|
|
return mFinalURLInfo.ref();
|
|
}
|
|
|
|
const URLInfo* ChannelWrapper::DocumentURLInfo() const {
|
|
if (mDocumentURLInfo.isNothing()) {
|
|
nsCOMPtr<nsIURI> uri = GetDocumentURI();
|
|
if (!uri) {
|
|
return nullptr;
|
|
}
|
|
mDocumentURLInfo.emplace(uri.get(), true);
|
|
}
|
|
return &mDocumentURLInfo.ref();
|
|
}
|
|
|
|
bool ChannelWrapper::Matches(
|
|
const dom::MozRequestFilter& aFilter, const WebExtensionPolicy* aExtension,
|
|
const dom::MozRequestMatchOptions& aOptions) const {
|
|
if (!HaveChannel()) {
|
|
return false;
|
|
}
|
|
|
|
if (!aFilter.mTypes.IsNull() && !aFilter.mTypes.Value().Contains(Type())) {
|
|
return false;
|
|
}
|
|
|
|
auto& urlInfo = FinalURLInfo();
|
|
if (aFilter.mUrls && !aFilter.mUrls->Matches(urlInfo)) {
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
|
|
bool isPrivate =
|
|
loadInfo && loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
if (!aFilter.mIncognito.IsNull() && aFilter.mIncognito.Value() != isPrivate) {
|
|
return false;
|
|
}
|
|
|
|
if (aExtension) {
|
|
// Verify extension access to private requests
|
|
if (isPrivate && !aExtension->PrivateBrowsingAllowed()) {
|
|
return false;
|
|
}
|
|
|
|
bool isProxy =
|
|
aOptions.mIsProxy && aExtension->HasPermission(nsGkAtoms::proxy);
|
|
// Proxies are allowed access to all urls, including restricted urls.
|
|
if (!aExtension->CanAccessURI(urlInfo, false, !isProxy, true)) {
|
|
return false;
|
|
}
|
|
|
|
// If this isn't the proxy phase of the request, check that the extension
|
|
// has origin permissions for origin that originated the request.
|
|
if (!isProxy) {
|
|
if (IsSystemLoad()) {
|
|
return false;
|
|
}
|
|
|
|
auto origin = DocumentURLInfo();
|
|
// Extensions with the file:-permission may observe requests from file:
|
|
// origins, because such documents can already be modified by content
|
|
// scripts anyway.
|
|
if (origin && !aExtension->CanAccessURI(*origin, false, true, true)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int64_t NormalizeFrameID(nsILoadInfo* aLoadInfo, uint64_t bcID) {
|
|
if (RefPtr<BrowsingContext> bc = aLoadInfo->GetBrowsingContext();
|
|
!bc || bcID == bc->Top()->Id()) {
|
|
return 0;
|
|
}
|
|
return bcID;
|
|
}
|
|
|
|
uint64_t ChannelWrapper::BrowsingContextId(nsILoadInfo* aLoadInfo) const {
|
|
auto frameID = aLoadInfo->GetFrameBrowsingContextID();
|
|
if (!frameID) {
|
|
frameID = aLoadInfo->GetBrowsingContextID();
|
|
}
|
|
return frameID;
|
|
}
|
|
|
|
int64_t ChannelWrapper::FrameId() const {
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
return NormalizeFrameID(loadInfo, BrowsingContextId(loadInfo));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int64_t ChannelWrapper::ParentFrameId() const {
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
if (RefPtr<BrowsingContext> bc = loadInfo->GetBrowsingContext()) {
|
|
if (BrowsingContextId(loadInfo) == bc->Top()->Id()) {
|
|
return -1;
|
|
}
|
|
|
|
uint64_t parentID = -1;
|
|
if (loadInfo->GetFrameBrowsingContextID()) {
|
|
parentID = loadInfo->GetBrowsingContextID();
|
|
} else if (bc->GetParent()) {
|
|
parentID = bc->GetParent()->Id();
|
|
}
|
|
return NormalizeFrameID(loadInfo, parentID);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void ChannelWrapper::GetFrameAncestors(
|
|
dom::Nullable<nsTArray<dom::MozFrameAncestorInfo>>& aFrameAncestors,
|
|
ErrorResult& aRv) const {
|
|
nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
|
|
if (!loadInfo || BrowsingContextId(loadInfo) == 0) {
|
|
aFrameAncestors.SetNull();
|
|
return;
|
|
}
|
|
|
|
nsresult rv = GetFrameAncestors(loadInfo, aFrameAncestors.SetValue());
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
nsresult ChannelWrapper::GetFrameAncestors(
|
|
nsILoadInfo* aLoadInfo,
|
|
nsTArray<dom::MozFrameAncestorInfo>& aFrameAncestors) const {
|
|
const nsTArray<nsCOMPtr<nsIPrincipal>>& ancestorPrincipals =
|
|
aLoadInfo->AncestorPrincipals();
|
|
const nsTArray<uint64_t>& ancestorBrowsingContextIDs =
|
|
aLoadInfo->AncestorBrowsingContextIDs();
|
|
uint32_t size = ancestorPrincipals.Length();
|
|
MOZ_DIAGNOSTIC_ASSERT(size == ancestorBrowsingContextIDs.Length());
|
|
if (size != ancestorBrowsingContextIDs.Length()) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
bool subFrame = aLoadInfo->GetExternalContentPolicyType() ==
|
|
ExtContentPolicy::TYPE_SUBDOCUMENT;
|
|
if (!aFrameAncestors.SetCapacity(subFrame ? size : size + 1, fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// The immediate parent is always the first element in the ancestor arrays,
|
|
// however SUBDOCUMENTs do not have their immediate parent included, so we
|
|
// inject it here. This will force wrapper.parentBrowsingContextId ==
|
|
// wrapper.frameAncestors[0].frameId to always be true. All ather requests
|
|
// already match this way.
|
|
if (subFrame) {
|
|
auto ancestor = aFrameAncestors.AppendElement();
|
|
GetDocumentURL(ancestor->mUrl);
|
|
ancestor->mFrameId = ParentFrameId();
|
|
}
|
|
|
|
for (uint32_t i = 0; i < size; ++i) {
|
|
auto ancestor = aFrameAncestors.AppendElement();
|
|
MOZ_TRY(ancestorPrincipals[i]->GetAsciiSpec(ancestor->mUrl));
|
|
ancestor->mFrameId =
|
|
NormalizeFrameID(aLoadInfo, ancestorBrowsingContextIDs[i]);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Response filtering
|
|
*****************************************************************************/
|
|
|
|
void ChannelWrapper::RegisterTraceableChannel(const WebExtensionPolicy& aAddon,
|
|
nsIRemoteTab* aBrowserParent) {
|
|
// We can't attach new listeners after the response has started, so don't
|
|
// bother registering anything.
|
|
if (mResponseStarted || !CanModify()) {
|
|
return;
|
|
}
|
|
|
|
mAddonEntries.InsertOrUpdate(aAddon.Id(), aBrowserParent);
|
|
if (!mChannelEntry) {
|
|
mChannelEntry = WebRequestService::GetSingleton().RegisterChannel(this);
|
|
CheckEventListeners();
|
|
}
|
|
}
|
|
|
|
already_AddRefed<nsITraceableChannel> ChannelWrapper::GetTraceableChannel(
|
|
nsAtom* aAddonId, dom::ContentParent* aContentParent) const {
|
|
nsCOMPtr<nsIRemoteTab> remoteTab;
|
|
if (mAddonEntries.Get(aAddonId, getter_AddRefs(remoteTab))) {
|
|
ContentParent* contentParent = nullptr;
|
|
if (remoteTab) {
|
|
contentParent =
|
|
BrowserHost::GetFrom(remoteTab.get())->GetActor()->Manager();
|
|
}
|
|
|
|
if (contentParent == aContentParent) {
|
|
nsCOMPtr<nsITraceableChannel> chan = QueryChannel();
|
|
return chan.forget();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ...
|
|
*****************************************************************************/
|
|
|
|
MozContentPolicyType GetContentPolicyType(ExtContentPolicyType aType) {
|
|
// Note: Please keep this function in sync with the external types in
|
|
// nsIContentPolicy.idl
|
|
switch (aType) {
|
|
case ExtContentPolicy::TYPE_DOCUMENT:
|
|
return MozContentPolicyType::Main_frame;
|
|
case ExtContentPolicy::TYPE_SUBDOCUMENT:
|
|
return MozContentPolicyType::Sub_frame;
|
|
case ExtContentPolicy::TYPE_STYLESHEET:
|
|
return MozContentPolicyType::Stylesheet;
|
|
case ExtContentPolicy::TYPE_SCRIPT:
|
|
return MozContentPolicyType::Script;
|
|
case ExtContentPolicy::TYPE_IMAGE:
|
|
return MozContentPolicyType::Image;
|
|
case ExtContentPolicy::TYPE_OBJECT:
|
|
return MozContentPolicyType::Object;
|
|
case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST:
|
|
return MozContentPolicyType::Object_subrequest;
|
|
case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
|
|
return MozContentPolicyType::Xmlhttprequest;
|
|
// TYPE_FETCH returns xmlhttprequest for cross-browser compatibility.
|
|
case ExtContentPolicy::TYPE_FETCH:
|
|
return MozContentPolicyType::Xmlhttprequest;
|
|
case ExtContentPolicy::TYPE_XSLT:
|
|
return MozContentPolicyType::Xslt;
|
|
case ExtContentPolicy::TYPE_PING:
|
|
return MozContentPolicyType::Ping;
|
|
case ExtContentPolicy::TYPE_BEACON:
|
|
return MozContentPolicyType::Beacon;
|
|
case ExtContentPolicy::TYPE_DTD:
|
|
return MozContentPolicyType::Xml_dtd;
|
|
case ExtContentPolicy::TYPE_FONT:
|
|
return MozContentPolicyType::Font;
|
|
case ExtContentPolicy::TYPE_MEDIA:
|
|
return MozContentPolicyType::Media;
|
|
case ExtContentPolicy::TYPE_WEBSOCKET:
|
|
return MozContentPolicyType::Websocket;
|
|
case ExtContentPolicy::TYPE_CSP_REPORT:
|
|
return MozContentPolicyType::Csp_report;
|
|
case ExtContentPolicy::TYPE_IMAGESET:
|
|
return MozContentPolicyType::Imageset;
|
|
case ExtContentPolicy::TYPE_WEB_MANIFEST:
|
|
return MozContentPolicyType::Web_manifest;
|
|
case ExtContentPolicy::TYPE_SPECULATIVE:
|
|
return MozContentPolicyType::Speculative;
|
|
case ExtContentPolicy::TYPE_INVALID:
|
|
case ExtContentPolicy::TYPE_OTHER:
|
|
case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD:
|
|
break;
|
|
// Do not add default: so that compilers can catch the missing case.
|
|
}
|
|
return MozContentPolicyType::Other;
|
|
}
|
|
|
|
MozContentPolicyType ChannelWrapper::Type() const {
|
|
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
|
|
return GetContentPolicyType(loadInfo->GetExternalContentPolicyType());
|
|
}
|
|
return MozContentPolicyType::Other;
|
|
}
|
|
|
|
void ChannelWrapper::GetMethod(nsCString& aMethod) const {
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetRequestMethod(aMethod);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ...
|
|
*****************************************************************************/
|
|
|
|
uint32_t ChannelWrapper::StatusCode() const {
|
|
uint32_t result = 0;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetResponseStatus(&result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void ChannelWrapper::GetStatusLine(nsCString& aRetVal) const {
|
|
nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel();
|
|
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(chan);
|
|
|
|
if (internal) {
|
|
nsAutoCString statusText;
|
|
uint32_t major, minor, status;
|
|
if (NS_FAILED(chan->GetResponseStatus(&status)) ||
|
|
NS_FAILED(chan->GetResponseStatusText(statusText)) ||
|
|
NS_FAILED(internal->GetResponseVersion(&major, &minor))) {
|
|
return;
|
|
}
|
|
|
|
aRetVal = nsPrintfCString("HTTP/%u.%u %u %s", major, minor, status,
|
|
statusText.get());
|
|
}
|
|
}
|
|
|
|
uint64_t ChannelWrapper::ResponseSize() const {
|
|
uint64_t result = 0;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetTransferSize(&result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint64_t ChannelWrapper::RequestSize() const {
|
|
uint64_t result = 0;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
Unused << chan->GetRequestSize(&result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ...
|
|
*****************************************************************************/
|
|
|
|
already_AddRefed<nsIURI> ChannelWrapper::FinalURI() const {
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
NS_GetFinalChannelURI(chan, getter_AddRefs(uri));
|
|
}
|
|
return uri.forget();
|
|
}
|
|
|
|
void ChannelWrapper::GetFinalURL(nsString& aRetVal) const {
|
|
if (HaveChannel()) {
|
|
aRetVal = FinalURLInfo().Spec();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ...
|
|
*****************************************************************************/
|
|
|
|
nsresult FillProxyInfo(MozProxyInfo& aDict, nsIProxyInfo* aProxyInfo) {
|
|
MOZ_TRY(aProxyInfo->GetHost(aDict.mHost));
|
|
MOZ_TRY(aProxyInfo->GetPort(&aDict.mPort));
|
|
MOZ_TRY(aProxyInfo->GetType(aDict.mType));
|
|
MOZ_TRY(aProxyInfo->GetUsername(aDict.mUsername));
|
|
MOZ_TRY(
|
|
aProxyInfo->GetProxyAuthorizationHeader(aDict.mProxyAuthorizationHeader));
|
|
MOZ_TRY(aProxyInfo->GetConnectionIsolationKey(aDict.mConnectionIsolationKey));
|
|
MOZ_TRY(aProxyInfo->GetFailoverTimeout(&aDict.mFailoverTimeout.Construct()));
|
|
|
|
uint32_t flags;
|
|
MOZ_TRY(aProxyInfo->GetFlags(&flags));
|
|
aDict.mProxyDNS = flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ChannelWrapper::GetProxyInfo(dom::Nullable<MozProxyInfo>& aRetVal,
|
|
ErrorResult& aRv) const {
|
|
nsCOMPtr<nsIProxyInfo> proxyInfo;
|
|
if (nsCOMPtr<nsIProxiedChannel> proxied = QueryChannel()) {
|
|
Unused << proxied->GetProxyInfo(getter_AddRefs(proxyInfo));
|
|
}
|
|
if (proxyInfo) {
|
|
MozProxyInfo result;
|
|
|
|
nsresult rv = FillProxyInfo(result, proxyInfo);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
} else {
|
|
aRetVal.SetValue(std::move(result));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetRemoteAddress(nsCString& aRetVal) const {
|
|
aRetVal.SetIsVoid(true);
|
|
if (nsCOMPtr<nsIHttpChannelInternal> internal = QueryChannel()) {
|
|
Unused << internal->GetRemoteAddress(aRetVal);
|
|
}
|
|
}
|
|
|
|
void FillClassification(
|
|
Sequence<mozilla::dom::MozUrlClassificationFlags>& classifications,
|
|
uint32_t classificationFlags, ErrorResult& aRv) {
|
|
if (classificationFlags == 0) {
|
|
return;
|
|
}
|
|
for (const auto& entry : classificationArray) {
|
|
if (classificationFlags & entry.mFlag) {
|
|
if (!classifications.AppendElement(entry.mValue, mozilla::fallible)) {
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::GetUrlClassification(
|
|
dom::Nullable<dom::MozUrlClassification>& aRetVal, ErrorResult& aRv) const {
|
|
MozUrlClassification classification;
|
|
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
|
|
nsCOMPtr<nsIClassifiedChannel> classified = do_QueryInterface(chan);
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
classified,
|
|
"Must be an object inheriting from both nsIHttpChannel and "
|
|
"nsIClassifiedChannel");
|
|
uint32_t classificationFlags;
|
|
classified->GetFirstPartyClassificationFlags(&classificationFlags);
|
|
FillClassification(classification.mFirstParty, classificationFlags, aRv);
|
|
if (aRv.Failed()) {
|
|
return;
|
|
}
|
|
classified->GetThirdPartyClassificationFlags(&classificationFlags);
|
|
FillClassification(classification.mThirdParty, classificationFlags, aRv);
|
|
}
|
|
aRetVal.SetValue(std::move(classification));
|
|
}
|
|
|
|
bool ChannelWrapper::ThirdParty() const {
|
|
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
|
|
components::ThirdPartyUtil::Service();
|
|
if (NS_WARN_IF(!thirdPartyUtil)) {
|
|
return true;
|
|
}
|
|
|
|
nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel();
|
|
if (!chan) {
|
|
return false;
|
|
}
|
|
|
|
bool thirdParty = false;
|
|
nsresult rv = thirdPartyUtil->IsThirdPartyChannel(chan, nullptr, &thirdParty);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return true;
|
|
}
|
|
|
|
return thirdParty;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Error handling
|
|
*****************************************************************************/
|
|
|
|
void ChannelWrapper::GetErrorString(nsString& aRetVal) const {
|
|
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
|
|
nsCOMPtr<nsISupports> securityInfo;
|
|
Unused << chan->GetSecurityInfo(getter_AddRefs(securityInfo));
|
|
if (nsCOMPtr<nsITransportSecurityInfo> tsi =
|
|
do_QueryInterface(securityInfo)) {
|
|
int32_t errorCode = 0;
|
|
tsi->GetErrorCode(&errorCode);
|
|
if (psm::IsNSSErrorCode(errorCode)) {
|
|
nsCOMPtr<nsINSSErrorsService> nsserr =
|
|
do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
|
|
|
|
nsresult rv = psm::GetXPCOMFromNSSError(errorCode);
|
|
if (nsserr && NS_SUCCEEDED(nsserr->GetErrorMessage(rv, aRetVal))) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult status;
|
|
if (NS_SUCCEEDED(chan->GetStatus(&status)) && NS_FAILED(status)) {
|
|
nsAutoCString name;
|
|
GetErrorName(status, name);
|
|
AppendUTF8toUTF16(name, aRetVal);
|
|
} else {
|
|
aRetVal.SetIsVoid(true);
|
|
}
|
|
} else {
|
|
aRetVal.AssignLiteral("NS_ERROR_UNEXPECTED");
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::ErrorCheck() {
|
|
if (!mFiredErrorEvent) {
|
|
nsAutoString error;
|
|
GetErrorString(error);
|
|
if (error.Length()) {
|
|
mChannelEntry = nullptr;
|
|
mFiredErrorEvent = true;
|
|
ChannelWrapper_Binding::ClearCachedErrorStringValue(this);
|
|
FireEvent(u"error"_ns);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* nsIWebRequestListener
|
|
*****************************************************************************/
|
|
|
|
NS_IMPL_ISUPPORTS(ChannelWrapper::RequestListener, nsIStreamListener,
|
|
nsIMultiPartChannelListener, nsIRequestObserver,
|
|
nsIThreadRetargetableStreamListener)
|
|
|
|
ChannelWrapper::RequestListener::~RequestListener() {
|
|
NS_ReleaseOnMainThread("RequestListener::mChannelWrapper",
|
|
mChannelWrapper.forget());
|
|
}
|
|
|
|
nsresult ChannelWrapper::RequestListener::Init() {
|
|
if (nsCOMPtr<nsITraceableChannel> chan = mChannelWrapper->QueryChannel()) {
|
|
return chan->SetNewListener(this, false,
|
|
getter_AddRefs(mOrigStreamListener));
|
|
}
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChannelWrapper::RequestListener::OnStartRequest(nsIRequest* request) {
|
|
MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
|
|
|
|
mChannelWrapper->mChannelEntry = nullptr;
|
|
mChannelWrapper->mResponseStarted = true;
|
|
mChannelWrapper->ErrorCheck();
|
|
mChannelWrapper->FireEvent(u"start"_ns);
|
|
|
|
return mOrigStreamListener->OnStartRequest(request);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChannelWrapper::RequestListener::OnStopRequest(nsIRequest* request,
|
|
nsresult aStatus) {
|
|
MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
|
|
|
|
mChannelWrapper->mChannelEntry = nullptr;
|
|
mChannelWrapper->ErrorCheck();
|
|
mChannelWrapper->FireEvent(u"stop"_ns);
|
|
|
|
return mOrigStreamListener->OnStopRequest(request, aStatus);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChannelWrapper::RequestListener::OnDataAvailable(nsIRequest* request,
|
|
nsIInputStream* inStr,
|
|
uint64_t sourceOffset,
|
|
uint32_t count) {
|
|
MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
|
|
return mOrigStreamListener->OnDataAvailable(request, inStr, sourceOffset,
|
|
count);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChannelWrapper::RequestListener::OnAfterLastPart(nsresult aStatus) {
|
|
MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
|
|
if (nsCOMPtr<nsIMultiPartChannelListener> listener =
|
|
do_QueryInterface(mOrigStreamListener)) {
|
|
return listener->OnAfterLastPart(aStatus);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ChannelWrapper::RequestListener::CheckListenerChain() {
|
|
MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread!");
|
|
nsresult rv;
|
|
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
|
|
do_QueryInterface(mOrigStreamListener, &rv);
|
|
if (retargetableListener) {
|
|
return retargetableListener->CheckListenerChain();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Event dispatching
|
|
*****************************************************************************/
|
|
|
|
void ChannelWrapper::FireEvent(const nsAString& aType) {
|
|
EventInit init;
|
|
init.mBubbles = false;
|
|
init.mCancelable = false;
|
|
|
|
RefPtr<Event> event = Event::Constructor(this, aType, init);
|
|
event->SetTrusted(true);
|
|
|
|
DispatchEvent(*event);
|
|
}
|
|
|
|
void ChannelWrapper::CheckEventListeners() {
|
|
if (!mAddedStreamListener &&
|
|
(HasListenersFor(nsGkAtoms::onerror) ||
|
|
HasListenersFor(nsGkAtoms::onstart) ||
|
|
HasListenersFor(nsGkAtoms::onstop) || mChannelEntry)) {
|
|
auto listener = MakeRefPtr<RequestListener>(this);
|
|
if (!NS_WARN_IF(NS_FAILED(listener->Init()))) {
|
|
mAddedStreamListener = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChannelWrapper::EventListenerAdded(nsAtom* aType) {
|
|
CheckEventListeners();
|
|
}
|
|
|
|
void ChannelWrapper::EventListenerRemoved(nsAtom* aType) {
|
|
CheckEventListeners();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Glue
|
|
*****************************************************************************/
|
|
|
|
JSObject* ChannelWrapper::WrapObject(JSContext* aCx, HandleObject aGivenProto) {
|
|
return ChannelWrapper_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ChannelWrapper)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChannelWrapper)
|
|
NS_INTERFACE_MAP_ENTRY(ChannelWrapper)
|
|
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChannelWrapper,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStub)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChannelWrapper,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStub)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ChannelWrapper,
|
|
DOMEventTargetHelper)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_ADDREF_INHERITED(ChannelWrapper, DOMEventTargetHelper)
|
|
NS_IMPL_RELEASE_INHERITED(ChannelWrapper, DOMEventTargetHelper)
|
|
|
|
} // namespace extensions
|
|
} // namespace mozilla
|