Bug 1396856: Part 3 - Add a WebIDL wrapper class for necko channels. r=ehsan,mixedpuppy

Ehsan, can you please review the DOM bindings, and Shane the request logic?

The bulk of the overhead WebRequest API is in its access to nsIChannel and
friends through XPConnect. Since it's not really feasible to convert channels
to use WebIDL bindings directly, this generic channel wrapper class serves the
same purpose.

MozReview-Commit-ID: 4mNP8HiKWK

--HG--
extra : rebase_source : 111687dd0925619b5d93447aecffacd5d53532ef
This commit is contained in:
Kris Maglione 2017-09-06 14:38:23 -07:00
parent c79059605f
commit d55de0c717
7 changed files with 1074 additions and 2 deletions

View File

@ -340,8 +340,8 @@ interface nsIContentPolicy : nsISupports
/* When adding new content types, please update nsContentBlocker,
* NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
* implementations, the static_assert in dom/cache/DBSchema.cpp,
* and other things that are not listed here that are related to
* nsIContentPolicy. */
* ChannelWrapper.webidl, ChannelWrapper.cpp, and other things that
* are not listed here that are related to nsIContentPolicy. */
//////////////////////////////////////////////////////////////////////

View File

@ -135,6 +135,10 @@ DOMInterfaces = {
'nativeType': 'nsDOMCaretPosition',
},
'ChannelWrapper': {
'nativeType': 'mozilla::extensions::ChannelWrapper',
},
'CharacterData': {
'nativeType': 'nsGenericDOMDataNode',
'concrete': False

View File

@ -0,0 +1,153 @@
/* 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/. */
interface LoadInfo;
interface MozChannel;
interface URI;
interface nsISupports;
/**
* Load types that correspond to the external types in nsIContentPolicy.idl.
* Please also update that IDL when updating this list.
*/
enum MozContentPolicyType {
"main_frame",
"sub_frame",
"stylesheet",
"script",
"image",
"object",
"object_subrequest",
"xmlhttprequest",
"fetch",
"xbl",
"xslt",
"ping",
"beacon",
"xml_dtd",
"font",
"media",
"websocket",
"csp_report",
"imageset",
"web_manifest",
"other"
};
/**
* A thin wrapper around nsIChannel and nsIHttpChannel that allows JS
* callers to access them without XPConnect overhead.
*/
[ChromeOnly, Exposed=System]
interface ChannelWrapper {
/**
* Returns the wrapper instance for the given channel. The same wrapper is
* always returned for a given channel.
*/
static ChannelWrapper get(MozChannel channel);
[Constant, StoreInSlot]
readonly attribute unsigned long long id;
// Not technically pure, since it's backed by a weak reference, but if JS
// has a reference to the previous value, we can depend on it not being
// collected.
[Pure]
attribute MozChannel? channel;
[Throws]
void cancel(unsigned long result);
[Throws]
void redirectTo(URI url);
[Pure]
attribute ByteString contentType;
[Cached, Pure]
readonly attribute ByteString method;
[Cached, Pure]
readonly attribute MozContentPolicyType type;
[Pure, SetterThrows]
attribute boolean suspended;
[Cached, GetterThrows, Pure]
readonly attribute URI finalURI;
[Cached, GetterThrows, Pure]
readonly attribute ByteString finalURL;
[Cached, Pure]
readonly attribute unsigned long statusCode;
[Cached, Pure]
readonly attribute ByteString statusLine;
[Cached, Frozen, GetterThrows, Pure]
readonly attribute MozProxyInfo? proxyInfo;
[Cached, Pure]
readonly attribute ByteString? remoteAddress;
[Cached, Pure]
readonly attribute LoadInfo? loadInfo;
[Cached, Pure]
readonly attribute boolean isSystemLoad;
[Cached, Pure]
readonly attribute ByteString? originURL;
[Cached, Pure]
readonly attribute ByteString? documentURL;
[Cached, GetterThrows, Pure]
readonly attribute boolean canModify;
[Cached, Constant]
readonly attribute long long windowId;
[Cached, Constant]
readonly attribute long long parentWindowId;
[Cached, Pure]
readonly attribute nsISupports? browserElement;
[Throws]
object getRequestHeaders();
[Throws]
object getResponseHeaders();
[Throws]
void setRequestHeader(ByteString header, ByteString value);
[Throws]
void setResponseHeader(ByteString header, ByteString value);
};
dictionary MozProxyInfo {
required ByteString host;
required long port;
required ByteString type;
required boolean proxyDNS;
ByteString? username = null;
unsigned long failoverTimeout;
};

View File

@ -432,6 +432,7 @@ WEBIDL_FILES = [
'CDATASection.webidl',
'ChannelMergerNode.webidl',
'ChannelSplitterNode.webidl',
'ChannelWrapper.webidl',
'CharacterData.webidl',
'CheckerboardReportService.webidl',
'ChildNode.webidl',

View File

@ -0,0 +1,679 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "SystemPrincipal.h"
#include "mozilla/AddonManagerWebAPI.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Unused.h"
#include "nsIContentPolicy.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILoadContext.h"
#include "nsILoadGroup.h"
#include "nsIProxiedChannel.h"
#include "nsIProxyInfo.h"
#include "nsIWritablePropertyBag2.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
using namespace mozilla::dom;
using namespace JS;
namespace mozilla {
namespace extensions {
#define CHANNELWRAPPER_PROP_KEY NS_LITERAL_STRING("ChannelWrapper::CachedInstance")
/*****************************************************************************
* Initialization
*****************************************************************************/
/* static */
already_AddRefed<ChannelWrapper>
ChannelWrapper::Get(const GlobalObject& global, nsIChannel* channel)
{
RefPtr<ChannelWrapper> wrapper;
nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
if (props) {
Unused << props->GetPropertyAsInterface(CHANNELWRAPPER_PROP_KEY,
NS_GET_IID(ChannelWrapper),
getter_AddRefs(wrapper));
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);
}
}
return wrapper.forget();
}
void
ChannelWrapper::SetChannel(nsIChannel* aChannel)
{
detail::ChannelHolder::SetChannel(aChannel);
ClearCachedAttributes();
}
void
ChannelWrapper::ClearCachedAttributes()
{
ChannelWrapperBinding::ClearCachedFinalURIValue(this);
ChannelWrapperBinding::ClearCachedFinalURLValue(this);
ChannelWrapperBinding::ClearCachedProxyInfoValue(this);
ChannelWrapperBinding::ClearCachedRemoteAddressValue(this);
ChannelWrapperBinding::ClearCachedStatusCodeValue(this);
ChannelWrapperBinding::ClearCachedStatusLineValue(this);
}
/*****************************************************************************
* ...
*****************************************************************************/
void
ChannelWrapper::Cancel(uint32_t aResult, ErrorResult& aRv)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
rv = chan->Cancel(nsresult(aResult));
}
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::SetSuspended(bool aSuspended, ErrorResult& aRv)
{
if (aSuspended != mSuspended) {
nsresult rv = NS_ERROR_UNEXPECTED;
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
if (aSuspended) {
rv = chan->Suspend();
} else {
rv = chan->Resume();
}
}
if (NS_FAILED(rv)) {
aRv.Throw(rv);
} else {
mSuspended = aSuspended;
}
}
}
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(JSContext* aCx)
: mCx(aCx)
, mMap(aCx)
{}
JSObject* VisitRequestHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv)
{
if (!Init()) {
return nullptr;
}
if (!CheckResult(aChannel->VisitRequestHeaders(this), aRv)) {
return nullptr;
}
return mMap;
}
JSObject* VisitResponseHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv)
{
if (!Init()) {
return nullptr;
}
if (!CheckResult(aChannel->VisitResponseHeaders(this), aRv)) {
return nullptr;
}
return mMap;
}
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 Init()
{
mMap = NewMapObject(mCx);
return mMap;
}
bool CheckResult(nsresult aNSRv, ErrorResult& aRv)
{
if (JS_IsExceptionPending(mCx)) {
aRv.NoteJSContextException(mCx);
return false;
}
if (NS_FAILED(aNSRv)) {
aRv.Throw(aNSRv);
return false;
}
return true;
}
JSContext* mCx;
RootedObject mMap;
nsrefcnt mRefCnt = 0;
};
NS_IMETHODIMP
HeaderVisitor::VisitHeader(const nsACString& aHeader, const nsACString& aValue)
{
RootedValue header(mCx);
RootedValue value(mCx);
if (!xpc::NonVoidStringToJsval(mCx, NS_ConvertUTF8toUTF16(aHeader), &header) ||
!xpc::NonVoidStringToJsval(mCx, NS_ConvertUTF8toUTF16(aValue), &value) ||
!MapSet(mCx, mMap, header, value)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMPL_QUERY_INTERFACE(HeaderVisitor, nsIHttpHeaderVisitor)
} // anonymous namespace
void
ChannelWrapper::GetRequestHeaders(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv) const
{
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
HeaderVisitor visitor(cx);
aRetVal.set(visitor.VisitRequestHeaders(chan, aRv));
} else {
aRv.Throw(NS_ERROR_UNEXPECTED);
}
}
void
ChannelWrapper::GetResponseHeaders(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv) const
{
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
HeaderVisitor visitor(cx);
aRetVal.set(visitor.VisitResponseHeaders(chan, aRv));
} else {
aRv.Throw(NS_ERROR_UNEXPECTED);
}
}
void
ChannelWrapper::SetRequestHeader(const nsCString& aHeader, const nsCString& aValue, ErrorResult& aRv)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
rv = chan->SetRequestHeader(aHeader, aValue, false);
}
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
void
ChannelWrapper::SetResponseHeader(const nsCString& aHeader, const nsCString& aValue, ErrorResult& aRv)
{
nsresult rv = NS_ERROR_UNEXPECTED;
if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
rv = chan->SetResponseHeader(aHeader, aValue, false);
}
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<nsIDOMElement>
ChannelWrapper::GetBrowserElement() const
{
if (nsCOMPtr<nsILoadContext> ctxt = GetLoadContext()) {
nsCOMPtr<nsIDOMElement> 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->LoadingPrincipal()) {
return IsSystemPrincipal(prin);
}
if (loadInfo->GetOuterWindowID() == loadInfo->GetTopOuterWindowID()) {
return false;
}
if (nsIPrincipal* prin = loadInfo->PrincipalToInherit()) {
return IsSystemPrincipal(prin);
}
if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
return IsSystemPrincipal(prin);
}
}
return false;
}
bool
ChannelWrapper::GetCanModify(ErrorResult& aRv) const
{
nsCOMPtr<nsIURI> uri = GetFinalURI(aRv);
nsAutoCString spec;
if (uri) {
uri->GetSpec(spec);
}
if (!uri || AddonManagerWebAPI::IsValidSite(uri)) {
return false;
}
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
if (nsIPrincipal* prin = loadInfo->LoadingPrincipal()) {
if (IsSystemPrincipal(prin)) {
return false;
}
if (prin->GetIsCodebasePrincipal() &&
(NS_FAILED(prin->GetURI(getter_AddRefs(uri))) ||
AddonManagerWebAPI::IsValidSite(uri))) {
return false;
}
}
}
return true;
}
void
ChannelWrapper::GetOriginURL(nsCString& aRetVal) const
{
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
nsCOMPtr<nsIURI> uri;
if (prin->GetIsCodebasePrincipal() &&
NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
Unused << uri->GetSpec(aRetVal);
}
}
}
}
void
ChannelWrapper::GetDocumentURL(nsCString& aRetVal) const
{
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
if (nsIPrincipal* prin = loadInfo->LoadingPrincipal()) {
nsCOMPtr<nsIURI> uri;
if (prin->GetIsCodebasePrincipal() &&
NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
Unused << uri->GetSpec(aRetVal);
}
}
}
}
int64_t
NormalizeWindowID(nsILoadInfo* aLoadInfo, uint64_t windowID)
{
if (windowID == aLoadInfo->GetTopOuterWindowID()) {
return 0;
}
return windowID;
}
uint64_t
ChannelWrapper::WindowId(nsILoadInfo* aLoadInfo) const
{
auto frameID = aLoadInfo->GetFrameOuterWindowID();
if (!frameID) {
frameID = aLoadInfo->GetOuterWindowID();
}
return frameID;
}
int64_t
ChannelWrapper::WindowId() const
{
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
return NormalizeWindowID(loadInfo, WindowId(loadInfo));
}
return 0;
}
int64_t
ChannelWrapper::ParentWindowId() const
{
if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
if (WindowId(loadInfo) == loadInfo->GetTopOuterWindowID()) {
return -1;
}
uint64_t parentID;
if (loadInfo->GetFrameOuterWindowID()) {
parentID = loadInfo->GetOuterWindowID();
} else {
parentID = loadInfo->GetParentOuterWindowID();
}
return NormalizeWindowID(loadInfo, parentID);
}
return -1;
}
/*****************************************************************************
* ...
*****************************************************************************/
MozContentPolicyType
GetContentPolicyType(uint32_t aType)
{
// Note: Please keep this function in sync with the external types in
// nsIContentPolicy.idl
switch (aType) {
case nsIContentPolicy::TYPE_DOCUMENT:
return MozContentPolicyType::Main_frame;
case nsIContentPolicy::TYPE_SUBDOCUMENT:
return MozContentPolicyType::Sub_frame;
case nsIContentPolicy::TYPE_STYLESHEET:
return MozContentPolicyType::Stylesheet;
case nsIContentPolicy::TYPE_SCRIPT:
return MozContentPolicyType::Script;
case nsIContentPolicy::TYPE_IMAGE:
return MozContentPolicyType::Image;
case nsIContentPolicy::TYPE_OBJECT:
return MozContentPolicyType::Object;
case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
return MozContentPolicyType::Object_subrequest;
case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
return MozContentPolicyType::Xmlhttprequest;
// TYPE_FETCH returns xmlhttprequest for cross-browser compatibility.
case nsIContentPolicy::TYPE_FETCH:
return MozContentPolicyType::Xmlhttprequest;
case nsIContentPolicy::TYPE_XBL:
return MozContentPolicyType::Xbl;
case nsIContentPolicy::TYPE_XSLT:
return MozContentPolicyType::Xslt;
case nsIContentPolicy::TYPE_PING:
return MozContentPolicyType::Ping;
case nsIContentPolicy::TYPE_BEACON:
return MozContentPolicyType::Beacon;
case nsIContentPolicy::TYPE_DTD:
return MozContentPolicyType::Xml_dtd;
case nsIContentPolicy::TYPE_FONT:
return MozContentPolicyType::Font;
case nsIContentPolicy::TYPE_MEDIA:
return MozContentPolicyType::Media;
case nsIContentPolicy::TYPE_WEBSOCKET:
return MozContentPolicyType::Websocket;
case nsIContentPolicy::TYPE_CSP_REPORT:
return MozContentPolicyType::Csp_report;
case nsIContentPolicy::TYPE_IMAGESET:
return MozContentPolicyType::Imageset;
case nsIContentPolicy::TYPE_WEB_MANIFEST:
return MozContentPolicyType::Web_manifest;
default:
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());
}
}
/*****************************************************************************
* ...
*****************************************************************************/
already_AddRefed<nsIURI>
ChannelWrapper::GetFinalURI(ErrorResult& aRv) const
{
nsresult rv = NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIURI> uri;
if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
rv = NS_GetFinalChannelURI(chan, getter_AddRefs(uri));
}
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
return uri.forget();;
}
void
ChannelWrapper::GetFinalURL(nsCString& aRetVal, ErrorResult& aRv) const
{
nsCOMPtr<nsIURI> uri = GetFinalURI(aRv);
if (uri) {
Unused << uri->GetSpec(aRetVal);
}
}
/*****************************************************************************
* ...
*****************************************************************************/
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->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(Move(result));
}
}
}
void
ChannelWrapper::GetRemoteAddress(nsCString& aRetVal) const
{
aRetVal.SetIsVoid(true);
if (nsCOMPtr<nsIHttpChannelInternal> internal = QueryChannel()) {
Unused << internal->GetRemoteAddress(aRetVal);
}
}
/*****************************************************************************
* Glue
*****************************************************************************/
JSObject*
ChannelWrapper::WrapObject(JSContext* aCx, HandleObject aGivenProto)
{
return ChannelWrapperBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(ChannelWrapper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChannelWrapper)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(ChannelWrapper)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ChannelWrapper)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ChannelWrapper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(ChannelWrapper)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ChannelWrapper)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ChannelWrapper)
} // namespace extensions
} // namespace mozilla

View File

@ -0,0 +1,229 @@
/* -*- 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/Attributes.h"
#include "mozilla/Maybe.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsWeakPtr.h"
#include "nsWrapperCache.h"
#define NS_CHANNELWRAPPER_IID \
{ 0xc06162d2, 0xb803, 0x43b4, \
{ 0xaa, 0x31, 0xcf, 0x69, 0x7f, 0x93, 0x68, 0x1c } }
class nsIDOMElement;
class nsILoadContext;
namespace mozilla {
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.
//
// 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->IsAlive(); }
void SetChannel(nsIChannel* aChannel)
{
mChannel = do_GetWeakReference(aChannel);
mWeakChannel = aChannel;
mWeakHttpChannel.reset();
}
already_AddRefed<nsIChannel> MaybeChannel() const
{
if (!mChannel->IsAlive()) {
mWeakChannel = nullptr;
}
return do_AddRef(mWeakChannel);
}
already_AddRefed<nsIHttpChannel> MaybeHttpChannel() const
{
if (mWeakHttpChannel.isNothing()) {
nsCOMPtr<nsIHttpChannel> chan = QueryChannel();
mWeakHttpChannel.emplace(chan.get());
}
if (!mChannel->IsAlive()) {
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;
};
}
class ChannelWrapper final : public nsISupports
, public nsWrapperCache
, private detail::ChannelHolder
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ChannelWrapper)
NS_DECLARE_STATIC_IID_ACCESSOR(NS_CHANNELWRAPPER_IID)
static already_AddRefed<extensions::ChannelWrapper> Get(const dom::GlobalObject& global, nsIChannel* channel);
uint64_t Id() const { return mId; }
already_AddRefed<nsIChannel> GetChannel() const { return MaybeChannel(); }
void SetChannel(nsIChannel* aChannel);
void Cancel(uint32_t result, ErrorResult& aRv);
void RedirectTo(nsIURI* uri, ErrorResult& aRv);
bool Suspended() const { return mSuspended; }
void SetSuspended(bool aSuspended, ErrorResult& aRv);
void GetContentType(nsCString& aContentType) const;
void SetContentType(const nsACString& aContentType);
void GetMethod(nsCString& aRetVal) const;
dom::MozContentPolicyType Type() const;
uint32_t StatusCode() const;
void GetStatusLine(nsCString& aRetVal) const;
already_AddRefed<nsIURI> GetFinalURI(ErrorResult& aRv) const;
void GetFinalURL(nsCString& aRetVal, ErrorResult& aRv) const;
already_AddRefed<nsILoadInfo> GetLoadInfo() const
{
nsCOMPtr<nsIChannel> chan = MaybeChannel();
if (chan) {
return chan->GetLoadInfo();
}
return nullptr;
}
int64_t WindowId() const;
int64_t ParentWindowId() const;
bool IsSystemLoad() const;
void GetOriginURL(nsCString& aRetVal) const;
void GetDocumentURL(nsCString& aRetVal) const;
already_AddRefed<nsILoadContext> GetLoadContext() const;
already_AddRefed<nsIDOMElement> GetBrowserElement() const;
bool GetCanModify(ErrorResult& aRv) const;
void GetProxyInfo(dom::Nullable<dom::MozProxyInfo>& aRetVal, ErrorResult& aRv) const;
void GetRemoteAddress(nsCString& aRetVal) const;
void GetRequestHeaders(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv) const;
void GetResponseHeaders(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv) const;
void SetRequestHeader(const nsCString& header, const nsCString& value, ErrorResult& aRv);
void SetResponseHeader(const nsCString& header, const nsCString& value, ErrorResult& aRv);
nsISupports* GetParentObject() const { return mParent; }
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
protected:
~ChannelWrapper() = default;
private:
ChannelWrapper(nsISupports* aParent, nsIChannel* aChannel)
: ChannelHolder(aChannel)
, mParent(aParent)
{}
void ClearCachedAttributes();
bool CheckAlive(ErrorResult& aRv) const
{
if (!HaveChannel()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return false;
}
return true;
}
uint64_t WindowId(nsILoadInfo* aLoadInfo) const;
static uint64_t GetNextId()
{
static uint64_t sNextId = 1;
return ++sNextId;
}
const uint64_t mId = GetNextId();
nsCOMPtr<nsISupports> mParent;
bool mSuspended = false;
};
NS_DEFINE_STATIC_IID_ACCESSOR(ChannelWrapper,
NS_CHANNELWRAPPER_IID)
} // namespace extensions
} // namespace mozilla
#endif // mozilla_extensions_ChannelWrapper_h

View File

@ -12,6 +12,7 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'webextensions'
UNIFIED_SOURCES += [
'ChannelWrapper.cpp',
'nsWebRequestListener.cpp',
'StreamFilter.cpp',
'StreamFilterChild.cpp',
@ -33,6 +34,7 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla.extensions += [
'ChannelWrapper.h',
'StreamFilter.h',
'StreamFilterBase.h',
'StreamFilterChild.h',
@ -40,6 +42,10 @@ EXPORTS.mozilla.extensions += [
'StreamFilterParent.h',
]
LOCAL_INCLUDES += [
'/caps',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'