Blob Blame History Raw
/* -*- 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/. */

#ifndef mozilla_mscom_COMPtrHolder_h
#define mozilla_mscom_COMPtrHolder_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Move.h"
#include "mozilla/mscom/ProxyStream.h"
#include "mozilla/mscom/Ptr.h"
#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
#endif  // defined(MOZ_CONTENT_SANDBOX)
#include "nsExceptionHandler.h"

namespace mozilla {
namespace mscom {

template <typename Interface, const IID& _IID>
class COMPtrHolder {
 public:
  typedef ProxyUniquePtr<Interface> COMPtrType;
  typedef COMPtrHolder<Interface, _IID> ThisType;
  typedef typename detail::EnvironmentSelector<Interface>::Type EnvType;

  COMPtrHolder() {}

  MOZ_IMPLICIT COMPtrHolder(decltype(nullptr)) {}

  explicit COMPtrHolder(COMPtrType&& aPtr) : mPtr(Forward<COMPtrType>(aPtr)) {}

  COMPtrHolder(COMPtrType&& aPtr, const ActivationContext& aActCtx)
      : mPtr(Forward<COMPtrType>(aPtr)), mActCtx(aActCtx) {}

  Interface* Get() const { return mPtr.get(); }

  MOZ_MUST_USE Interface* Release() { return mPtr.release(); }

  void Set(COMPtrType&& aPtr) { mPtr = Forward<COMPtrType>(aPtr); }

  void SetActCtx(const ActivationContext& aActCtx) { mActCtx = aActCtx; }

#if defined(MOZ_CONTENT_SANDBOX)
  // This method is const because we need to call it during IPC write, where
  // we are passed as a const argument. At higher sandboxing levels we need to
  // save this artifact from the serialization process for later deletion.
  void PreserveStream(PreservedStreamPtr aPtr) const {
    MOZ_ASSERT(!mMarshaledStream);
    mMarshaledStream = Move(aPtr);
  }

  PreservedStreamPtr GetPreservedStream() { return Move(mMarshaledStream); }
#endif  // defined(MOZ_CONTENT_SANDBOX)

  COMPtrHolder(const COMPtrHolder& aOther) = delete;

  COMPtrHolder(COMPtrHolder&& aOther)
      : mPtr(Move(aOther.mPtr))
#if defined(MOZ_CONTENT_SANDBOX)
        ,
        mMarshaledStream(Move(aOther.mMarshaledStream))
#endif  // defined(MOZ_CONTENT_SANDBOX)
  {
  }

  // COMPtrHolder is eventually added as a member of a struct that is declared
  // in IPDL. The generated C++ code for that IPDL struct includes copy
  // constructors and assignment operators that assume that all members are
  // copyable. I don't think that those copy constructors and operator= are
  // actually used by any generated code, but they are made available. Since no
  // move semantics are available, this terrible hack makes COMPtrHolder build
  // when used as a member of an IPDL struct.
  ThisType& operator=(const ThisType& aOther) {
    Set(Move(aOther.mPtr));

#if defined(MOZ_CONTENT_SANDBOX)
    mMarshaledStream = Move(aOther.mMarshaledStream);
#endif  // defined(MOZ_CONTENT_SANDBOX)

    return *this;
  }

  ThisType& operator=(ThisType&& aOther) {
    Set(Move(aOther.mPtr));

#if defined(MOZ_CONTENT_SANDBOX)
    mMarshaledStream = Move(aOther.mMarshaledStream);
#endif  // defined(MOZ_CONTENT_SANDBOX)

    return *this;
  }

  bool operator==(const ThisType& aOther) const { return mPtr == aOther.mPtr; }

  bool IsNull() const { return !mPtr; }

 private:
  // This is mutable to facilitate the above operator= hack
  mutable COMPtrType mPtr;
  ActivationContext mActCtx;

#if defined(MOZ_CONTENT_SANDBOX)
  // This is mutable so that we may optionally store a reference to a marshaled
  // stream to be cleaned up later via PreserveStream().
  mutable PreservedStreamPtr mMarshaledStream;
#endif  // defined(MOZ_CONTENT_SANDBOX)
};

}  // namespace mscom
}  // namespace mozilla

namespace IPC {

template <typename Interface, const IID& _IID>
struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>> {
  typedef mozilla::mscom::COMPtrHolder<Interface, _IID> paramType;

  static void Write(Message* aMsg, const paramType& aParam) {
#if defined(MOZ_CONTENT_SANDBOX)
    static const bool sIsStreamPreservationNeeded =
        XRE_IsParentProcess() &&
        mozilla::GetEffectiveContentSandboxLevel() >= 3;
#else
    const bool sIsStreamPreservationNeeded = false;
#endif  // defined(MOZ_CONTENT_SANDBOX)

    typename paramType::EnvType env;

    mozilla::mscom::ProxyStreamFlags flags =
        sIsStreamPreservationNeeded
            ? mozilla::mscom::ProxyStreamFlags::ePreservable
            : mozilla::mscom::ProxyStreamFlags::eDefault;

    mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get(), &env, flags);
    int bufLen;
    const BYTE* buf = proxyStream.GetBuffer(bufLen);
    MOZ_ASSERT(buf || !bufLen);
    aMsg->WriteInt(bufLen);
    if (bufLen) {
      aMsg->WriteBytes(reinterpret_cast<const char*>(buf), bufLen);
    }

#if defined(MOZ_CONTENT_SANDBOX)
    if (sIsStreamPreservationNeeded) {
      /**
       * When we're sending a ProxyStream from parent to content and the
       * content sandboxing level is >= 3, content is unable to communicate
       * its releasing of its reference to the proxied object. We preserve the
       * marshaled proxy data here and later manually release it on content's
       * behalf.
       */
      aParam.PreserveStream(proxyStream.GetPreservedStream());
    }
#endif  // defined(MOZ_CONTENT_SANDBOX)
  }

  static bool Read(const Message* aMsg, PickleIterator* aIter,
                   paramType* aResult) {
    int length;
    if (!aMsg->ReadLength(aIter, &length)) {
      return false;
    }

    mozilla::UniquePtr<BYTE[]> buf;
    if (length) {
      buf = mozilla::MakeUnique<BYTE[]>(length);
      if (!aMsg->ReadBytesInto(aIter, buf.get(), length)) {
        return false;
      }
    }

    typename paramType::EnvType env;

    mozilla::mscom::ProxyStream proxyStream(_IID, buf.get(), length, &env);
    if (!proxyStream.IsValid()) {
      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamValid"),
                                         NS_LITERAL_CSTRING("false"));
      return false;
    }

    typename paramType::COMPtrType ptr;
    if (!proxyStream.GetInterface(mozilla::mscom::getter_AddRefs(ptr))) {
      return false;
    }

    aResult->Set(mozilla::Move(ptr));
    return true;
  }
};

}  // namespace IPC

#endif  // mozilla_mscom_COMPtrHolder_h