/* -*- 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. // // 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 MaybeChannel() const { if (!HaveChannel()) { mWeakChannel = nullptr; } return do_AddRef(mWeakChannel); } already_AddRefed MaybeHttpChannel() const { if (mWeakHttpChannel.isNothing()) { nsCOMPtr 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 MOZ_NON_OWNING_REF mWeakHttpChannel; }; } // namespace detail class WebRequestChannelEntry; class ChannelWrapper final : public DOMEventTargetHelper, public SupportsWeakPtr, public LinkedListElement, 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 Get( const dom::GlobalObject& global, nsIChannel* channel); static already_AddRefed GetRegisteredChannel( const dom::GlobalObject& global, uint64_t aChannelId, const WebExtensionPolicy& aAddon, nsIRemoteTab* aBrowserParent); uint64_t Id() const { return mId; } already_AddRefed 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 GetTraceableChannel( nsAtom* aAddonId, 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 FinalURI() const; void GetFinalURL(nsString& aRetVal) const; bool Matches(const dom::MozRequestFilter& aFilter, const WebExtensionPolicy* aExtension, const dom::MozRequestMatchOptions& aOptions) const; already_AddRefed GetLoadInfo() const { nsCOMPtr chan = MaybeChannel(); if (chan) { return chan->LoadInfo(); } return nullptr; } int64_t FrameId() const; int64_t ParentFrameId() const; void GetFrameAncestors( dom::Nullable>& aFrameAncestors, ErrorResult& aRv) const; bool IsServiceWorkerScript() const; static bool IsServiceWorkerScript(const nsCOMPtr& aChannel); bool IsSystemLoad() const; void GetOriginURL(nsCString& aRetVal) const; void GetDocumentURL(nsCString& aRetVal) const; already_AddRefed GetOriginURI() const; already_AddRefed GetDocumentURI() const; already_AddRefed GetLoadContext() const; already_AddRefed GetBrowserElement() const; bool CanModify() const; bool GetCanModify(ErrorResult& aRv) const { return CanModify(); } void GetProxyInfo(dom::Nullable& aRetVal, ErrorResult& aRv) const; void GetRemoteAddress(nsCString& aRetVal) const; void GetRequestHeaders(nsTArray& aRetVal, ErrorResult& aRv) const; void GetRequestHeader(const nsCString& aHeader, nsCString& aResult, ErrorResult& aRv) const; void GetResponseHeaders(nsTArray& 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& 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 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& 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 mChannelWrapper; protected: ~ChannelWrapperStub() = default; }; RefPtr mStub; mutable Maybe mFinalURLInfo; mutable Maybe mDocumentURLInfo; UniquePtr mChannelEntry; // The overridden Content-Type header value. nsCString mContentTypeHdr = VoidCString(); const uint64_t mId = GetNextId(); nsCOMPtr mParent; bool mAddedStreamListener = false; bool mFiredErrorEvent = false; bool mSuspended = false; bool mResponseStarted = false; nsInterfaceHashtable 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 mChannelWrapper; nsCOMPtr mOrigStreamListener; }; }; NS_DEFINE_STATIC_IID_ACCESSOR(ChannelWrapper, NS_CHANNELWRAPPER_IID) } // namespace extensions } // namespace mozilla #endif // mozilla_extensions_ChannelWrapper_h