summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/testutil
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/testutil')
-rw-r--r--gfx/layers/apz/testutil/APZTestData.cpp124
-rw-r--r--gfx/layers/apz/testutil/APZTestData.h252
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 */