/* -*- 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 __IPC_GLUE_IPCMESSAGEUTILSSPECIALIZATIONS_H__ #define __IPC_GLUE_IPCMESSAGEUTILSSPECIALIZATIONS_H__ #include #include #include #include #include #include #include #include #include "chrome/common/ipc_message.h" #include "chrome/common/ipc_message_utils.h" #include "ipc/EnumSerializer.h" #include "ipc/IPCMessageUtils.h" #include "mozilla/Assertions.h" #include "mozilla/BitSet.h" #include "mozilla/EnumSet.h" #include "mozilla/EnumTypeTraits.h" #include "mozilla/IntegerRange.h" #include "mozilla/Maybe.h" #include "mozilla/TimeStamp.h" #ifdef XP_WIN # include "mozilla/TimeStamp_windows.h" #endif #include "mozilla/Unused.h" #include "mozilla/Vector.h" #include "mozilla/dom/ipc/StructuredCloneData.h" #include "nsCSSPropertyID.h" #include "nsDebug.h" #include "nsHashKeys.h" #include "nsIContentPolicy.h" #include "nsID.h" #include "nsILoadInfo.h" #include "nsIThread.h" #include "nsLiteralString.h" #include "nsString.h" #include "nsTArray.h" #include "nsTHashtable.h" // XXX Includes that are only required by implementations which could be moved // to the cpp file. #include "base/string_util.h" // for StringPrintf #include "mozilla/ArrayUtils.h" // for ArrayLength #include "mozilla/CheckedInt.h" #ifdef _MSC_VER # pragma warning(disable : 4800) #endif namespace mozilla { template class Variant; namespace detail { template struct VariantTag; } } // namespace mozilla namespace mozilla::dom { template class Optional; } namespace IPC { template <> struct ParamTraits { typedef nsACString paramType; static void Write(Message* aMsg, const paramType& aParam) { bool isVoid = aParam.IsVoid(); aMsg->WriteBool(isVoid); if (isVoid) // represents a nullptr pointer return; uint32_t length = aParam.Length(); WriteParam(aMsg, length); aMsg->WriteBytes(aParam.BeginReading(), length); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { bool isVoid; if (!aMsg->ReadBool(aIter, &isVoid)) return false; if (isVoid) { aResult->SetIsVoid(true); return true; } uint32_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } if (!aMsg->HasBytesAvailable(aIter, length)) { return false; } aResult->SetLength(length); return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length); } static void Log(const paramType& aParam, std::wstring* aLog) { if (aParam.IsVoid()) aLog->append(L"(NULL)"); else aLog->append(UTF8ToWide(aParam.BeginReading())); } }; template <> struct ParamTraits { typedef nsAString paramType; static void Write(Message* aMsg, const paramType& aParam) { bool isVoid = aParam.IsVoid(); aMsg->WriteBool(isVoid); if (isVoid) // represents a nullptr pointer return; uint32_t length = aParam.Length(); WriteParam(aMsg, length); aMsg->WriteBytes(aParam.BeginReading(), length * sizeof(char16_t)); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { bool isVoid; if (!aMsg->ReadBool(aIter, &isVoid)) return false; if (isVoid) { aResult->SetIsVoid(true); return true; } uint32_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } mozilla::CheckedInt byteLength = mozilla::CheckedInt(length) * sizeof(char16_t); if (!byteLength.isValid() || !aMsg->HasBytesAvailable(aIter, byteLength.value())) { return false; } aResult->SetLength(length); return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), byteLength.value()); } static void Log(const paramType& aParam, std::wstring* aLog) { if (aParam.IsVoid()) aLog->append(L"(NULL)"); else { #ifdef WCHAR_T_IS_UTF16 aLog->append(reinterpret_cast(aParam.BeginReading())); #else uint32_t length = aParam.Length(); for (uint32_t index = 0; index < length; index++) { aLog->push_back(std::wstring::value_type(aParam[index])); } #endif } } }; template <> struct ParamTraits : ParamTraits { typedef nsCString paramType; }; template <> struct ParamTraits : ParamTraits { typedef nsLiteralCString paramType; }; #ifdef MOZILLA_INTERNAL_API template <> struct ParamTraits : ParamTraits { typedef nsAutoCString paramType; }; #endif // MOZILLA_INTERNAL_API template <> struct ParamTraits : ParamTraits { typedef nsString paramType; }; template <> struct ParamTraits : ParamTraits { typedef nsLiteralString paramType; }; template <> struct ParamTraits : ParamTraits { typedef nsDependentSubstring paramType; }; template <> struct ParamTraits : ParamTraits { typedef nsDependentCSubstring paramType; }; #ifdef MOZILLA_INTERNAL_API template <> struct ParamTraits : ParamTraits { typedef nsAutoString paramType; }; #endif // MOZILLA_INTERNAL_API template <> struct ParamTraits> { typedef nsTHashtable paramType; static void Write(Message* aMsg, const paramType& aParam) { uint32_t count = aParam.Count(); WriteParam(aMsg, count); for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) { WriteParam(aMsg, iter.Get()->GetKey()); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { uint32_t count; if (!ReadParam(aMsg, aIter, &count)) { return false; } paramType table(count); for (uint32_t i = 0; i < count; ++i) { uint64_t key; if (!ReadParam(aMsg, aIter, &key)) { return false; } table.PutEntry(key); } *aResult = std::move(table); return true; } }; // Pickle::ReadBytes and ::WriteBytes take the length in ints, so we must // ensure there is no overflow. This returns |false| if it would overflow. // Otherwise, it returns |true| and places the byte length in |aByteLength|. bool ByteLengthIsValid(uint32_t aNumElements, size_t aElementSize, int* aByteLength); // Note: IPDL will sometimes codegen specialized implementations of // nsTArray serialization and deserialization code in // implementSpecialArrayPickling(). This is needed when ParamTraits // is not defined. template struct ParamTraits> { typedef nsTArray paramType; // We write arrays of integer or floating-point data using a single pickling // call, rather than writing each element individually. We deliberately do // not use mozilla::IsPod here because it is perfectly reasonable to have // a data structure T for which IsPod::value is true, yet also have a // ParamTraits specialization. static const bool sUseWriteBytes = (std::is_integral_v || std::is_floating_point_v); static void Write(Message* aMsg, const paramType& aParam) { uint32_t length = aParam.Length(); WriteParam(aMsg, length); if (sUseWriteBytes) { int pickledLength = 0; MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength)); aMsg->WriteBytes(aParam.Elements(), pickledLength); } else { const E* elems = aParam.Elements(); for (uint32_t index = 0; index < length; index++) { WriteParam(aMsg, elems[index]); } } } // This method uses infallible allocation so that an OOM failure will // show up as an OOM crash rather than an IPC FatalError. static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { uint32_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } if (sUseWriteBytes) { int pickledLength = 0; if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) { return false; } E* elements = aResult->AppendElements(length); return aMsg->ReadBytesInto(aIter, elements, pickledLength); } else { // Each ReadParam may read more than 1 byte each; this is an attempt // to minimally validate that the length isn't much larger than what's // actually available in aMsg. if (!aMsg->HasBytesAvailable(aIter, length)) { return false; } aResult->SetCapacity(length); for (uint32_t index = 0; index < length; index++) { E* element = aResult->AppendElement(); if (!ReadParam(aMsg, aIter, element)) { return false; } } return true; } } static void Log(const paramType& aParam, std::wstring* aLog) { for (uint32_t index = 0; index < aParam.Length(); index++) { if (index) { aLog->append(L" "); } LogParam(aParam[index], aLog); } } }; template struct ParamTraits> : ParamTraits> {}; template struct ParamTraits> { typedef FallibleTArray paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, static_cast&>(aParam)); } // Deserialize the array infallibly, but return a FallibleTArray. static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { nsTArray temp; if (!ReadParam(aMsg, aIter, &temp)) return false; *aResult = std::move(temp); return true; } static void Log(const paramType& aParam, std::wstring* aLog) { LogParam(static_cast&>(aParam), aLog); } }; template struct ParamTraits> : ParamTraits> { typedef AutoTArray paramType; }; template struct ParamTraits> : ParamTraits> {}; template struct ParamTraits> { typedef mozilla::Vector paramType; // We write arrays of integer or floating-point data using a single pickling // call, rather than writing each element individually. We deliberately do // not use mozilla::IsPod here because it is perfectly reasonable to have // a data structure T for which IsPod::value is true, yet also have a // ParamTraits specialization. static const bool sUseWriteBytes = (std::is_integral_v || std::is_floating_point_v); static void Write(Message* aMsg, const paramType& aParam) { uint32_t length = aParam.length(); WriteParam(aMsg, length); if (sUseWriteBytes) { int pickledLength = 0; MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength)); aMsg->WriteBytes(aParam.begin(), pickledLength); return; } for (const E& elem : aParam) { WriteParam(aMsg, elem); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { uint32_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } if (sUseWriteBytes) { int pickledLength = 0; if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) { return false; } if (!aResult->resizeUninitialized(length)) { // So that OOM failure shows up as OOM crash instead of IPC FatalError. NS_ABORT_OOM(length * sizeof(E)); } E* elements = aResult->begin(); return aMsg->ReadBytesInto(aIter, elements, pickledLength); } // Each ReadParam may read more than 1 byte each; this is an attempt // to minimally validate that the length isn't much larger than what's // actually available in aMsg. if (!aMsg->HasBytesAvailable(aIter, length)) { return false; } if (!aResult->resize(length)) { // So that OOM failure shows up as OOM crash instead of IPC FatalError. NS_ABORT_OOM(length * sizeof(E)); } for (uint32_t index = 0; index < length; ++index) { if (!ReadParam(aMsg, aIter, &((*aResult)[index]))) { return false; } } return true; } static void Log(const paramType& aParam, std::wstring* aLog) { for (uint32_t index = 0, len = aParam.length(); index < len; ++index) { if (index) { aLog->append(L" "); } LogParam(aParam[index], aLog); } } }; template struct ParamTraits> { typedef std::vector paramType; // We write arrays of integer or floating-point data using a single pickling // call, rather than writing each element individually. We deliberately do // not use mozilla::IsPod here because it is perfectly reasonable to have // a data structure T for which IsPod::value is true, yet also have a // ParamTraits specialization. static const bool sUseWriteBytes = (std::is_integral_v || std::is_floating_point_v); static void Write(Message* aMsg, const paramType& aParam) { uint32_t length = aParam.size(); WriteParam(aMsg, length); if (sUseWriteBytes) { int pickledLength = 0; MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength)); aMsg->WriteBytes(aParam.data(), pickledLength); return; } for (const E& elem : aParam) { WriteParam(aMsg, elem); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { uint32_t length; if (!ReadParam(aMsg, aIter, &length)) { return false; } if (sUseWriteBytes) { int pickledLength = 0; if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) { return false; } aResult->resize(length); E* elements = aResult->data(); return aMsg->ReadBytesInto(aIter, elements, pickledLength); } // Each ReadParam may read more than 1 byte each; this is an attempt // to minimally validate that the length isn't much larger than what's // actually available in aMsg. if (!aMsg->HasBytesAvailable(aIter, length)) { return false; } aResult->resize(length); for (uint32_t index = 0; index < length; ++index) { if (!ReadParam(aMsg, aIter, &((*aResult)[index]))) { return false; } } return true; } static void Log(const paramType& aParam, std::wstring* aLog) { for (uint32_t index = 0, len = aParam.size(); index < len; ++index) { if (index) { aLog->append(L" "); } LogParam(aParam[index], aLog); } } }; template struct ParamTraits> final { using T = std::unordered_map; static void Write(Message* const msg, const T& in) { WriteParam(msg, in.size()); for (const auto& pair : in) { WriteParam(msg, pair.first); WriteParam(msg, pair.second); } } static bool Read(const Message* const msg, PickleIterator* const itr, T* const out) { size_t size = 0; if (!ReadParam(msg, itr, &size)) return false; T map; map.reserve(size); for (const auto i : mozilla::IntegerRange(size)) { std::pair pair; mozilla::Unused << i; if (!ReadParam(msg, itr, &(pair.first)) || !ReadParam(msg, itr, &(pair.second))) { return false; } map.insert(std::move(pair)); } *out = std::move(map); return true; } }; template <> struct ParamTraits { typedef float paramType; static void Write(Message* aMsg, const paramType& aParam) { aMsg->WriteBytes(&aParam, sizeof(paramType)); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult)); } static void Log(const paramType& aParam, std::wstring* aLog) { aLog->append(StringPrintf(L"%g", aParam)); } }; template <> struct ParamTraits : public ContiguousEnumSerializer {}; template <> struct ParamTraits { typedef nsID paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.m0); WriteParam(aMsg, aParam.m1); WriteParam(aMsg, aParam.m2); for (unsigned int i = 0; i < mozilla::ArrayLength(aParam.m3); i++) { WriteParam(aMsg, aParam.m3[i]); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { if (!ReadParam(aMsg, aIter, &(aResult->m0)) || !ReadParam(aMsg, aIter, &(aResult->m1)) || !ReadParam(aMsg, aIter, &(aResult->m2))) return false; for (unsigned int i = 0; i < mozilla::ArrayLength(aResult->m3); i++) if (!ReadParam(aMsg, aIter, &(aResult->m3[i]))) return false; return true; } static void Log(const paramType& aParam, std::wstring* aLog) { aLog->append(L"{"); aLog->append( StringPrintf(L"%8.8X-%4.4X-%4.4X-", aParam.m0, aParam.m1, aParam.m2)); for (unsigned int i = 0; i < mozilla::ArrayLength(aParam.m3); i++) aLog->append(StringPrintf(L"%2.2X", aParam.m3[i])); aLog->append(L"}"); } }; template <> struct ParamTraits : public ContiguousEnumSerializerInclusive< nsContentPolicyType, nsIContentPolicy::TYPE_INVALID, nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD> {}; template <> struct ParamTraits { typedef mozilla::TimeDuration paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mValue); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return ReadParam(aMsg, aIter, &aResult->mValue); }; }; template <> struct ParamTraits { typedef mozilla::TimeStamp paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mValue); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return ReadParam(aMsg, aIter, &aResult->mValue); }; }; #ifdef XP_WIN template <> struct ParamTraits { typedef mozilla::TimeStampValue paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mGTC); WriteParam(aMsg, aParam.mQPC); WriteParam(aMsg, aParam.mUsedCanonicalNow); WriteParam(aMsg, aParam.mIsNull); WriteParam(aMsg, aParam.mHasQPC); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return (ReadParam(aMsg, aIter, &aResult->mGTC) && ReadParam(aMsg, aIter, &aResult->mQPC) && ReadParam(aMsg, aIter, &aResult->mUsedCanonicalNow) && ReadParam(aMsg, aIter, &aResult->mIsNull) && ReadParam(aMsg, aIter, &aResult->mHasQPC)); } }; #else template <> struct ParamTraits { typedef mozilla::TimeStamp63Bit paramType; static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mUsedCanonicalNow); WriteParam(aMsg, aParam.mTimeStamp); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { bool success = true; uint64_t result; success &= ReadParam(aMsg, aIter, &result); aResult->mUsedCanonicalNow = result & 0x01; success &= ReadParam(aMsg, aIter, &result); aResult->mTimeStamp = result & 0x7FFFFFFFFFFFFFFF; return success; } }; #endif template <> struct ParamTraits { typedef mozilla::dom::ipc::StructuredCloneData paramType; static void Write(Message* aMsg, const paramType& aParam) { aParam.WriteIPCParams(aMsg); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { return aResult->ReadIPCParams(aMsg, aIter); } static void Log(const paramType& aParam, std::wstring* aLog) { LogParam(aParam.DataLength(), aLog); } }; template struct ParamTraits> { typedef mozilla::Maybe paramType; static void Write(Message* msg, const paramType& param) { if (param.isSome()) { WriteParam(msg, true); WriteParam(msg, param.value()); } else { WriteParam(msg, false); } } static bool Read(const Message* msg, PickleIterator* iter, paramType* result) { bool isSome; if (!ReadParam(msg, iter, &isSome)) { return false; } if (isSome) { T tmp; if (!ReadParam(msg, iter, &tmp)) { return false; } *result = mozilla::Some(std::move(tmp)); } else { *result = mozilla::Nothing(); } return true; } }; template struct ParamTraits> { typedef mozilla::EnumSet paramType; typedef U serializedType; static void Write(Message* msg, const paramType& param) { MOZ_RELEASE_ASSERT(IsLegalValue(param.serialize())); WriteParam(msg, param.serialize()); } static bool Read(const Message* msg, PickleIterator* iter, paramType* result) { serializedType tmp; if (ReadParam(msg, iter, &tmp)) { if (IsLegalValue(tmp)) { result->deserialize(tmp); return true; } } return false; } static constexpr serializedType AllEnumBits() { return ~serializedType(0) >> (std::numeric_limits::digits - (mozilla::MaxEnumValue::value + 1)); } static constexpr bool IsLegalValue(const serializedType value) { static_assert(mozilla::MaxEnumValue::value < std::numeric_limits::digits, "Enum max value is not in the range!"); static_assert( std::is_unsigned::value)>::value, "Type of MaxEnumValue::value specialization should be unsigned!"); return (value & AllEnumBits()) == value; } }; template struct ParamTraits> { typedef mozilla::Variant paramType; using Tag = typename mozilla::detail::VariantTag::Type; static void Write(Message* msg, const paramType& param) { WriteParam(msg, param.tag); param.match([msg](const auto& t) { WriteParam(msg, t); }); } // Because VariantReader is a nested struct, we need the dummy template // parameter to avoid making VariantReader<0> an explicit specialization, // which is not allowed for a nested class template template struct VariantReader { using Next = VariantReader; static bool Read(const Message* msg, PickleIterator* iter, Tag tag, paramType* result) { // Since the VariantReader specializations start at N , we need to // subtract one to look at N - 1, the first valid tag. This means our // comparisons are off by 1. If we get to N = 0 then we have failed to // find a match to the tag. if (tag == N - 1) { // Recall, even though the template parameter is N, we are // actually interested in the N - 1 tag. // Default construct our field within the result outparameter and // directly deserialize into the variant. Note that this means that // every type in Ts needs to be default constructible return ReadParam(msg, iter, &result->template emplace()); } else { return Next::Read(msg, iter, tag, result); } } }; // VariantReader // Since we are conditioning on tag = N - 1 in the preceding specialization, // if we get to `VariantReader<0, dummy>` we have failed to find // a matching tag. template struct VariantReader<0, dummy> { static bool Read(const Message* msg, PickleIterator* iter, Tag tag, paramType* result) { return false; } }; static bool Read(const Message* msg, PickleIterator* iter, paramType* result) { Tag tag; if (ReadParam(msg, iter, &tag)) { return VariantReader::Read(msg, iter, tag, result); } return false; } }; template struct ParamTraits> { typedef mozilla::dom::Optional paramType; static void Write(Message* aMsg, const paramType& aParam) { if (aParam.WasPassed()) { WriteParam(aMsg, true); WriteParam(aMsg, aParam.Value()); return; } WriteParam(aMsg, false); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { bool wasPassed = false; if (!ReadParam(aMsg, aIter, &wasPassed)) { return false; } aResult->Reset(); if (wasPassed) { if (!ReadParam(aMsg, aIter, &aResult->Construct())) { return false; } } return true; } }; struct CrossOriginOpenerPolicyValidator { static bool IsLegalValue(nsILoadInfo::CrossOriginOpenerPolicy e) { return e == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE || e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN || e == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS || e == nsILoadInfo:: OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; } }; template <> struct ParamTraits : EnumSerializer {}; struct CrossOriginEmbedderPolicyValidator { static bool IsLegalValue(nsILoadInfo::CrossOriginEmbedderPolicy e) { return e == nsILoadInfo::EMBEDDER_POLICY_NULL || e == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP; } }; template <> struct ParamTraits : EnumSerializer {}; template struct ParamTraits> { typedef mozilla::BitSet paramType; static void Write(Message* aMsg, const paramType& aParam) { for (Word word : aParam.Storage()) { WriteParam(aMsg, word); } } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) { for (Word& word : aResult->Storage()) { if (!ReadParam(aMsg, aIter, &word)) { return false; } } return true; } }; } /* namespace IPC */ #endif /* __IPC_GLUE_IPCMESSAGEUTILSSPECIALIZATIONS_H__ */