Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef mozilla_extensions_ChannelWrapper_h
#define mozilla_extensions_ChannelWrapper_h
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ChannelWrapperBinding.h"
#include "mozilla/WebRequestService.h"
#include "mozilla/extensions/MatchPattern.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "nsAtomHashKeys.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsIMultiPartChannel.h"
#include "nsIStreamListener.h"
#include "nsIRemoteTab.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsInterfaceHashtable.h"
#include "nsIWeakReferenceUtils.h"
#include "nsWrapperCache.h"
#define NS_CHANNELWRAPPER_IID \
{ \
0xc06162d2, 0xb803, 0x43b4, { \
0xaa, 0x31, 0xcf, 0x69, 0x7f, 0x93, 0x68, 0x1c \
} \
}
class nsILoadContext;
class nsITraceableChannel;
namespace mozilla {
namespace dom {
class ContentParent;
class Element;
} // namespace dom
namespace extensions {
namespace detail {
// We need to store our wrapped channel as a weak reference, since channels
// are not cycle collected, and we're going to be hanging this wrapper
// instance off the channel in order to ensure the same channel always has
// the same wrapper.
//
// But since performance matters here, and we don't want to have to
// QueryInterface the channel every time we touch it, we store separate
// nsIChannel and nsIHttpChannel weak references, and check that the WeakPtr
// is alive before returning it.
//
// Although the class is designed for use with generic nsIChannel instances,
// the dependency on weak refs implies that we can only wrap channels that
// implement nsISupportsWeakReference. In practice, only nsHttpChannel meets
// that requirement.
//
// This holder class prevents us from accidentally touching the weak pointer
// members directly from our ChannelWrapper class.
struct ChannelHolder {
explicit ChannelHolder(nsIChannel* aChannel)
: mChannel(do_GetWeakReference(aChannel)), mWeakChannel(aChannel) {}
bool HaveChannel() const { return mChannel && mChannel->IsAlive(); }
void SetChannel(nsIChannel* aChannel) {
mChannel = do_GetWeakReference(aChannel);
mWeakChannel = aChannel;
mWeakHttpChannel.reset();
}
already_AddRefed<nsIChannel> MaybeChannel() const {
if (!HaveChannel()) {
mWeakChannel = nullptr;
}
return do_AddRef(mWeakChannel);
}
already_AddRefed<nsIHttpChannel> MaybeHttpChannel() const {
if (mWeakHttpChannel.isNothing()) {
nsCOMPtr<nsIHttpChannel> chan = QueryChannel();
mWeakHttpChannel.emplace(chan.get());
}
if (!HaveChannel()) {
mWeakHttpChannel.ref() = nullptr;
}
return do_AddRef(mWeakHttpChannel.value());
}
const nsQueryReferent QueryChannel() const {
return do_QueryReferent(mChannel);
}
private:
nsWeakPtr mChannel;
mutable nsIChannel* MOZ_NON_OWNING_REF mWeakChannel;
mutable Maybe<nsIHttpChannel*> MOZ_NON_OWNING_REF mWeakHttpChannel;
};
} // namespace detail
class WebRequestChannelEntry;
class ChannelWrapper final : public DOMEventTargetHelper,
public SupportsWeakPtr,
public LinkedListElement<ChannelWrapper>,
private detail::ChannelHolder {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ChannelWrapper, DOMEventTargetHelper)
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CHANNELWRAPPER_IID)
void Die();
static already_AddRefed<extensions::ChannelWrapper> Get(
const dom::GlobalObject& global, nsIChannel* channel);
static already_AddRefed<extensions::ChannelWrapper> GetRegisteredChannel(
const dom::GlobalObject& global, uint64_t aChannelId,
const WebExtensionPolicy& aAddon, nsIRemoteTab* aBrowserParent);
uint64_t Id() const { return mId; }
already_AddRefed<nsIChannel> GetChannel() const { return MaybeChannel(); }
void SetChannel(nsIChannel* aChannel);
void Cancel(uint32_t result, uint32_t reason, ErrorResult& aRv);
void RedirectTo(nsIURI* uri, ErrorResult& aRv);
void UpgradeToSecure(ErrorResult& aRv);
bool Suspended() const { return mSuspended; }
void Suspend(const nsCString& aProfileMarkerText, ErrorResult& aRv);
void Resume(ErrorResult& aRv);
void GetContentType(nsCString& aContentType) const;
void SetContentType(const nsACString& aContentType);
void RegisterTraceableChannel(const WebExtensionPolicy& aAddon,
nsIRemoteTab* aBrowserParent);
already_AddRefed<nsITraceableChannel> GetTraceableChannel(
const WebExtensionPolicy& aAddon,
dom::ContentParent* aContentParent) const;
void GetMethod(nsCString& aRetVal) const;
dom::MozContentPolicyType Type() const;
uint32_t StatusCode() const;
uint64_t ResponseSize() const;
uint64_t RequestSize() const;
void GetStatusLine(nsCString& aRetVal) const;
void GetErrorString(nsString& aRetVal) const;
void ErrorCheck();
IMPL_EVENT_HANDLER(error);
IMPL_EVENT_HANDLER(start);
IMPL_EVENT_HANDLER(stop);
already_AddRefed<nsIURI> GetFinalURI() const;
void GetFinalURL(nsString& aRetVal) const;
bool Matches(const dom::MozRequestFilter& aFilter,
const WebExtensionPolicy* aExtension,
const dom::MozRequestMatchOptions& aOptions) const;
already_AddRefed<nsILoadInfo> GetLoadInfo() const {
nsCOMPtr<nsIChannel> chan = MaybeChannel();
if (chan) {
return chan->LoadInfo();
}
return nullptr;
}
int64_t FrameId() const;
int64_t ParentFrameId() const;
void GetFrameAncestors(
dom::Nullable<nsTArray<dom::MozFrameAncestorInfo>>& aFrameAncestors,
ErrorResult& aRv) const;
bool IsServiceWorkerScript() const;
static bool IsServiceWorkerScript(const nsCOMPtr<nsIChannel>& aChannel);
void GetOriginURL(nsCString& aRetVal) const;
void GetDocumentURL(nsCString& aRetVal) const;
already_AddRefed<nsIURI> GetOriginURI() const;
already_AddRefed<nsIURI> GetDocumentURI() const;
already_AddRefed<nsILoadContext> GetLoadContext() const;
already_AddRefed<dom::Element> GetBrowserElement() const;
bool CanModify() const;
bool GetCanModify(ErrorResult& aRv) const { return CanModify(); }
void GetProxyInfo(dom::Nullable<dom::MozProxyInfo>& aRetVal,
ErrorResult& aRv) const;
void GetRemoteAddress(nsCString& aRetVal) const;
void GetRequestHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
ErrorResult& aRv) const;
void GetRequestHeader(const nsCString& aHeader, nsCString& aResult,
ErrorResult& aRv) const;
void GetResponseHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
ErrorResult& aRv) const;
void SetRequestHeader(const nsCString& header, const nsCString& value,
bool merge, ErrorResult& aRv);
void SetResponseHeader(const nsCString& header, const nsCString& value,
bool merge, ErrorResult& aRv);
void GetUrlClassification(dom::Nullable<dom::MozUrlClassification>& aRetVal,
ErrorResult& aRv) const;
bool ThirdParty() const;
using EventTarget::EventListenerAdded;
using EventTarget::EventListenerRemoved;
virtual void EventListenerAdded(nsAtom* aType) override;
virtual void EventListenerRemoved(nsAtom* aType) override;
nsISupports* GetParentObject() const { return mParent; }
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
protected:
~ChannelWrapper();
private:
ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel);
void ClearCachedAttributes();
bool CheckAlive(ErrorResult& aRv) const {
if (!HaveChannel()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return false;
}
return true;
}
void FireEvent(const nsAString& aType);
const URLInfo& FinalURLInfo() const;
const URLInfo* DocumentURLInfo() const;
uint64_t BrowsingContextId(nsILoadInfo* aLoadInfo) const;
nsresult GetFrameAncestors(
nsILoadInfo* aLoadInfo,
nsTArray<dom::MozFrameAncestorInfo>& aFrameAncestors) const;
static uint64_t GetNextId() {
static uint64_t sNextId = 1;
return ++sNextId;
}
void CheckEventListeners();
class ChannelWrapperStub final : public nsISupports {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(ChannelWrapperStub)
explicit ChannelWrapperStub(ChannelWrapper* aChannelWrapper)
: mChannelWrapper(aChannelWrapper) {}
private:
friend class ChannelWrapper;
RefPtr<ChannelWrapper> mChannelWrapper;
protected:
~ChannelWrapperStub() = default;
};
RefPtr<ChannelWrapperStub> mStub;
mutable Maybe<URLInfo> mFinalURLInfo;
mutable Maybe<URLInfo> mDocumentURLInfo;
UniquePtr<WebRequestChannelEntry> mChannelEntry;
// The overridden Content-Type header value.
nsCString mContentTypeHdr = VoidCString();
const uint64_t mId = GetNextId();
nsCOMPtr<nsISupports> mParent;
bool mAddedStreamListener = false;
bool mFiredErrorEvent = false;
bool mSuspended = false;
bool mResponseStarted = false;
nsInterfaceHashtable<nsAtomHashKey, nsIRemoteTab> mAddonEntries;
// The text for the "Extension Suspend" marker, set from the Suspend method
// when called for the first time and then cleared on the Resume method.
nsCString mSuspendedMarkerText = VoidCString();
class RequestListener final : public nsIMultiPartChannelListener,
public nsIThreadRetargetableStreamListener {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIMULTIPARTCHANNELLISTENER
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
explicit RequestListener(ChannelWrapper* aWrapper)
: mChannelWrapper(aWrapper) {}
nsresult Init();
protected:
virtual ~RequestListener();
private:
RefPtr<ChannelWrapper> mChannelWrapper;
nsCOMPtr<nsIStreamListener> mOrigStreamListener;
};
};
NS_DEFINE_STATIC_IID_ACCESSOR(ChannelWrapper, NS_CHANNELWRAPPER_IID)
} // namespace extensions
} // namespace mozilla
#endif // mozilla_extensions_ChannelWrapper_h