273 lines
10 KiB
C++
273 lines
10 KiB
C++
/* -*- 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 "TestWRScrollData.h"
|
|
#include "APZTestAccess.h"
|
|
#include "gtest/gtest.h"
|
|
#include "FrameMetrics.h"
|
|
#include "gfxPlatform.h"
|
|
#include "mozilla/layers/APZUpdater.h"
|
|
#include "mozilla/layers/LayersTypes.h"
|
|
#include "mozilla/layers/ScrollableLayerGuid.h"
|
|
#include "mozilla/layers/WebRenderScrollDataWrapper.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "apz/src/APZCTreeManager.h"
|
|
|
|
using mozilla::layers::APZCTreeManager;
|
|
using mozilla::layers::APZUpdater;
|
|
using mozilla::layers::LayersId;
|
|
using mozilla::layers::ScrollableLayerGuid;
|
|
using mozilla::layers::ScrollMetadata;
|
|
using mozilla::layers::TestWRScrollData;
|
|
using mozilla::layers::WebRenderLayerScrollData;
|
|
using mozilla::layers::WebRenderScrollDataWrapper;
|
|
|
|
/* static */
|
|
TestWRScrollData TestWRScrollData::Create(const char* aTreeShape,
|
|
const APZUpdater& aUpdater,
|
|
const LayerIntRect* aVisibleRects,
|
|
const gfx::Matrix4x4* aTransforms) {
|
|
// The WebRenderLayerScrollData tree needs to be created in a fairly
|
|
// particular way (for example, each node needs to know the number of
|
|
// descendants it has), so this function takes care to create the nodes
|
|
// in the same order as WebRenderCommandBuilder would.
|
|
TestWRScrollData result;
|
|
const size_t len = strlen(aTreeShape);
|
|
// "Layer index" in this function refers to the index by which a layer will
|
|
// be accessible via TestWRScrollData::GetLayer(), and matches the order
|
|
// in which the layer appears in |aTreeShape|.
|
|
size_t currentLayerIndex = 0;
|
|
struct LayerEntry {
|
|
size_t mLayerIndex;
|
|
int32_t mDescendantCount = 0;
|
|
};
|
|
// Layers we have encountered in |aTreeShape|, but have not built a
|
|
// WebRenderLayerScrollData for. (It can only be built after its
|
|
// descendants have been encountered and counted.)
|
|
std::stack<LayerEntry> pendingLayers;
|
|
std::vector<WebRenderLayerScrollData> finishedLayers;
|
|
// Tracks the level of nesting of '(' characters. Starts at 1 to account
|
|
// for the root layer.
|
|
size_t depth = 1;
|
|
// Helper function for finishing a layer once all its descendants have been
|
|
// encountered.
|
|
auto finishLayer = [&] {
|
|
MOZ_ASSERT(!pendingLayers.empty());
|
|
LayerEntry entry = pendingLayers.top();
|
|
|
|
WebRenderLayerScrollData layer;
|
|
APZTestAccess::InitializeForTest(layer, entry.mDescendantCount);
|
|
if (aVisibleRects) {
|
|
layer.SetVisibleRect(aVisibleRects[entry.mLayerIndex]);
|
|
}
|
|
if (aTransforms) {
|
|
layer.SetTransform(aTransforms[entry.mLayerIndex]);
|
|
}
|
|
finishedLayers.push_back(std::move(layer));
|
|
|
|
// |finishedLayers| stores the layers in a different order than they
|
|
// appeared in |aTreeShape|. To be able to access layers by their layer
|
|
// index, keep a mapping from layer index to index in |finishedLayers|.
|
|
result.mIndexMap.emplace(entry.mLayerIndex, finishedLayers.size() - 1);
|
|
|
|
pendingLayers.pop();
|
|
|
|
// Keep track of descendant counts. The +1 is for the layer just finished.
|
|
if (!pendingLayers.empty()) {
|
|
pendingLayers.top().mDescendantCount += (entry.mDescendantCount + 1);
|
|
}
|
|
};
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if (aTreeShape[i] == '(') {
|
|
++depth;
|
|
} else if (aTreeShape[i] == ')') {
|
|
if (pendingLayers.size() <= 1) {
|
|
printf("Invalid tree shape: too many ')'\n");
|
|
MOZ_CRASH();
|
|
}
|
|
finishLayer(); // finish last layer at current depth
|
|
--depth;
|
|
} else {
|
|
if (aTreeShape[i] != 'x') {
|
|
printf("The only allowed character to represent a layer is 'x'\n");
|
|
MOZ_CRASH();
|
|
}
|
|
if (depth == pendingLayers.size()) {
|
|
// We have a previous layer at this same depth to finish.
|
|
if (depth <= 1) {
|
|
printf("The tree is only allowed to have one root\n");
|
|
MOZ_CRASH();
|
|
}
|
|
finishLayer();
|
|
}
|
|
MOZ_ASSERT(depth == pendingLayers.size() + 1);
|
|
pendingLayers.push({currentLayerIndex});
|
|
++currentLayerIndex;
|
|
}
|
|
}
|
|
if (pendingLayers.size() != 1) {
|
|
printf("Invalid tree shape: '(' and ')' not balanced\n");
|
|
MOZ_CRASH();
|
|
}
|
|
finishLayer(); // finish root layer
|
|
|
|
// As in WebRenderCommandBuilder, the layers need to be added to the
|
|
// WebRenderScrollData in reverse of the order in which they were built.
|
|
for (auto it = finishedLayers.rbegin(); it != finishedLayers.rend(); ++it) {
|
|
result.AddLayerData(std::move(*it));
|
|
}
|
|
// mIndexMap also needs to be adjusted to accout for the reversal above.
|
|
for (auto& [layerIndex, storedIndex] : result.mIndexMap) {
|
|
(void)layerIndex; // suppress -Werror=unused-variable
|
|
storedIndex = result.GetLayerCount() - storedIndex - 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const WebRenderLayerScrollData* TestWRScrollData::operator[](
|
|
size_t aLayerIndex) const {
|
|
auto it = mIndexMap.find(aLayerIndex);
|
|
if (it == mIndexMap.end()) {
|
|
return nullptr;
|
|
}
|
|
return GetLayerData(it->second);
|
|
}
|
|
|
|
WebRenderLayerScrollData* TestWRScrollData::operator[](size_t aLayerIndex) {
|
|
auto it = mIndexMap.find(aLayerIndex);
|
|
if (it == mIndexMap.end()) {
|
|
return nullptr;
|
|
}
|
|
return GetLayerData(it->second);
|
|
}
|
|
|
|
void TestWRScrollData::SetScrollMetadata(
|
|
size_t aLayerIndex, const nsTArray<ScrollMetadata>& aMetadata) {
|
|
WebRenderLayerScrollData* layer = operator[](aLayerIndex);
|
|
MOZ_ASSERT(layer);
|
|
for (const ScrollMetadata& metadata : aMetadata) {
|
|
layer->AppendScrollMetadata(*this, metadata);
|
|
}
|
|
}
|
|
|
|
class WebRenderScrollDataWrapperTester : public ::testing::Test {
|
|
protected:
|
|
virtual void SetUp() {
|
|
// This ensures ScrollMetadata::sNullMetadata is initialized.
|
|
gfxPlatform::GetPlatform();
|
|
|
|
mManager = APZCTreeManager::Create(LayersId{0});
|
|
mUpdater = new APZUpdater(mManager, false);
|
|
}
|
|
|
|
RefPtr<APZCTreeManager> mManager;
|
|
RefPtr<APZUpdater> mUpdater;
|
|
};
|
|
|
|
TEST_F(WebRenderScrollDataWrapperTester, SimpleTree) {
|
|
auto layers = TestWRScrollData::Create("x(x(x(xx)x(x)))", *mUpdater);
|
|
WebRenderScrollDataWrapper w0(*mUpdater, &layers);
|
|
|
|
ASSERT_EQ(layers[0], w0.GetLayer());
|
|
WebRenderScrollDataWrapper w1 = w0.GetLastChild();
|
|
ASSERT_EQ(layers[1], w1.GetLayer());
|
|
ASSERT_FALSE(w1.GetPrevSibling().IsValid());
|
|
WebRenderScrollDataWrapper w5 = w1.GetLastChild();
|
|
ASSERT_EQ(layers[5], w5.GetLayer());
|
|
WebRenderScrollDataWrapper w6 = w5.GetLastChild();
|
|
ASSERT_EQ(layers[6], w6.GetLayer());
|
|
ASSERT_FALSE(w6.GetLastChild().IsValid());
|
|
WebRenderScrollDataWrapper w2 = w5.GetPrevSibling();
|
|
ASSERT_EQ(layers[2], w2.GetLayer());
|
|
ASSERT_FALSE(w2.GetPrevSibling().IsValid());
|
|
WebRenderScrollDataWrapper w4 = w2.GetLastChild();
|
|
ASSERT_EQ(layers[4], w4.GetLayer());
|
|
ASSERT_FALSE(w4.GetLastChild().IsValid());
|
|
WebRenderScrollDataWrapper w3 = w4.GetPrevSibling();
|
|
ASSERT_EQ(layers[3], w3.GetLayer());
|
|
ASSERT_FALSE(w3.GetLastChild().IsValid());
|
|
ASSERT_FALSE(w3.GetPrevSibling().IsValid());
|
|
}
|
|
|
|
static ScrollMetadata MakeMetadata(ScrollableLayerGuid::ViewID aId) {
|
|
ScrollMetadata metadata;
|
|
metadata.GetMetrics().SetScrollId(aId);
|
|
return metadata;
|
|
}
|
|
|
|
TEST_F(WebRenderScrollDataWrapperTester, MultiFramemetricsTree) {
|
|
auto layers = TestWRScrollData::Create("x(x(x(xx)x(x)))", *mUpdater);
|
|
|
|
nsTArray<ScrollMetadata> metadata;
|
|
metadata.InsertElementAt(0,
|
|
MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID +
|
|
0)); // topmost of root layer
|
|
metadata.InsertElementAt(0,
|
|
MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 1));
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 2));
|
|
metadata.InsertElementAt(0,
|
|
MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(
|
|
ScrollableLayerGuid::NULL_SCROLL_ID)); // bottom of root layer
|
|
layers.SetScrollMetadata(0, metadata);
|
|
|
|
metadata.Clear();
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 3));
|
|
layers.SetScrollMetadata(1, metadata);
|
|
|
|
metadata.Clear();
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 4));
|
|
layers.SetScrollMetadata(2, metadata);
|
|
|
|
metadata.Clear();
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 5));
|
|
layers.SetScrollMetadata(4, metadata);
|
|
|
|
metadata.Clear();
|
|
metadata.InsertElementAt(0,
|
|
MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
|
|
metadata.InsertElementAt(
|
|
0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 6));
|
|
layers.SetScrollMetadata(5, metadata);
|
|
|
|
WebRenderScrollDataWrapper wrapper(*mUpdater, &layers);
|
|
nsTArray<WebRenderLayerScrollData*> expectedLayers;
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[0]);
|
|
expectedLayers.AppendElement(layers[1]);
|
|
expectedLayers.AppendElement(layers[5]);
|
|
expectedLayers.AppendElement(layers[5]);
|
|
expectedLayers.AppendElement(layers[6]);
|
|
nsTArray<ScrollableLayerGuid::ViewID> expectedIds;
|
|
expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 0);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 1);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 2);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 3);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 6);
|
|
expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
|
|
for (int i = 0; i < 10; i++) {
|
|
ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
|
|
ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
|
|
wrapper = wrapper.GetLastChild();
|
|
}
|
|
ASSERT_FALSE(wrapper.IsValid());
|
|
}
|