toolkit/components/extensions/webrequest/ChannelWrapper.h
author Ehsan Akhgari <ehsan@mozilla.com>
Thu, 29 Nov 2018 10:30:46 +0000
changeset 505141 04f0bbf40bf36957dc1f72a8aae9916df0e3222f
parent 500257 c4633ad9220fa16e7ab25ba30e82cd792ca1eb5e
child 505383 6f3709b3878117466168c40affa7bca0b60cf75b
permissions -rw-r--r--
Bug 1508472 - Part 5: Fifth batch of comment fix-ups in preparation for the tree reformat r=sylvestre This is a best effort attempt at ensuring that the adverse impact of reformatting the entire tree over the comments would be minimal. I've used a combination of strategies including disabling of formatting, some manual formatting and some changes to formatting to work around some clang-format limitations. Differential Revision: https://phabricator.services.mozilla.com/D13371

/* -*- 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 "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIChannel.h"
#include "nsIHttpChannel.h"
#include "nsIStreamListener.h"
#include "nsITabParent.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsPointerHashKeys.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 nsIContentParent;
  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<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;
  };
}

class WebRequestChannelEntry;

class ChannelWrapper final : public DOMEventTargetHelper
                           , public SupportsWeakPtr<ChannelWrapper>
                           , public LinkedListElement<ChannelWrapper>
                           , private detail::ChannelHolder
{
public:
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ChannelWrapper)
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_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, nsITabParent* aTabParent);

  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);
  void UpgradeToSecure(ErrorResult& aRv);


  bool Suspended() const { return mSuspended; }

  void SetSuspended(bool aSuspended, ErrorResult& aRv);


  void GetContentType(nsCString& aContentType) const;
  void SetContentType(const nsACString& aContentType);


  void RegisterTraceableChannel(const WebExtensionPolicy& aAddon, nsITabParent* aTabParent);

  already_AddRefed<nsITraceableChannel> GetTraceableChannel(nsAtom* aAddonId, dom::nsIContentParent* aContentParent) const;


  void GetMethod(nsCString& aRetVal) const;

  dom::MozContentPolicyType Type() const;


  uint32_t StatusCode() 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> FinalURI() 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->GetLoadInfo();
    }
    return nullptr;
  }

  int64_t WindowId() const;

  int64_t ParentWindowId() const;

  void GetFrameAncestors(dom::Nullable<nsTArray<dom::MozFrameAncestorInfo>>& aFrameAncestors, ErrorResult& aRv) const;

  bool IsSystemLoad() const;

  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 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);


  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::HandleObject 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 WindowId(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<nsPtrHashKey<const nsAtom>, nsITabParent> mAddonEntries;


  class RequestListener final : public nsIStreamListener
                              , public nsIThreadRetargetableStreamListener
  {
  public:
    NS_DECL_THREADSAFE_ISUPPORTS
    NS_DECL_NSIREQUESTOBSERVER
    NS_DECL_NSISTREAMLISTENER
    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