diff options
Diffstat (limited to 'gfx/layers/apz/testutil')
-rw-r--r-- | gfx/layers/apz/testutil/APZTestData.cpp | 124 | ||||
-rw-r--r-- | gfx/layers/apz/testutil/APZTestData.h | 252 |
2 files changed, 376 insertions, 0 deletions
diff --git a/gfx/layers/apz/testutil/APZTestData.cpp b/gfx/layers/apz/testutil/APZTestData.cpp new file mode 100644 index 0000000000..4154607d75 --- /dev/null +++ b/gfx/layers/apz/testutil/APZTestData.cpp @@ -0,0 +1,124 @@ +/* -*- 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/. */ + +#include "APZTestData.h" +#include "mozilla/dom/APZTestDataBinding.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsString.h" + +namespace mozilla { +namespace layers { + +struct APZTestDataToJSConverter { + template <typename Key, typename Value, typename KeyValuePair> + static void ConvertMap(const std::map<Key, Value>& aFrom, + dom::Sequence<KeyValuePair>& aOutTo, + void (*aElementConverter)(const Key&, const Value&, + KeyValuePair&)) { + for (auto it = aFrom.begin(); it != aFrom.end(); ++it) { + if (!aOutTo.AppendElement(fallible)) { + // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might + // involve multiple reallocations) and potentially crashing here, + // SetCapacity could be called outside the loop once. + mozalloc_handle_oom(0); + } + aElementConverter(it->first, it->second, aOutTo.LastElement()); + } + } + + template <typename Src, typename Target> + static void ConvertList(const nsTArray<Src>& aFrom, + dom::Sequence<Target>& aOutTo, + void (*aElementConverter)(const Src&, Target&)) { + for (auto it = aFrom.begin(); it != aFrom.end(); ++it) { + if (!aOutTo.AppendElement(fallible)) { + // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might + // involve multiple reallocations) and potentially crashing here, + // SetCapacity could be called outside the loop once. + mozalloc_handle_oom(0); + } + aElementConverter(*it, aOutTo.LastElement()); + } + } + + static void ConvertAPZTestData(const APZTestData& aFrom, + dom::APZTestData& aOutTo) { + ConvertMap(aFrom.mPaints, aOutTo.mPaints.Construct(), ConvertBucket); + ConvertMap(aFrom.mRepaintRequests, aOutTo.mRepaintRequests.Construct(), + ConvertBucket); + ConvertList(aFrom.mHitResults, aOutTo.mHitResults.Construct(), + ConvertHitResult); + ConvertList(aFrom.mSampledResults, aOutTo.mSampledResults.Construct(), + ConvertSampledResult); + ConvertMap(aFrom.mAdditionalData, aOutTo.mAdditionalData.Construct(), + ConvertAdditionalDataEntry); + } + + static void ConvertBucket(const SequenceNumber& aKey, + const APZTestData::Bucket& aValue, + dom::APZBucket& aOutKeyValuePair) { + aOutKeyValuePair.mSequenceNumber.Construct() = aKey; + ConvertMap(aValue, aOutKeyValuePair.mScrollFrames.Construct(), + ConvertScrollFrameData); + } + + static void ConvertScrollFrameData(const APZTestData::ViewID& aKey, + const APZTestData::ScrollFrameData& aValue, + dom::ScrollFrameData& aOutKeyValuePair) { + aOutKeyValuePair.mScrollId.Construct() = aKey; + ConvertMap(aValue, aOutKeyValuePair.mEntries.Construct(), ConvertEntry); + } + + static void ConvertEntry(const std::string& aKey, const std::string& aValue, + dom::ScrollFrameDataEntry& aOutKeyValuePair) { + ConvertString(aKey, aOutKeyValuePair.mKey.Construct()); + ConvertString(aValue, aOutKeyValuePair.mValue.Construct()); + } + + static void ConvertAdditionalDataEntry( + const std::string& aKey, const std::string& aValue, + dom::AdditionalDataEntry& aOutKeyValuePair) { + ConvertString(aKey, aOutKeyValuePair.mKey.Construct()); + ConvertString(aValue, aOutKeyValuePair.mValue.Construct()); + } + + static void ConvertString(const std::string& aFrom, nsString& aOutTo) { + CopyUTF8toUTF16(aFrom, aOutTo); + } + + static void ConvertHitResult(const APZTestData::HitResult& aResult, + dom::APZHitResult& aOutHitResult) { + aOutHitResult.mScreenX.Construct() = aResult.point.x; + aOutHitResult.mScreenY.Construct() = aResult.point.y; + static_assert(MaxEnumValue<gfx::CompositorHitTestInfo::valueType>::value < + std::numeric_limits<uint16_t>::digits, + "CompositorHitTestFlags MAX value have to be less than " + "number of bits in uint16_t"); + aOutHitResult.mHitResult.Construct() = + static_cast<uint16_t>(aResult.result.serialize()); + aOutHitResult.mLayersId.Construct() = aResult.layersId.mId; + aOutHitResult.mScrollId.Construct() = aResult.scrollId; + } + + static void ConvertSampledResult(const APZTestData::SampledResult& aResult, + dom::APZSampledResult& aOutSampledResult) { + aOutSampledResult.mScrollOffsetX.Construct() = aResult.scrollOffset.x; + aOutSampledResult.mScrollOffsetY.Construct() = aResult.scrollOffset.y; + aOutSampledResult.mLayersId.Construct() = aResult.layersId.mId; + aOutSampledResult.mScrollId.Construct() = aResult.scrollId; + aOutSampledResult.mSampledTimeStamp.Construct() = aResult.sampledTimeStamp; + } +}; + +bool APZTestData::ToJS(JS::MutableHandle<JS::Value> aOutValue, + JSContext* aContext) const { + dom::APZTestData result; + APZTestDataToJSConverter::ConvertAPZTestData(*this, result); + return dom::ToJSValue(aContext, result, aOutValue); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/apz/testutil/APZTestData.h b/gfx/layers/apz/testutil/APZTestData.h new file mode 100644 index 0000000000..e4a73c80cc --- /dev/null +++ b/gfx/layers/apz/testutil/APZTestData.h @@ -0,0 +1,252 @@ +/* -*- 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_layers_APZTestData_h +#define mozilla_layers_APZTestData_h + +#include <map> + +#include "nsDebug.h" // for NS_WARNING +#include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp +#include "nsTArray.h" +#include "mozilla/Assertions.h" // for MOZ_ASSERT +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/GfxMessageUtils.h" // for ParamTraits specializations +#include "mozilla/StaticPrefs_apz.h" +#include "mozilla/ToString.h" // for ToString +#include "mozilla/gfx/CompositorHitTestInfo.h" +#include "mozilla/layers/LayersMessageUtils.h" // for ParamTraits specializations +#include "mozilla/layers/ScrollableLayerGuid.h" +#include "ipc/IPCMessageUtils.h" +#include "js/TypeDecls.h" + +namespace mozilla { +namespace layers { + +typedef uint32_t SequenceNumber; + +/** + * This structure is used to store information logged by various gecko + * components for later examination by test code. + * It contains a bucket for every paint (initiated on the client side), + * and every repaint request (initiated on the compositor side by + * AsyncPanZoomController::RequestContentRepait), which are identified by + * sequence numbers, and within that, a set of arbitrary string key/value + * pairs for every scrollable frame, identified by a scroll id. + * There are two instances of this data structure for every content thread: + * one on the client side and one of the compositor side. + * It also contains a list of hit-test results for MozMouseHittest events + * dispatched during testing. This list is only populated on the compositor + * instance of this class. + */ +// TODO(botond): +// - Improve warnings/asserts. +// - Add ability to associate a repaint request triggered during a layers +// update with the sequence number of the paint that caused the layers +// update. +class APZTestData { + typedef ScrollableLayerGuid::ViewID ViewID; + friend struct IPC::ParamTraits<APZTestData>; + friend struct APZTestDataToJSConverter; + + public: + void StartNewPaint(SequenceNumber aSequenceNumber) { + // We should never get more than one paint with the same sequence number. + MOZ_ASSERT(mPaints.find(aSequenceNumber) == mPaints.end()); + mPaints.insert(DataStore::value_type(aSequenceNumber, Bucket())); + } + void LogTestDataForPaint(SequenceNumber aSequenceNumber, ViewID aScrollId, + const std::string& aKey, const std::string& aValue) { + LogTestDataImpl(mPaints, aSequenceNumber, aScrollId, aKey, aValue); + } + + void StartNewRepaintRequest(SequenceNumber aSequenceNumber) { + typedef std::pair<DataStore::iterator, bool> InsertResultT; + DebugOnly<InsertResultT> insertResult = mRepaintRequests.insert( + DataStore::value_type(aSequenceNumber, Bucket())); + MOZ_ASSERT(((InsertResultT&)insertResult).second, + "Already have a repaint request with this sequence number"); + } + void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber, + ViewID aScrollId, const std::string& aKey, + const std::string& aValue) { + LogTestDataImpl(mRepaintRequests, aSequenceNumber, aScrollId, aKey, aValue); + } + void RecordHitResult(const ScreenPoint& aPoint, + const mozilla::gfx::CompositorHitTestInfo& aResult, + const LayersId& aLayersId, const ViewID& aScrollId) { + mHitResults.AppendElement(HitResult{aPoint, aResult, aLayersId, aScrollId}); + } + void RecordSampledResult(const CSSPoint& aScrollOffset, + DOMHighResTimeStamp aSampledTimeStamp, + const LayersId& aLayersId, const ViewID& aScrollId) { + mSampledResults.AppendElement( + SampledResult{aScrollOffset, aSampledTimeStamp, aLayersId, aScrollId}); + } + void RecordAdditionalData(const std::string& aKey, + const std::string& aValue) { + mAdditionalData[aKey] = aValue; + } + + // Convert this object to a JS representation. + bool ToJS(JS::MutableHandle<JS::Value> aOutValue, JSContext* aContext) const; + + // Use dummy derived structures wrapping the typedefs to work around a type + // name length limit in MSVC. + typedef std::map<std::string, std::string> ScrollFrameDataBase; + struct ScrollFrameData : ScrollFrameDataBase {}; + typedef std::map<ViewID, ScrollFrameData> BucketBase; + struct Bucket : BucketBase {}; + typedef std::map<SequenceNumber, Bucket> DataStoreBase; + struct DataStore : DataStoreBase {}; + struct HitResult { + ScreenPoint point; + mozilla::gfx::CompositorHitTestInfo result; + LayersId layersId; + ViewID scrollId; + }; + struct SampledResult { + CSSPoint scrollOffset; + DOMHighResTimeStamp sampledTimeStamp; + LayersId layersId; + ViewID scrollId; + }; + + private: + DataStore mPaints; + DataStore mRepaintRequests; + CopyableTArray<HitResult> mHitResults; + CopyableTArray<SampledResult> mSampledResults; + // Additional free-form data that's not grouped paint or scroll frame. + std::map<std::string, std::string> mAdditionalData; + + void LogTestDataImpl(DataStore& aDataStore, SequenceNumber aSequenceNumber, + ViewID aScrollId, const std::string& aKey, + const std::string& aValue) { + auto bucketIterator = aDataStore.find(aSequenceNumber); + if (bucketIterator == aDataStore.end()) { + MOZ_ASSERT(false, + "LogTestDataImpl called with nonexistent sequence number"); + return; + } + Bucket& bucket = bucketIterator->second; + ScrollFrameData& scrollFrameData = + bucket[aScrollId]; // create if doesn't exist + MOZ_ASSERT(scrollFrameData.find(aKey) == scrollFrameData.end() || + scrollFrameData[aKey] == aValue); + scrollFrameData.insert(ScrollFrameData::value_type(aKey, aValue)); + } +}; + +// A helper class for logging data for a paint. +class APZPaintLogHelper { + public: + APZPaintLogHelper(APZTestData* aTestData, SequenceNumber aPaintSequenceNumber) + : mTestData(aTestData), mPaintSequenceNumber(aPaintSequenceNumber) { + MOZ_ASSERT(!aTestData || StaticPrefs::apz_test_logging_enabled(), + "don't call me"); + } + + template <typename Value> + void LogTestData(ScrollableLayerGuid::ViewID aScrollId, + const std::string& aKey, const Value& aValue) const { + if (mTestData) { // avoid stringifying if mTestData == nullptr + LogTestData(aScrollId, aKey, ToString(aValue)); + } + } + + void LogTestData(ScrollableLayerGuid::ViewID aScrollId, + const std::string& aKey, const std::string& aValue) const { + if (mTestData) { + mTestData->LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, + aValue); + } + } + + private: + APZTestData* mTestData; + SequenceNumber mPaintSequenceNumber; +}; + +} // namespace layers +} // namespace mozilla + +namespace IPC { + +template <> +struct ParamTraits<mozilla::layers::APZTestData> { + typedef mozilla::layers::APZTestData paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mPaints); + WriteParam(aWriter, aParam.mRepaintRequests); + WriteParam(aWriter, aParam.mHitResults); + WriteParam(aWriter, aParam.mSampledResults); + WriteParam(aWriter, aParam.mAdditionalData); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->mPaints) && + ReadParam(aReader, &aResult->mRepaintRequests) && + ReadParam(aReader, &aResult->mHitResults) && + ReadParam(aReader, &aResult->mSampledResults) && + ReadParam(aReader, &aResult->mAdditionalData)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::APZTestData::ScrollFrameData> + : ParamTraits<mozilla::layers::APZTestData::ScrollFrameDataBase> {}; + +template <> +struct ParamTraits<mozilla::layers::APZTestData::Bucket> + : ParamTraits<mozilla::layers::APZTestData::BucketBase> {}; + +template <> +struct ParamTraits<mozilla::layers::APZTestData::DataStore> + : ParamTraits<mozilla::layers::APZTestData::DataStoreBase> {}; + +template <> +struct ParamTraits<mozilla::layers::APZTestData::HitResult> { + typedef mozilla::layers::APZTestData::HitResult paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.point); + WriteParam(aWriter, aParam.result); + WriteParam(aWriter, aParam.layersId); + WriteParam(aWriter, aParam.scrollId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->point) && + ReadParam(aReader, &aResult->result) && + ReadParam(aReader, &aResult->layersId) && + ReadParam(aReader, &aResult->scrollId)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::APZTestData::SampledResult> { + typedef mozilla::layers::APZTestData::SampledResult paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.scrollOffset); + WriteParam(aWriter, aParam.sampledTimeStamp); + WriteParam(aWriter, aParam.layersId); + WriteParam(aWriter, aParam.scrollId); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return (ReadParam(aReader, &aResult->scrollOffset) && + ReadParam(aReader, &aResult->sampledTimeStamp) && + ReadParam(aReader, &aResult->layersId) && + ReadParam(aReader, &aResult->scrollId)); + } +}; + +} // namespace IPC + +#endif /* mozilla_layers_APZTestData_h */ |