mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 01:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
120
gfx/layers/apz/test/gtest/APZCBasicTester.h
Normal file
120
gfx/layers/apz/test/gtest/APZCBasicTester.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_APZCBasicTester_h
|
||||
#define mozilla_layers_APZCBasicTester_h
|
||||
|
||||
/**
|
||||
* Defines a test fixture used for testing a single APZC.
|
||||
*/
|
||||
|
||||
#include "APZTestCommon.h"
|
||||
|
||||
class APZCBasicTester : public APZCTesterBase {
|
||||
public:
|
||||
explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
|
||||
: mGestureBehavior(aGestureBehavior)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
gfxPrefs::GetSingleton();
|
||||
APZThreadUtils::SetThreadAssertionsEnabled(false);
|
||||
APZThreadUtils::SetControllerThread(MessageLoop::current());
|
||||
|
||||
tm = new TestAPZCTreeManager(mcc);
|
||||
apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
|
||||
apzc->SetFrameMetrics(TestFrameMetrics());
|
||||
apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the APZC's scroll range in CSS pixels.
|
||||
*/
|
||||
CSSRect GetScrollRange() const
|
||||
{
|
||||
const FrameMetrics& metrics = apzc->GetFrameMetrics();
|
||||
return CSSRect(
|
||||
metrics.GetScrollableRect().TopLeft(),
|
||||
metrics.GetScrollableRect().Size() - metrics.CalculateCompositedSizeInCssPixels());
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
apzc->Destroy();
|
||||
tm->ClearTree();
|
||||
tm->ClearContentController();
|
||||
}
|
||||
|
||||
void MakeApzcWaitForMainThread()
|
||||
{
|
||||
apzc->SetWaitForMainThread();
|
||||
}
|
||||
|
||||
void MakeApzcZoomable()
|
||||
{
|
||||
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f)));
|
||||
}
|
||||
|
||||
void MakeApzcUnzoomable()
|
||||
{
|
||||
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToParentLayerScale(1.0f), CSSToParentLayerScale(1.0f)));
|
||||
}
|
||||
|
||||
void PanIntoOverscroll();
|
||||
|
||||
/**
|
||||
* Sample animations once, 1 ms later than the last sample.
|
||||
*/
|
||||
void SampleAnimationOnce()
|
||||
{
|
||||
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
mcc->AdvanceBy(increment);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample animations until we recover from overscroll.
|
||||
* @param aExpectedScrollOffset the expected reported scroll offset
|
||||
* throughout the animation
|
||||
*/
|
||||
void SampleAnimationUntilRecoveredFromOverscroll(const ParentLayerPoint& aExpectedScrollOffset)
|
||||
{
|
||||
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
|
||||
bool recoveredFromOverscroll = false;
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
|
||||
// The reported scroll offset should be the same throughout.
|
||||
EXPECT_EQ(aExpectedScrollOffset, pointOut);
|
||||
|
||||
// Trigger computation of the overscroll tranform, to make sure
|
||||
// no assetions fire during the calculation.
|
||||
apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL);
|
||||
|
||||
if (!apzc->IsOverscrolled()) {
|
||||
recoveredFromOverscroll = true;
|
||||
}
|
||||
|
||||
mcc->AdvanceBy(increment);
|
||||
}
|
||||
EXPECT_TRUE(recoveredFromOverscroll);
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
void TestOverscroll();
|
||||
|
||||
AsyncPanZoomController::GestureBehavior mGestureBehavior;
|
||||
RefPtr<TestAPZCTreeManager> tm;
|
||||
RefPtr<TestAsyncPanZoomController> apzc;
|
||||
};
|
||||
|
||||
#endif // mozilla_layers_APZCBasicTester_h
|
||||
194
gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
Normal file
194
gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_APZCTreeManagerTester_h
|
||||
#define mozilla_layers_APZCTreeManagerTester_h
|
||||
|
||||
/**
|
||||
* Defines a test fixture used for testing multiple APZCs interacting in
|
||||
* an APZCTreeManager.
|
||||
*/
|
||||
|
||||
#include "APZTestCommon.h"
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
class APZCTreeManagerTester : public APZCTesterBase {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
gfxPrefs::GetSingleton();
|
||||
gfxPlatform::GetPlatform();
|
||||
APZThreadUtils::SetThreadAssertionsEnabled(false);
|
||||
APZThreadUtils::SetControllerThread(MessageLoop::current());
|
||||
|
||||
manager = new TestAPZCTreeManager(mcc);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
manager->ClearTree();
|
||||
manager->ClearContentController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample animations once for all APZCs, 1 ms later than the last sample.
|
||||
*/
|
||||
void SampleAnimationsOnce() {
|
||||
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
mcc->AdvanceBy(increment);
|
||||
|
||||
for (const RefPtr<Layer>& layer : layers) {
|
||||
if (TestAsyncPanZoomController* apzc = ApzcOf(layer)) {
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<Layer> > layers;
|
||||
RefPtr<LayerManager> lm;
|
||||
RefPtr<Layer> root;
|
||||
|
||||
RefPtr<TestAPZCTreeManager> manager;
|
||||
|
||||
protected:
|
||||
static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,
|
||||
const CSSRect& aScrollableRect,
|
||||
const ParentLayerRect& aCompositionBounds)
|
||||
{
|
||||
ScrollMetadata metadata;
|
||||
FrameMetrics& metrics = metadata.GetMetrics();
|
||||
metrics.SetScrollId(aScrollId);
|
||||
// By convention in this test file, START_SCROLL_ID is the root, so mark it as such.
|
||||
if (aScrollId == FrameMetrics::START_SCROLL_ID) {
|
||||
metadata.SetIsLayersIdRoot(true);
|
||||
}
|
||||
metrics.SetCompositionBounds(aCompositionBounds);
|
||||
metrics.SetScrollableRect(aScrollableRect);
|
||||
metrics.SetScrollOffset(CSSPoint(0, 0));
|
||||
metadata.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
|
||||
metadata.SetLineScrollAmount(LayoutDeviceIntSize(5, 10));
|
||||
metadata.SetAllowVerticalScrollWithWheel(true);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
static void SetEventRegionsBasedOnBottommostMetrics(Layer* aLayer)
|
||||
{
|
||||
const FrameMetrics& metrics = aLayer->GetScrollMetadata(0).GetMetrics();
|
||||
CSSRect scrollableRect = metrics.GetScrollableRect();
|
||||
if (!scrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
|
||||
// The purpose of this is to roughly mimic what layout would do in the
|
||||
// case of a scrollable frame with the event regions and clip. This lets
|
||||
// us exercise the hit-testing code in APZCTreeManager
|
||||
EventRegions er = aLayer->GetEventRegions();
|
||||
IntRect scrollRect = RoundedToInt(
|
||||
scrollableRect * metrics.LayersPixelsPerCSSPixel()).ToUnknownRect();
|
||||
er.mHitRegion = nsIntRegion(IntRect(
|
||||
RoundedToInt(metrics.GetCompositionBounds().TopLeft().ToUnknownPoint()),
|
||||
scrollRect.Size()));
|
||||
aLayer->SetEventRegions(er);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
|
||||
CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
|
||||
ParentLayerIntRect compositionBounds = ViewAs<ParentLayerPixel>(
|
||||
aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds());
|
||||
ScrollMetadata metadata = BuildScrollMetadata(aScrollId, aScrollableRect,
|
||||
ParentLayerRect(compositionBounds));
|
||||
aLayer->SetScrollMetadata(metadata);
|
||||
aLayer->SetClipRect(Some(compositionBounds));
|
||||
SetEventRegionsBasedOnBottommostMetrics(aLayer);
|
||||
}
|
||||
|
||||
void SetScrollHandoff(Layer* aChild, Layer* aParent) {
|
||||
ScrollMetadata metadata = aChild->GetScrollMetadata(0);
|
||||
metadata.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId());
|
||||
aChild->SetScrollMetadata(metadata);
|
||||
}
|
||||
|
||||
static TestAsyncPanZoomController* ApzcOf(Layer* aLayer) {
|
||||
EXPECT_EQ(1u, aLayer->GetScrollMetadataCount());
|
||||
return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(0);
|
||||
}
|
||||
|
||||
static TestAsyncPanZoomController* ApzcOf(Layer* aLayer, uint32_t aIndex) {
|
||||
EXPECT_LT(aIndex, aLayer->GetScrollMetadataCount());
|
||||
return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(aIndex);
|
||||
}
|
||||
|
||||
void CreateSimpleScrollingLayer() {
|
||||
const char* layerTreeSyntax = "t";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
|
||||
}
|
||||
|
||||
void CreateSimpleDTCScrollingLayer() {
|
||||
const char* layerTreeSyntax = "t";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
|
||||
|
||||
EventRegions regions;
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 200));
|
||||
regions.mDispatchToContentHitRegion = regions.mHitRegion;
|
||||
layers[0]->SetEventRegions(regions);
|
||||
}
|
||||
|
||||
void CreateSimpleMultiLayerTree() {
|
||||
const char* layerTreeSyntax = "c(tt)";
|
||||
// LayerID 0 12
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
nsIntRegion(IntRect(0,0,100,50)),
|
||||
nsIntRegion(IntRect(0,50,100,50)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
}
|
||||
|
||||
void CreatePotentiallyLeakingTree() {
|
||||
const char* layerTreeSyntax = "c(c(c(t))c(c(t)))";
|
||||
// LayerID 0 1 2 3 4 5 6
|
||||
root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
|
||||
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
|
||||
}
|
||||
|
||||
void CreateBug1194876Tree() {
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
// LayerID 0 1
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollHandoff(layers[1], layers[0]);
|
||||
|
||||
// Make layers[1] the root content
|
||||
ScrollMetadata childMetadata = layers[1]->GetScrollMetadata(0);
|
||||
childMetadata.GetMetrics().SetIsRootContent(true);
|
||||
layers[1]->SetScrollMetadata(childMetadata);
|
||||
|
||||
// Both layers are fully dispatch-to-content
|
||||
EventRegions regions;
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
|
||||
regions.mDispatchToContentHitRegion = regions.mHitRegion;
|
||||
layers[0]->SetEventRegions(regions);
|
||||
layers[1]->SetEventRegions(regions);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // mozilla_layers_APZCTreeManagerTester_h
|
||||
609
gfx/layers/apz/test/gtest/APZTestCommon.h
Normal file
609
gfx/layers/apz/test/gtest/APZTestCommon.h
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_APZTestCommon_h
|
||||
#define mozilla_layers_APZTestCommon_h
|
||||
|
||||
/**
|
||||
* Defines a set of mock classes and utility functions/classes for
|
||||
* writing APZ gtests.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
|
||||
#include "mozilla/layers/GeckoContentController.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/layers/APZCTreeManager.h"
|
||||
#include "mozilla/layers/LayerMetricsWrapper.h"
|
||||
#include "mozilla/layers/APZThreadUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "apz/src/AsyncPanZoomController.h"
|
||||
#include "apz/src/HitTestingTreeNode.h"
|
||||
#include "base/task.h"
|
||||
#include "Layers.h"
|
||||
#include "TestLayers.h"
|
||||
#include "UnitTransforms.h"
|
||||
#include "gfxPrefs.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
using ::testing::_;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::AtLeast;
|
||||
using ::testing::AtMost;
|
||||
using ::testing::MockFunction;
|
||||
using ::testing::InSequence;
|
||||
typedef mozilla::layers::GeckoContentController::TapType TapType;
|
||||
|
||||
template<class T>
|
||||
class ScopedGfxPref {
|
||||
public:
|
||||
ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
|
||||
: mSetPrefFunc(aSetPrefFunc)
|
||||
{
|
||||
mOldVal = aGetPrefFunc();
|
||||
aSetPrefFunc(aVal);
|
||||
}
|
||||
|
||||
~ScopedGfxPref() {
|
||||
mSetPrefFunc(mOldVal);
|
||||
}
|
||||
|
||||
private:
|
||||
void (*mSetPrefFunc)(T);
|
||||
T mOldVal;
|
||||
};
|
||||
|
||||
#define SCOPED_GFX_PREF(prefBase, prefType, prefValue) \
|
||||
ScopedGfxPref<prefType> pref_##prefBase( \
|
||||
&(gfxPrefs::prefBase), \
|
||||
&(gfxPrefs::Set##prefBase), \
|
||||
prefValue)
|
||||
|
||||
static TimeStamp GetStartupTime() {
|
||||
static TimeStamp sStartupTime = TimeStamp::Now();
|
||||
return sStartupTime;
|
||||
}
|
||||
|
||||
class MockContentController : public GeckoContentController {
|
||||
public:
|
||||
MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
|
||||
MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
|
||||
MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
|
||||
MOCK_METHOD5(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
|
||||
MOCK_METHOD4(NotifyPinchGesture, void(PinchGestureInput::PinchGestureType, const ScrollableLayerGuid&, LayoutDeviceCoord, Modifiers));
|
||||
// Can't use the macros with already_AddRefed :(
|
||||
void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
|
||||
RefPtr<Runnable> task = aTask;
|
||||
}
|
||||
bool IsRepaintThread() {
|
||||
return NS_IsMainThread();
|
||||
}
|
||||
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
|
||||
NS_DispatchToMainThread(Move(aTask));
|
||||
}
|
||||
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
|
||||
MOCK_METHOD0(NotifyFlushComplete, void());
|
||||
};
|
||||
|
||||
class MockContentControllerDelayed : public MockContentController {
|
||||
public:
|
||||
MockContentControllerDelayed()
|
||||
: mTime(GetStartupTime())
|
||||
{
|
||||
}
|
||||
|
||||
const TimeStamp& Time() {
|
||||
return mTime;
|
||||
}
|
||||
|
||||
void AdvanceByMillis(int aMillis) {
|
||||
AdvanceBy(TimeDuration::FromMilliseconds(aMillis));
|
||||
}
|
||||
|
||||
void AdvanceBy(const TimeDuration& aIncrement) {
|
||||
TimeStamp target = mTime + aIncrement;
|
||||
while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) {
|
||||
RunNextDelayedTask();
|
||||
}
|
||||
mTime = target;
|
||||
}
|
||||
|
||||
void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
|
||||
RefPtr<Runnable> task = aTask;
|
||||
TimeStamp runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs);
|
||||
int insIndex = mTaskQueue.Length();
|
||||
while (insIndex > 0) {
|
||||
if (mTaskQueue[insIndex - 1].second <= runAtTime) {
|
||||
break;
|
||||
}
|
||||
insIndex--;
|
||||
}
|
||||
mTaskQueue.InsertElementAt(insIndex, std::make_pair(task, runAtTime));
|
||||
}
|
||||
|
||||
// Run all the tasks in the queue, returning the number of tasks
|
||||
// run. Note that if a task queues another task while running, that
|
||||
// new task will not be run. Therefore, there may be still be tasks
|
||||
// in the queue after this function is called. Only when the return
|
||||
// value is 0 is the queue guaranteed to be empty.
|
||||
int RunThroughDelayedTasks() {
|
||||
nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> runQueue;
|
||||
runQueue.SwapElements(mTaskQueue);
|
||||
int numTasks = runQueue.Length();
|
||||
for (int i = 0; i < numTasks; i++) {
|
||||
mTime = runQueue[i].second;
|
||||
runQueue[i].first->Run();
|
||||
|
||||
// Deleting the task is important in order to release the reference to
|
||||
// the callee object.
|
||||
runQueue[i].first = nullptr;
|
||||
}
|
||||
return numTasks;
|
||||
}
|
||||
|
||||
private:
|
||||
void RunNextDelayedTask() {
|
||||
std::pair<RefPtr<Runnable>, TimeStamp> next = mTaskQueue[0];
|
||||
mTaskQueue.RemoveElementAt(0);
|
||||
mTime = next.second;
|
||||
next.first->Run();
|
||||
// Deleting the task is important in order to release the reference to
|
||||
// the callee object.
|
||||
next.first = nullptr;
|
||||
}
|
||||
|
||||
// The following array is sorted by timestamp (tasks are inserted in order by
|
||||
// timestamp).
|
||||
nsTArray<std::pair<RefPtr<Runnable>, TimeStamp>> mTaskQueue;
|
||||
TimeStamp mTime;
|
||||
};
|
||||
|
||||
class TestAPZCTreeManager : public APZCTreeManager {
|
||||
public:
|
||||
explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc) : mcc(aMcc) {}
|
||||
|
||||
RefPtr<InputQueue> GetInputQueue() const {
|
||||
return mInputQueue;
|
||||
}
|
||||
|
||||
void ClearContentController() {
|
||||
mcc = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController) override;
|
||||
|
||||
TimeStamp GetFrameTime() override {
|
||||
return mcc->Time();
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MockContentControllerDelayed> mcc;
|
||||
};
|
||||
|
||||
class TestAsyncPanZoomController : public AsyncPanZoomController {
|
||||
public:
|
||||
TestAsyncPanZoomController(uint64_t aLayersId, MockContentControllerDelayed* aMcc,
|
||||
TestAPZCTreeManager* aTreeManager,
|
||||
GestureBehavior aBehavior = DEFAULT_GESTURES)
|
||||
: AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(),
|
||||
aMcc, aBehavior)
|
||||
, mWaitForMainThread(false)
|
||||
, mcc(aMcc)
|
||||
{}
|
||||
|
||||
nsEventStatus ReceiveInputEvent(const InputData& aEvent, ScrollableLayerGuid* aDummy, uint64_t* aOutInputBlockId) {
|
||||
// This is a function whose signature matches exactly the ReceiveInputEvent
|
||||
// on APZCTreeManager. This allows us to templates for functions like
|
||||
// TouchDown, TouchUp, etc so that we can reuse the code for dispatching
|
||||
// events into both APZC and APZCTM.
|
||||
return ReceiveInputEvent(aEvent, aOutInputBlockId);
|
||||
}
|
||||
|
||||
nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
|
||||
return GetInputQueue()->ReceiveInputEvent(this, !mWaitForMainThread, aEvent, aOutInputBlockId);
|
||||
}
|
||||
|
||||
void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
|
||||
GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
|
||||
}
|
||||
|
||||
void ConfirmTarget(uint64_t aInputBlockId) {
|
||||
RefPtr<AsyncPanZoomController> target = this;
|
||||
GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target);
|
||||
}
|
||||
|
||||
void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
|
||||
GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
|
||||
}
|
||||
|
||||
void SetFrameMetrics(const FrameMetrics& metrics) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
mFrameMetrics = metrics;
|
||||
}
|
||||
|
||||
FrameMetrics& GetFrameMetrics() {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
return mFrameMetrics;
|
||||
}
|
||||
|
||||
ScrollMetadata& GetScrollMetadata() {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
return mScrollMetadata;
|
||||
}
|
||||
|
||||
const FrameMetrics& GetFrameMetrics() const {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
return mFrameMetrics;
|
||||
}
|
||||
|
||||
using AsyncPanZoomController::GetVelocityVector;
|
||||
|
||||
void AssertStateIsReset() const {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
EXPECT_EQ(NOTHING, mState);
|
||||
}
|
||||
|
||||
void AssertStateIsFling() const {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
EXPECT_EQ(FLING, mState);
|
||||
}
|
||||
|
||||
void AdvanceAnimationsUntilEnd(const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
|
||||
while (AdvanceAnimations(mcc->Time())) {
|
||||
mcc->AdvanceBy(aIncrement);
|
||||
}
|
||||
}
|
||||
|
||||
bool SampleContentTransformForFrame(AsyncTransform* aOutTransform,
|
||||
ParentLayerPoint& aScrollOffset,
|
||||
const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
|
||||
mcc->AdvanceBy(aIncrement);
|
||||
bool ret = AdvanceAnimations(mcc->Time());
|
||||
if (aOutTransform) {
|
||||
*aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL);
|
||||
}
|
||||
aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SetWaitForMainThread() {
|
||||
mWaitForMainThread = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mWaitForMainThread;
|
||||
MockContentControllerDelayed* mcc;
|
||||
};
|
||||
|
||||
class APZCTesterBase : public ::testing::Test {
|
||||
public:
|
||||
APZCTesterBase() {
|
||||
mcc = new NiceMock<MockContentControllerDelayed>();
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeDuration aTapLength,
|
||||
nsEventStatus (*aOutEventStatuses)[2] = nullptr,
|
||||
uint64_t* aOutInputBlockId = nullptr);
|
||||
|
||||
template<class InputReceiver>
|
||||
void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint, TimeDuration aTapLength);
|
||||
|
||||
template<class InputReceiver>
|
||||
void Pan(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aTouchStart,
|
||||
const ScreenIntPoint& aTouchEnd,
|
||||
bool aKeepFingerDown = false,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
|
||||
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
|
||||
uint64_t* aOutInputBlockId = nullptr);
|
||||
|
||||
/*
|
||||
* A version of Pan() that only takes y coordinates rather than (x, y) points
|
||||
* for the touch start and end points, and uses 10 for the x coordinates.
|
||||
* This is for convenience, as most tests only need to pan in one direction.
|
||||
*/
|
||||
template<class InputReceiver>
|
||||
void Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
|
||||
int aTouchEndY, bool aKeepFingerDown = false,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
|
||||
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
|
||||
uint64_t* aOutInputBlockId = nullptr);
|
||||
|
||||
/*
|
||||
* Dispatches mock touch events to the apzc and checks whether apzc properly
|
||||
* consumed them and triggered scrolling behavior.
|
||||
*/
|
||||
template<class InputReceiver>
|
||||
void PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
|
||||
int aTouchEndY,
|
||||
bool aExpectConsumed,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors,
|
||||
uint64_t* aOutInputBlockId = nullptr);
|
||||
|
||||
void ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
|
||||
int aTouchStartY,
|
||||
int aTouchEndY,
|
||||
uint64_t* aOutInputBlockId = nullptr);
|
||||
|
||||
template<class InputReceiver>
|
||||
void DoubleTap(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint,
|
||||
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
|
||||
uint64_t (*aOutInputBlockIds)[2] = nullptr);
|
||||
|
||||
template<class InputReceiver>
|
||||
void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint,
|
||||
uint64_t (*aOutInputBlockIds)[2] = nullptr);
|
||||
|
||||
protected:
|
||||
RefPtr<MockContentControllerDelayed> mcc;
|
||||
};
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint, TimeDuration aTapLength,
|
||||
nsEventStatus (*aOutEventStatuses)[2],
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
// Even if the caller doesn't care about the block id, we need it to set the
|
||||
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
|
||||
uint64_t blockId;
|
||||
if (!aOutInputBlockId) {
|
||||
aOutInputBlockId = &blockId;
|
||||
}
|
||||
|
||||
nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), aOutInputBlockId);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[0] = status;
|
||||
}
|
||||
mcc->AdvanceBy(aTapLength);
|
||||
|
||||
// If touch-action is enabled then simulate the allowed touch behaviour
|
||||
// notification that the main thread is supposed to deliver.
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
|
||||
}
|
||||
|
||||
status = TouchUp(aTarget, aPoint, mcc->Time());
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[1] = status;
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint,
|
||||
TimeDuration aTapLength)
|
||||
{
|
||||
nsEventStatus statuses[2];
|
||||
Tap(aTarget, aPoint, aTapLength, &statuses);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aTouchStart,
|
||||
const ScreenIntPoint& aTouchEnd,
|
||||
bool aKeepFingerDown,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors,
|
||||
nsEventStatus (*aOutEventStatuses)[4],
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
// Reduce the touch start and move tolerance to a tiny value.
|
||||
// We can't use a scoped pref because this value might be read at some later
|
||||
// time when the events are actually processed, rather than when we deliver
|
||||
// them.
|
||||
gfxPrefs::SetAPZTouchStartTolerance(1.0f / 1000.0f);
|
||||
gfxPrefs::SetAPZTouchMoveTolerance(0.0f);
|
||||
const int OVERCOME_TOUCH_TOLERANCE = 1;
|
||||
|
||||
const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50);
|
||||
|
||||
// Even if the caller doesn't care about the block id, we need it to set the
|
||||
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
|
||||
uint64_t blockId;
|
||||
if (!aOutInputBlockId) {
|
||||
aOutInputBlockId = &blockId;
|
||||
}
|
||||
|
||||
// Make sure the move is large enough to not be handled as a tap
|
||||
nsEventStatus status = TouchDown(aTarget,
|
||||
ScreenIntPoint(aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE),
|
||||
mcc->Time(), aOutInputBlockId);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[0] = status;
|
||||
}
|
||||
|
||||
mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
|
||||
|
||||
// Allowed touch behaviours must be set after sending touch-start.
|
||||
if (status != nsEventStatus_eConsumeNoDefault) {
|
||||
if (aAllowedTouchBehaviors) {
|
||||
EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
|
||||
aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
|
||||
} else if (gfxPrefs::TouchActionEnabled()) {
|
||||
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
|
||||
}
|
||||
}
|
||||
|
||||
status = TouchMove(aTarget, aTouchStart, mcc->Time());
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[1] = status;
|
||||
}
|
||||
|
||||
mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
|
||||
|
||||
status = TouchMove(aTarget, aTouchEnd, mcc->Time());
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[2] = status;
|
||||
}
|
||||
|
||||
mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
|
||||
|
||||
if (!aKeepFingerDown) {
|
||||
status = TouchUp(aTarget, aTouchEnd, mcc->Time());
|
||||
} else {
|
||||
status = nsEventStatus_eIgnore;
|
||||
}
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[3] = status;
|
||||
}
|
||||
|
||||
// Don't increment the time here. Animations started on touch-up, such as
|
||||
// flings, are affected by elapsed time, and we want to be able to sample
|
||||
// them immediately after they start, without time having elapsed.
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
|
||||
int aTouchStartY, int aTouchEndY, bool aKeepFingerDown,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors,
|
||||
nsEventStatus (*aOutEventStatuses)[4],
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY),
|
||||
aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
int aTouchStartY,
|
||||
int aTouchEndY,
|
||||
bool aExpectConsumed,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors,
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
nsEventStatus statuses[4]; // down, move, move, up
|
||||
Pan(aTarget, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
|
||||
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
|
||||
|
||||
nsEventStatus touchMoveStatus;
|
||||
if (aExpectConsumed) {
|
||||
touchMoveStatus = nsEventStatus_eConsumeDoDefault;
|
||||
} else {
|
||||
touchMoveStatus = nsEventStatus_eIgnore;
|
||||
}
|
||||
EXPECT_EQ(touchMoveStatus, statuses[1]);
|
||||
EXPECT_EQ(touchMoveStatus, statuses[2]);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTesterBase::ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
|
||||
int aTouchStartY, int aTouchEndY,
|
||||
uint64_t* aOutInputBlockId)
|
||||
{
|
||||
Pan(aApzc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
|
||||
aApzc->CancelAnimation();
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::DoubleTap(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint,
|
||||
nsEventStatus (*aOutEventStatuses)[4],
|
||||
uint64_t (*aOutInputBlockIds)[2])
|
||||
{
|
||||
uint64_t blockId;
|
||||
nsEventStatus status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[0] = status;
|
||||
}
|
||||
if (aOutInputBlockIds) {
|
||||
(*aOutInputBlockIds)[0] = blockId;
|
||||
}
|
||||
mcc->AdvanceByMillis(10);
|
||||
|
||||
// If touch-action is enabled then simulate the allowed touch behaviour
|
||||
// notification that the main thread is supposed to deliver.
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
SetDefaultAllowedTouchBehavior(aTarget, blockId);
|
||||
}
|
||||
|
||||
status = TouchUp(aTarget, aPoint, mcc->Time());
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[1] = status;
|
||||
}
|
||||
mcc->AdvanceByMillis(10);
|
||||
status = TouchDown(aTarget, aPoint, mcc->Time(), &blockId);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[2] = status;
|
||||
}
|
||||
if (aOutInputBlockIds) {
|
||||
(*aOutInputBlockIds)[1] = blockId;
|
||||
}
|
||||
mcc->AdvanceByMillis(10);
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
SetDefaultAllowedTouchBehavior(aTarget, blockId);
|
||||
}
|
||||
|
||||
status = TouchUp(aTarget, aPoint, mcc->Time());
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[3] = status;
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
APZCTesterBase::DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aPoint,
|
||||
uint64_t (*aOutInputBlockIds)[2])
|
||||
{
|
||||
nsEventStatus statuses[4];
|
||||
DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
|
||||
}
|
||||
|
||||
AsyncPanZoomController*
|
||||
TestAPZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
|
||||
GeckoContentController* aController)
|
||||
{
|
||||
MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
|
||||
return new TestAsyncPanZoomController(aLayersId, mcc, this,
|
||||
AsyncPanZoomController::USE_GESTURE_DETECTOR);
|
||||
}
|
||||
|
||||
FrameMetrics
|
||||
TestFrameMetrics()
|
||||
{
|
||||
FrameMetrics fm;
|
||||
|
||||
fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
|
||||
fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
|
||||
fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
|
||||
fm.SetScrollableRect(CSSRect(0, 0, 100, 100));
|
||||
|
||||
return fm;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
MillisecondsSinceStartup(TimeStamp aTime)
|
||||
{
|
||||
return (aTime - GetStartupTime()).ToMilliseconds();
|
||||
}
|
||||
|
||||
#endif // mozilla_layers_APZTestCommon_h
|
||||
297
gfx/layers/apz/test/gtest/InputUtils.h
Normal file
297
gfx/layers/apz/test/gtest/InputUtils.h
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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_InputUtils_h
|
||||
#define mozilla_layers_InputUtils_h
|
||||
|
||||
/**
|
||||
* Defines a set of utility functions for generating input events
|
||||
* to an APZC/APZCTM during APZ gtests.
|
||||
*/
|
||||
|
||||
#include "APZTestCommon.h"
|
||||
|
||||
/* The InputReceiver template parameter used in the helper functions below needs
|
||||
* to be a class that implements functions with the signatures:
|
||||
* nsEventStatus ReceiveInputEvent(const InputData& aEvent,
|
||||
* ScrollableLayerGuid* aGuid,
|
||||
* uint64_t* aOutInputBlockId);
|
||||
* void SetAllowedTouchBehavior(uint64_t aInputBlockId,
|
||||
* const nsTArray<uint32_t>& aBehaviours);
|
||||
* The classes that currently implement these are APZCTreeManager and
|
||||
* TestAsyncPanZoomController. Using this template allows us to test individual
|
||||
* APZC instances in isolation and also an entire APZ tree, while using the same
|
||||
* code to dispatch input events.
|
||||
*/
|
||||
|
||||
// Some helper functions for constructing input event objects suitable to be
|
||||
// passed either to an APZC (which expects an transformed point), or to an APZTM
|
||||
// (which expects an untransformed point). We handle both cases by setting both
|
||||
// the transformed and untransformed fields to the same value.
|
||||
SingleTouchData
|
||||
CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
|
||||
{
|
||||
SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
|
||||
touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
|
||||
return touch;
|
||||
}
|
||||
|
||||
// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
|
||||
SingleTouchData
|
||||
CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
|
||||
{
|
||||
return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
|
||||
}
|
||||
|
||||
PinchGestureInput
|
||||
CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
|
||||
const ScreenIntPoint& aFocus,
|
||||
float aCurrentSpan, float aPreviousSpan)
|
||||
{
|
||||
ParentLayerPoint localFocus(aFocus.x, aFocus.y);
|
||||
PinchGestureInput result(aType, 0, TimeStamp(), localFocus,
|
||||
aCurrentSpan, aPreviousSpan, 0);
|
||||
result.mFocusPoint = aFocus;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
|
||||
uint64_t aInputBlockId,
|
||||
int touchPoints = 1)
|
||||
{
|
||||
nsTArray<uint32_t> defaultBehaviors;
|
||||
// use the default value where everything is allowed
|
||||
for (int i = 0; i < touchPoints; i++) {
|
||||
defaultBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM
|
||||
| mozilla::layers::AllowedTouchBehavior::DOUBLE_TAP_ZOOM);
|
||||
}
|
||||
aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
|
||||
}
|
||||
|
||||
|
||||
MultiTouchInput
|
||||
CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType, TimeStamp aTime)
|
||||
{
|
||||
return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
TouchDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
|
||||
return aTarget->ReceiveInputEvent(mti, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
TouchMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime)
|
||||
{
|
||||
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
|
||||
return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
TouchUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime)
|
||||
{
|
||||
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
|
||||
return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aFocus,
|
||||
const ScreenIntPoint& aSecondFocus, float aScale,
|
||||
nsEventStatus (*aOutEventStatuses)[3] = nullptr)
|
||||
{
|
||||
nsEventStatus actualStatus = aTarget->ReceiveInputEvent(
|
||||
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
|
||||
aFocus, 10.0, 10.0),
|
||||
nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[0] = actualStatus;
|
||||
}
|
||||
actualStatus = aTarget->ReceiveInputEvent(
|
||||
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
aSecondFocus, 10.0 * aScale, 10.0),
|
||||
nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[1] = actualStatus;
|
||||
}
|
||||
actualStatus = aTarget->ReceiveInputEvent(
|
||||
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
|
||||
// note: negative values here tell APZC
|
||||
// not to turn the pinch into a pan
|
||||
aFocus, -1.0, -1.0),
|
||||
nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[2] = actualStatus;
|
||||
}
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aFocus, float aScale,
|
||||
bool aShouldTriggerPinch)
|
||||
{
|
||||
nsEventStatus statuses[3]; // scalebegin, scale, scaleend
|
||||
PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses);
|
||||
|
||||
nsEventStatus expectedStatus = aShouldTriggerPinch
|
||||
? nsEventStatus_eConsumeNoDefault
|
||||
: nsEventStatus_eIgnore;
|
||||
EXPECT_EQ(expectedStatus, statuses[0]);
|
||||
EXPECT_EQ(expectedStatus, statuses[1]);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aFocus, float aScale,
|
||||
int& inputId,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
|
||||
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
|
||||
uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
// Having pinch coordinates in float type may cause problems with high-precision scale values
|
||||
// since SingleTouchData accepts integer value. But for trivial tests it should be ok.
|
||||
float pinchLength = 100.0;
|
||||
float pinchLengthScaled = pinchLength * aScale;
|
||||
|
||||
// Even if the caller doesn't care about the block id, we need it to set the
|
||||
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
|
||||
uint64_t blockId;
|
||||
if (!aOutInputBlockId) {
|
||||
aOutInputBlockId = &blockId;
|
||||
}
|
||||
|
||||
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
|
||||
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
|
||||
nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[0] = status;
|
||||
}
|
||||
|
||||
if (aAllowedTouchBehaviors) {
|
||||
EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
|
||||
aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
|
||||
} else if (gfxPrefs::TouchActionEnabled()) {
|
||||
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
|
||||
}
|
||||
|
||||
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
|
||||
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
|
||||
status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[1] = status;
|
||||
}
|
||||
|
||||
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
|
||||
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
|
||||
status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[2] = status;
|
||||
}
|
||||
|
||||
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
|
||||
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
|
||||
status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
|
||||
if (aOutEventStatuses) {
|
||||
(*aOutEventStatuses)[3] = status;
|
||||
}
|
||||
|
||||
inputId += 2;
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
void
|
||||
PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
|
||||
const ScreenIntPoint& aFocus, float aScale,
|
||||
int& inputId, bool aShouldTriggerPinch,
|
||||
nsTArray<uint32_t>* aAllowedTouchBehaviors)
|
||||
{
|
||||
nsEventStatus statuses[4]; // down, move, move, up
|
||||
PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
|
||||
|
||||
nsEventStatus expectedMoveStatus = aShouldTriggerPinch
|
||||
? nsEventStatus_eConsumeDoDefault
|
||||
: nsEventStatus_eIgnore;
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
|
||||
EXPECT_EQ(expectedMoveStatus, statuses[1]);
|
||||
EXPECT_EQ(expectedMoveStatus, statuses[2]);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
Wheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
|
||||
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
|
||||
aPoint, aDelta.x, aDelta.y, false);
|
||||
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
SmoothWheel(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
const ScreenPoint& aDelta, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
ScrollWheelInput input(MillisecondsSinceStartup(aTime), aTime, 0,
|
||||
ScrollWheelInput::SCROLLMODE_SMOOTH, ScrollWheelInput::SCROLLDELTA_LINE,
|
||||
aPoint, aDelta.x, aDelta.y, false);
|
||||
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
MouseDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
MouseInput input(MouseInput::MOUSE_DOWN, MouseInput::ButtonType::LEFT_BUTTON,
|
||||
0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
|
||||
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
MouseMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
MouseInput input(MouseInput::MOUSE_MOVE, MouseInput::ButtonType::LEFT_BUTTON,
|
||||
0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
|
||||
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
template<class InputReceiver>
|
||||
nsEventStatus
|
||||
MouseUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
||||
TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
|
||||
{
|
||||
MouseInput input(MouseInput::MOUSE_UP, MouseInput::ButtonType::LEFT_BUTTON,
|
||||
0, 0, aPoint, MillisecondsSinceStartup(aTime), aTime, 0);
|
||||
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
|
||||
}
|
||||
|
||||
|
||||
#endif // mozilla_layers_InputUtils_h
|
||||
356
gfx/layers/apz/test/gtest/TestBasic.cpp
Normal file
356
gfx/layers/apz/test/gtest/TestBasic.cpp
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCBasicTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
TEST_F(APZCBasicTester, Overzoom) {
|
||||
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
|
||||
FrameMetrics fm;
|
||||
fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
|
||||
fm.SetScrollableRect(CSSRect(0, 0, 125, 150));
|
||||
fm.SetScrollOffset(CSSPoint(10, 0));
|
||||
fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0));
|
||||
fm.SetIsRootContent(true);
|
||||
apzc->SetFrameMetrics(fm);
|
||||
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
|
||||
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true);
|
||||
|
||||
fm = apzc->GetFrameMetrics();
|
||||
EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
|
||||
// bug 936721 - PGO builds introduce rounding error so
|
||||
// use a fuzzy match instead
|
||||
EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
|
||||
EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, SimpleTransform) {
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
|
||||
EXPECT_EQ(ParentLayerPoint(), pointOut);
|
||||
EXPECT_EQ(AsyncTransform(), viewTransformOut);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(APZCBasicTester, ComplexTransform) {
|
||||
// This test assumes there is a page that gets rendered to
|
||||
// two layers. In CSS pixels, the first layer is 50x50 and
|
||||
// the second layer is 25x50. The widget scale factor is 3.0
|
||||
// and the presShell resolution is 2.0. Therefore, these layers
|
||||
// end up being 300x300 and 150x300 in layer pixels.
|
||||
//
|
||||
// The second (child) layer has an additional CSS transform that
|
||||
// stretches it by 2.0 on the x-axis. Therefore, after applying
|
||||
// CSS transforms, the two layers are the same size in screen
|
||||
// pixels.
|
||||
//
|
||||
// The screen itself is 24x24 in screen pixels (therefore 4x4 in
|
||||
// CSS pixels). The displayport is 1 extra CSS pixel on all
|
||||
// sides.
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> childApzc =
|
||||
new TestAsyncPanZoomController(0, mcc, tm);
|
||||
|
||||
const char* layerTreeSyntax = "c(c)";
|
||||
// LayerID 0 1
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 300, 300)),
|
||||
nsIntRegion(IntRect(0, 0, 150, 300)),
|
||||
};
|
||||
Matrix4x4 transforms[] = {
|
||||
Matrix4x4(),
|
||||
Matrix4x4(),
|
||||
};
|
||||
transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
|
||||
transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
|
||||
|
||||
nsTArray<RefPtr<Layer> > layers;
|
||||
RefPtr<LayerManager> lm;
|
||||
RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
|
||||
|
||||
ScrollMetadata metadata;
|
||||
FrameMetrics& metrics = metadata.GetMetrics();
|
||||
metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24));
|
||||
metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6));
|
||||
metrics.SetScrollOffset(CSSPoint(10, 10));
|
||||
metrics.SetScrollableRect(CSSRect(0, 0, 50, 50));
|
||||
metrics.SetCumulativeResolution(LayoutDeviceToLayerScale2D(2, 2));
|
||||
metrics.SetPresShellResolution(2.0f);
|
||||
metrics.SetZoom(CSSToParentLayerScale2D(6, 6));
|
||||
metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
|
||||
metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
|
||||
|
||||
ScrollMetadata childMetadata = metadata;
|
||||
FrameMetrics& childMetrics = childMetadata.GetMetrics();
|
||||
childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
|
||||
|
||||
layers[0]->SetScrollMetadata(metadata);
|
||||
layers[1]->SetScrollMetadata(childMetadata);
|
||||
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
|
||||
// Both the parent and child layer should behave exactly the same here, because
|
||||
// the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
|
||||
|
||||
// initial transform
|
||||
apzc->SetFrameMetrics(metrics);
|
||||
apzc->NotifyLayersUpdated(metadata, true, true);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
|
||||
|
||||
childApzc->SetFrameMetrics(childMetrics);
|
||||
childApzc->NotifyLayersUpdated(childMetadata, true, true);
|
||||
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
|
||||
|
||||
// do an async scroll by 5 pixels and check the transform
|
||||
metrics.ScrollBy(CSSPoint(5, 0));
|
||||
apzc->SetFrameMetrics(metrics);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
|
||||
|
||||
childMetrics.ScrollBy(CSSPoint(5, 0));
|
||||
childApzc->SetFrameMetrics(childMetrics);
|
||||
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
|
||||
|
||||
// do an async zoom of 1.5x and check the transform
|
||||
metrics.ZoomBy(1.5f);
|
||||
apzc->SetFrameMetrics(metrics);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
|
||||
|
||||
childMetrics.ZoomBy(1.5f);
|
||||
childApzc->SetFrameMetrics(childMetrics);
|
||||
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
|
||||
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
|
||||
|
||||
childApzc->Destroy();
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, Fling) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
|
||||
// Fling down. Each step scroll further down
|
||||
Pan(apzc, touchStart, touchEnd);
|
||||
ParentLayerPoint lastPoint;
|
||||
for (int i = 1; i < 50; i+=1) {
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(1));
|
||||
EXPECT_GT(pointOut.y, lastPoint.y);
|
||||
lastPoint = pointOut;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, FlingIntoOverscroll) {
|
||||
// Enable overscrolling.
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
|
||||
// Scroll down by 25 px. Don't fling for simplicity.
|
||||
ApzcPanNoFling(apzc, 50, 25);
|
||||
|
||||
// Now scroll back up by 20px, this time flinging after.
|
||||
// The fling should cover the remaining 5 px of room to scroll, then
|
||||
// go into overscroll, and finally snap-back to recover from overscroll.
|
||||
Pan(apzc, 25, 45);
|
||||
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
|
||||
bool reachedOverscroll = false;
|
||||
bool recoveredFromOverscroll = false;
|
||||
while (apzc->AdvanceAnimations(mcc->Time())) {
|
||||
if (!reachedOverscroll && apzc->IsOverscrolled()) {
|
||||
reachedOverscroll = true;
|
||||
}
|
||||
if (reachedOverscroll && !apzc->IsOverscrolled()) {
|
||||
recoveredFromOverscroll = true;
|
||||
}
|
||||
mcc->AdvanceBy(increment);
|
||||
}
|
||||
EXPECT_TRUE(reachedOverscroll);
|
||||
EXPECT_TRUE(recoveredFromOverscroll);
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, PanningTransformNotifications) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
// Scroll down by 25 px. Ensure we only get one set of
|
||||
// state change notifications.
|
||||
//
|
||||
// Then, scroll back up by 20px, this time flinging after.
|
||||
// The fling should cover the remaining 5 px of room to scroll, then
|
||||
// go into overscroll, and finally snap-back to recover from overscroll.
|
||||
// Again, ensure we only get one set of state change notifications for
|
||||
// this entire procedure.
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(check, Call("Simple pan"));
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
|
||||
EXPECT_CALL(check, Call("Complex pan"));
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartTouch,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformBegin,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eStartPanning,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eEndTouch,_)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::eTransformEnd,_)).Times(1);
|
||||
EXPECT_CALL(check, Call("Done"));
|
||||
}
|
||||
|
||||
check.Call("Simple pan");
|
||||
ApzcPanNoFling(apzc, 50, 25);
|
||||
check.Call("Complex pan");
|
||||
Pan(apzc, 25, 45);
|
||||
apzc->AdvanceAnimationsUntilEnd();
|
||||
check.Call("Done");
|
||||
}
|
||||
|
||||
void APZCBasicTester::PanIntoOverscroll()
|
||||
{
|
||||
int touchStart = 500;
|
||||
int touchEnd = 10;
|
||||
Pan(apzc, touchStart, touchEnd);
|
||||
EXPECT_TRUE(apzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
void APZCBasicTester::TestOverscroll()
|
||||
{
|
||||
// Pan sufficiently to hit overscroll behavior
|
||||
PanIntoOverscroll();
|
||||
|
||||
// Check that we recover from overscroll via an animation.
|
||||
ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
|
||||
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(APZCBasicTester, OverScrollPanning) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
TestOverscroll();
|
||||
}
|
||||
|
||||
// Tests that an overscroll animation doesn't trigger an assertion failure
|
||||
// in the case where a sample has a velocity of zero.
|
||||
TEST_F(APZCBasicTester, OverScroll_Bug1152051a) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
// Doctor the prefs to make the velocity zero at the end of the first sample.
|
||||
|
||||
// This ensures our incoming velocity to the overscroll animation is
|
||||
// a round(ish) number, 4.9 (that being the distance of the pan before
|
||||
// overscroll, which is 500 - 10 = 490 pixels, divided by the duration of
|
||||
// the pan, which is 100 ms).
|
||||
SCOPED_GFX_PREF(APZFlingFriction, float, 0);
|
||||
|
||||
// To ensure the velocity after the first sample is 0, set the spring
|
||||
// stiffness to the incoming velocity (4.9) divided by the overscroll
|
||||
// (400 pixels) times the step duration (1 ms).
|
||||
SCOPED_GFX_PREF(APZOverscrollSpringStiffness, float, 0.01225f);
|
||||
|
||||
TestOverscroll();
|
||||
}
|
||||
|
||||
// Tests that ending an overscroll animation doesn't leave around state that
|
||||
// confuses the next overscroll animation.
|
||||
TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
SCOPED_GFX_PREF(APZOverscrollStopDistanceThreshold, float, 0.1f);
|
||||
|
||||
// Pan sufficiently to hit overscroll behavior
|
||||
PanIntoOverscroll();
|
||||
|
||||
// Sample animations once, to give the fling animation started on touch-up
|
||||
// a chance to realize it's overscrolled, and schedule a call to
|
||||
// HandleFlingOverscroll().
|
||||
SampleAnimationOnce();
|
||||
|
||||
// This advances the time and runs the HandleFlingOverscroll task scheduled in
|
||||
// the previous call, which starts an overscroll animation. It then samples
|
||||
// the overscroll animation once, to get it to initialize the first overscroll
|
||||
// sample.
|
||||
SampleAnimationOnce();
|
||||
|
||||
// Do a touch-down to cancel the overscroll animation, and then a touch-up
|
||||
// to schedule a new one since we're still overscrolled. We don't pan because
|
||||
// panning can trigger functions that clear the overscroll animation state
|
||||
// in other ways.
|
||||
uint64_t blockId;
|
||||
nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
SetDefaultAllowedTouchBehavior(apzc, blockId);
|
||||
}
|
||||
TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
|
||||
|
||||
// Sample the second overscroll animation to its end.
|
||||
// If the ending of the first overscroll animation fails to clear state
|
||||
// properly, this will assert.
|
||||
ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
|
||||
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, OverScrollAbort) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
// Pan sufficiently to hit overscroll behavior
|
||||
int touchStart = 500;
|
||||
int touchEnd = 10;
|
||||
Pan(apzc, touchStart, touchEnd);
|
||||
EXPECT_TRUE(apzc->IsOverscrolled());
|
||||
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
|
||||
// This sample call will run to the end of the fling animation
|
||||
// and will schedule the overscroll animation.
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(10000));
|
||||
EXPECT_TRUE(apzc->IsOverscrolled());
|
||||
|
||||
// At this point, we have an active overscroll animation.
|
||||
// Check that cancelling the animation clears the overscroll.
|
||||
apzc->CancelAnimation();
|
||||
EXPECT_FALSE(apzc->IsOverscrolled());
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, OverScrollPanningAbort) {
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
// Pan sufficiently to hit overscroll behaviour. Keep the finger down so
|
||||
// the pan does not end.
|
||||
int touchStart = 500;
|
||||
int touchEnd = 10;
|
||||
Pan(apzc, touchStart, touchEnd, true); // keep finger down
|
||||
EXPECT_TRUE(apzc->IsOverscrolled());
|
||||
|
||||
// Check that calling CancelAnimation() while the user is still panning
|
||||
// (and thus no fling or snap-back animation has had a chance to start)
|
||||
// clears the overscroll.
|
||||
apzc->CancelAnimation();
|
||||
EXPECT_FALSE(apzc->IsOverscrolled());
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
272
gfx/layers/apz/test/gtest/TestEventRegions.cpp
Normal file
272
gfx/layers/apz/test/gtest/TestEventRegions.cpp
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZEventRegionsTester : public APZCTreeManagerTester {
|
||||
protected:
|
||||
UniquePtr<ScopedLayerTreeRegistration> registration;
|
||||
TestAsyncPanZoomController* rootApzc;
|
||||
|
||||
void CreateEventRegionsLayerTree1() {
|
||||
const char* layerTreeSyntax = "c(tt)";
|
||||
nsIntRegion layerVisibleRegions[] = {
|
||||
nsIntRegion(IntRect(0, 0, 200, 200)), // root
|
||||
nsIntRegion(IntRect(0, 0, 100, 200)), // left half
|
||||
nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2);
|
||||
SetScrollHandoff(layers[1], root);
|
||||
SetScrollHandoff(layers[2], root);
|
||||
|
||||
// Set up the event regions over a 200x200 area. The root layer has the
|
||||
// whole 200x200 as the hit region; layers[1] has the left half and
|
||||
// layers[2] has the bottom half. The bottom-left 100x100 area is also
|
||||
// in the d-t-c region for both layers[1] and layers[2] (but layers[2] is
|
||||
// on top so it gets the events by default if the main thread doesn't
|
||||
// respond).
|
||||
EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
|
||||
root->SetEventRegions(regions);
|
||||
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100));
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
|
||||
layers[1]->SetEventRegions(regions);
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
|
||||
layers[2]->SetEventRegions(regions);
|
||||
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
}
|
||||
|
||||
void CreateEventRegionsLayerTree2() {
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
nsIntRegion layerVisibleRegions[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 500)),
|
||||
nsIntRegion(IntRect(0, 150, 100, 100)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
|
||||
|
||||
// Set up the event regions so that the child thebes layer is positioned far
|
||||
// away from the scrolling container layer.
|
||||
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
|
||||
root->SetEventRegions(regions);
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
|
||||
layers[1]->SetEventRegions(regions);
|
||||
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
}
|
||||
|
||||
void CreateObscuringLayerTree() {
|
||||
const char* layerTreeSyntax = "c(c(t)t)";
|
||||
// LayerID 0 1 2 3
|
||||
// 0 is the root.
|
||||
// 1 is a parent scrollable layer.
|
||||
// 2 is a child scrollable layer.
|
||||
// 3 is the Obscurer, who ruins everything.
|
||||
nsIntRegion layerVisibleRegions[] = {
|
||||
// x coordinates are uninteresting
|
||||
nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
|
||||
nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
|
||||
nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150]
|
||||
nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200]
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
|
||||
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300));
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100));
|
||||
SetScrollHandoff(layers[2], layers[1]);
|
||||
SetScrollHandoff(layers[1], root);
|
||||
|
||||
EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
|
||||
root->SetEventRegions(regions);
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
|
||||
layers[1]->SetEventRegions(regions);
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
|
||||
layers[2]->SetEventRegions(regions);
|
||||
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
}
|
||||
|
||||
void CreateBug1119497LayerTree() {
|
||||
const char* layerTreeSyntax = "c(tt)";
|
||||
// LayerID 0 12
|
||||
// 0 is the root and has an APZC
|
||||
// 1 is behind 2 and has an APZC
|
||||
// 2 entirely covers 1 and should take all the input events, but has no APZC
|
||||
// so hits to 2 should go to to the root APZC
|
||||
nsIntRegion layerVisibleRegions[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
|
||||
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
|
||||
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
}
|
||||
|
||||
void CreateBug1117712LayerTree() {
|
||||
const char* layerTreeSyntax = "c(c(t)t)";
|
||||
// LayerID 0 1 2 3
|
||||
// 0 is the root
|
||||
// 1 is a container layer whose sole purpose to make a non-empty ancestor
|
||||
// transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
|
||||
// transforms are different from 3's.
|
||||
// 2 is a small layer that is the actual target
|
||||
// 3 is a big layer obscuring 2 with a dispatch-to-content region
|
||||
nsIntRegion layerVisibleRegions[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 0, 0, 0)),
|
||||
nsIntRegion(IntRect(0, 0, 10, 10)),
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
};
|
||||
Matrix4x4 layerTransforms[] = {
|
||||
Matrix4x4(),
|
||||
Matrix4x4::Translation(50, 0, 0),
|
||||
Matrix4x4(),
|
||||
Matrix4x4(),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers);
|
||||
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 10, 10));
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollHandoff(layers[3], layers[2]);
|
||||
|
||||
EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
|
||||
layers[2]->SetEventRegions(regions);
|
||||
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
|
||||
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
|
||||
layers[3]->SetEventRegions(regions);
|
||||
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) {
|
||||
CreateEventRegionsLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* root = ApzcOf(layers[0]);
|
||||
TestAsyncPanZoomController* left = ApzcOf(layers[1]);
|
||||
TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped on left"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped on bottom"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped on root"));
|
||||
EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped on bottom again"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped on left this time"));
|
||||
}
|
||||
|
||||
TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
|
||||
|
||||
// Tap in the exposed hit regions of each of the layers once and ensure
|
||||
// the clicks are dispatched right away
|
||||
Tap(manager, ScreenIntPoint(10, 10), tapDuration);
|
||||
mcc->RunThroughDelayedTasks(); // this runs the tap event
|
||||
check.Call("Tapped on left");
|
||||
Tap(manager, ScreenIntPoint(110, 110), tapDuration);
|
||||
mcc->RunThroughDelayedTasks(); // this runs the tap event
|
||||
check.Call("Tapped on bottom");
|
||||
Tap(manager, ScreenIntPoint(110, 10), tapDuration);
|
||||
mcc->RunThroughDelayedTasks(); // this runs the tap event
|
||||
check.Call("Tapped on root");
|
||||
|
||||
// Now tap on the dispatch-to-content region where the layers overlap
|
||||
Tap(manager, ScreenIntPoint(10, 110), tapDuration);
|
||||
mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout
|
||||
check.Call("Tap pending on d-t-c region");
|
||||
mcc->RunThroughDelayedTasks(); // this runs the tap event
|
||||
check.Call("Tapped on bottom again");
|
||||
|
||||
// Now let's do that again, but simulate a main-thread response
|
||||
uint64_t inputBlockId = 0;
|
||||
Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
|
||||
nsTArray<ScrollableLayerGuid> targets;
|
||||
targets.AppendElement(left->GetGuid());
|
||||
manager->SetTargetAPZC(inputBlockId, targets);
|
||||
while (mcc->RunThroughDelayedTasks()); // this runs the tap event
|
||||
check.Call("Tapped on left this time");
|
||||
}
|
||||
|
||||
TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
|
||||
CreateEventRegionsLayerTree2();
|
||||
|
||||
// Tap in the area of the child layer that's not directly included in the
|
||||
// parent layer's hit region. Verify that it comes out of the APZC's
|
||||
// content controller, which indicates the input events got routed correctly
|
||||
// to the APZC.
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1);
|
||||
Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
|
||||
}
|
||||
|
||||
TEST_F(APZEventRegionsTester, Obscuration) {
|
||||
CreateObscuringLayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
TestAsyncPanZoomController* parent = ApzcOf(layers[1]);
|
||||
TestAsyncPanZoomController* child = ApzcOf(layers[2]);
|
||||
|
||||
ApzcPanNoFling(parent, 75, 25);
|
||||
|
||||
HitTestResult result;
|
||||
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result);
|
||||
EXPECT_EQ(child, hit.get());
|
||||
EXPECT_EQ(HitTestResult::HitLayer, result);
|
||||
}
|
||||
|
||||
TEST_F(APZEventRegionsTester, Bug1119497) {
|
||||
CreateBug1119497LayerTree();
|
||||
|
||||
HitTestResult result;
|
||||
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result);
|
||||
// We should hit layers[2], so |result| will be HitLayer but there's no
|
||||
// actual APZC on layers[2], so it will be the APZC of the root layer.
|
||||
EXPECT_EQ(ApzcOf(layers[0]), hit.get());
|
||||
EXPECT_EQ(HitTestResult::HitLayer, result);
|
||||
}
|
||||
|
||||
TEST_F(APZEventRegionsTester, Bug1117712) {
|
||||
CreateBug1117712LayerTree();
|
||||
|
||||
TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
|
||||
|
||||
// These touch events should hit the dispatch-to-content region of layers[3]
|
||||
// and so get queued with that APZC as the tentative target.
|
||||
uint64_t inputBlockId = 0;
|
||||
Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
|
||||
// But now we tell the APZ that really it hit layers[2], and expect the tap
|
||||
// to be delivered at the correct coordinates.
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
|
||||
|
||||
nsTArray<ScrollableLayerGuid> targets;
|
||||
targets.AppendElement(apzc2->GetGuid());
|
||||
manager->SetTargetAPZC(inputBlockId, targets);
|
||||
}
|
||||
638
gfx/layers/apz/test/gtest/TestGestureDetector.cpp
Normal file
638
gfx/layers/apz/test/gtest/TestGestureDetector.cpp
Normal file
|
|
@ -0,0 +1,638 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCBasicTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
|
||||
class APZCGestureDetectorTester : public APZCBasicTester {
|
||||
public:
|
||||
APZCGestureDetectorTester()
|
||||
: APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
FrameMetrics GetPinchableFrameMetrics()
|
||||
{
|
||||
FrameMetrics fm;
|
||||
fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
|
||||
fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
|
||||
fm.SetScrollOffset(CSSPoint(300, 300));
|
||||
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
|
||||
// APZC only allows zooming on the root scrollable frame.
|
||||
fm.SetIsRootContent(true);
|
||||
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
|
||||
return fm;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, Pan_After_Pinch) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
|
||||
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
|
||||
apzc->SetFrameMetrics(originalMetrics);
|
||||
|
||||
MakeApzcZoomable();
|
||||
|
||||
// Test parameters
|
||||
float zoomAmount = 1.25;
|
||||
float pinchLength = 100.0;
|
||||
float pinchLengthScaled = pinchLength * zoomAmount;
|
||||
int focusX = 250;
|
||||
int focusY = 300;
|
||||
int panDistance = 20;
|
||||
|
||||
int firstFingerId = 0;
|
||||
int secondFingerId = firstFingerId + 1;
|
||||
|
||||
// Put fingers down
|
||||
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX, focusY));
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Spread fingers out to enter the pinch state
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLength, focusY));
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLength, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Do the actual pinch of 1.25x
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Verify that the zoom changed, just to make sure our code above did what it
|
||||
// was supposed to.
|
||||
FrameMetrics zoomedMetrics = apzc->GetFrameMetrics();
|
||||
float newZoom = zoomedMetrics.GetZoom().ToScaleFactor().scale;
|
||||
EXPECT_EQ(originalMetrics.GetZoom().ToScaleFactor().scale * zoomAmount, newZoom);
|
||||
|
||||
// Now we lift one finger...
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// ... and pan with the remaining finger. This pan just breaks through the
|
||||
// distance threshold.
|
||||
focusY += 40;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// This one does an actual pan of 20 pixels
|
||||
focusY += panDistance;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Lift the remaining finger
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Verify that we scrolled
|
||||
FrameMetrics finalMetrics = apzc->GetFrameMetrics();
|
||||
EXPECT_EQ(zoomedMetrics.GetScrollOffset().y - (panDistance / newZoom), finalMetrics.GetScrollOffset().y);
|
||||
|
||||
// Clear out any remaining fling animation and pending tasks
|
||||
apzc->AdvanceAnimationsUntilEnd();
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, Pan_With_Tap) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
|
||||
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
|
||||
apzc->SetFrameMetrics(originalMetrics);
|
||||
|
||||
// Making the APZC zoomable isn't really needed for the correct operation of
|
||||
// this test, but it could help catch regressions where we accidentally enter
|
||||
// a pinch state.
|
||||
MakeApzcZoomable();
|
||||
|
||||
// Test parameters
|
||||
int touchX = 250;
|
||||
int touchY = 300;
|
||||
int panDistance = 20;
|
||||
|
||||
int firstFingerId = 0;
|
||||
int secondFingerId = firstFingerId + 1;
|
||||
|
||||
// Put finger down
|
||||
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Start a pan, break through the threshold
|
||||
touchY += 40;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Do an actual pan for a bit
|
||||
touchY += panDistance;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Put a second finger down
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Lift the second finger
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Bust through the threshold again
|
||||
touchY += 40;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Do some more actual panning
|
||||
touchY += panDistance;
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Lift the first finger
|
||||
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
// Verify that we scrolled
|
||||
FrameMetrics finalMetrics = apzc->GetFrameMetrics();
|
||||
float zoom = finalMetrics.GetZoom().ToScaleFactor().scale;
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().y - (panDistance * 2 / zoom), finalMetrics.GetScrollOffset().y);
|
||||
|
||||
// Clear out any remaining fling animation and pending tasks
|
||||
apzc->AdvanceAnimationsUntilEnd();
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
class APZCFlingStopTester : public APZCGestureDetectorTester {
|
||||
protected:
|
||||
// Start a fling, and then tap while the fling is ongoing. When
|
||||
// aSlow is false, the tap will happen while the fling is at a
|
||||
// high velocity, and we check that the tap doesn't trigger sending a tap
|
||||
// to content. If aSlow is true, the tap will happen while the fling
|
||||
// is at a slow velocity, and we check that the tap does trigger sending
|
||||
// a tap to content. See bug 1022956.
|
||||
void DoFlingStopTest(bool aSlow) {
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
|
||||
// Start the fling down.
|
||||
Pan(apzc, touchStart, touchEnd);
|
||||
// The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
|
||||
|
||||
// If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
|
||||
// the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
|
||||
// friction and threshold values change, but they should be deterministic at least.
|
||||
int timeDelta = aSlow ? 2000 : 10;
|
||||
int tapCallsExpected = aSlow ? 2 : 1;
|
||||
|
||||
// Advance the fling animation by timeDelta milliseconds.
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
|
||||
|
||||
// Deliver a tap to abort the fling. Ensure that we get a SingleTap
|
||||
// call out of it if and only if the fling is slow.
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _)).Times(tapCallsExpected);
|
||||
Tap(apzc, ScreenIntPoint(10, 10), 0);
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
|
||||
// Deliver another tap, to make sure that taps are flowing properly once
|
||||
// the fling is aborted.
|
||||
Tap(apzc, ScreenIntPoint(100, 100), 0);
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
|
||||
// Verify that we didn't advance any further after the fling was aborted, in either case.
|
||||
ParentLayerPoint finalPointOut;
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut);
|
||||
EXPECT_EQ(pointOut.x, finalPointOut.x);
|
||||
EXPECT_EQ(pointOut.y, finalPointOut.y);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
void DoFlingStopWithSlowListener(bool aPreventDefault) {
|
||||
MakeApzcWaitForMainThread();
|
||||
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
uint64_t blockId = 0;
|
||||
|
||||
// Start the fling down.
|
||||
Pan(apzc, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
|
||||
apzc->ConfirmTarget(blockId);
|
||||
apzc->ContentReceivedInputBlock(blockId, false);
|
||||
|
||||
// Sample the fling a couple of times to ensure it's going.
|
||||
ParentLayerPoint point, finalPoint;
|
||||
AsyncTransform viewTransform;
|
||||
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
|
||||
apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, TimeDuration::FromMilliseconds(10));
|
||||
EXPECT_GT(finalPoint.y, point.y);
|
||||
|
||||
// Now we put our finger down to stop the fling
|
||||
TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
|
||||
|
||||
// Re-sample to make sure it hasn't moved
|
||||
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
|
||||
EXPECT_EQ(finalPoint.x, point.x);
|
||||
EXPECT_EQ(finalPoint.y, point.y);
|
||||
|
||||
// respond to the touchdown that stopped the fling.
|
||||
// even if we do a prevent-default on it, the animation should remain stopped.
|
||||
apzc->ContentReceivedInputBlock(blockId, aPreventDefault);
|
||||
|
||||
// Verify the page hasn't moved
|
||||
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(70));
|
||||
EXPECT_EQ(finalPoint.x, point.x);
|
||||
EXPECT_EQ(finalPoint.y, point.y);
|
||||
|
||||
// clean up
|
||||
TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZCFlingStopTester, FlingStop) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
DoFlingStopTest(false);
|
||||
}
|
||||
|
||||
TEST_F(APZCFlingStopTester, FlingStopTap) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
DoFlingStopTest(true);
|
||||
}
|
||||
|
||||
TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
DoFlingStopWithSlowListener(false);
|
||||
}
|
||||
|
||||
TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
DoFlingStopWithSlowListener(true);
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, ShortPress) {
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
{
|
||||
InSequence s;
|
||||
// This verifies that the single tap notification is sent after the
|
||||
// touchup is fully processed. The ordering here is important.
|
||||
EXPECT_CALL(check, Call("pre-tap"));
|
||||
EXPECT_CALL(check, Call("post-tap"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
}
|
||||
|
||||
check.Call("pre-tap");
|
||||
TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
|
||||
check.Call("post-tap");
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, MediumPress) {
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
{
|
||||
InSequence s;
|
||||
// This verifies that the single tap notification is sent after the
|
||||
// touchup is fully processed. The ordering here is important.
|
||||
EXPECT_CALL(check, Call("pre-tap"));
|
||||
EXPECT_CALL(check, Call("post-tap"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
}
|
||||
|
||||
check.Call("pre-tap");
|
||||
TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(400));
|
||||
check.Call("post-tap");
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
class APZCLongPressTester : public APZCGestureDetectorTester {
|
||||
protected:
|
||||
void DoLongPressTest(uint32_t aBehavior) {
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
uint64_t blockId = 0;
|
||||
|
||||
nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
|
||||
}
|
||||
// Have content "respond" to the touchstart
|
||||
apzc->ContentReceivedInputBlock(blockId, false);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(check, Call("preHandleLongTap"));
|
||||
blockId++;
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
|
||||
EXPECT_CALL(check, Call("postHandleLongTap"));
|
||||
|
||||
EXPECT_CALL(check, Call("preHandleLongTapUp"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("postHandleLongTapUp"));
|
||||
}
|
||||
|
||||
// Manually invoke the longpress while the touch is currently down.
|
||||
check.Call("preHandleLongTap");
|
||||
mcc->RunThroughDelayedTasks();
|
||||
check.Call("postHandleLongTap");
|
||||
|
||||
// Dispatching the longpress event starts a new touch block, which
|
||||
// needs a new content response and also has a pending timeout task
|
||||
// in the queue. Deal with those here. We do the content response first
|
||||
// with preventDefault=false, and then we run the timeout task which
|
||||
// "loses the race" and does nothing.
|
||||
apzc->ContentReceivedInputBlock(blockId, false);
|
||||
mcc->AdvanceByMillis(1000);
|
||||
|
||||
// Finally, simulate lifting the finger. Since the long-press wasn't
|
||||
// prevent-defaulted, we should get a long-tap-up event.
|
||||
check.Call("preHandleLongTapUp");
|
||||
status = TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
|
||||
mcc->RunThroughDelayedTasks();
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
check.Call("postHandleLongTapUp");
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
|
||||
int touchX = 10,
|
||||
touchStartY = 10,
|
||||
touchEndY = 50;
|
||||
|
||||
uint64_t blockId = 0;
|
||||
nsEventStatus status = TouchDown(apzc, ScreenIntPoint(touchX, touchStartY), mcc->Time(), &blockId);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
// SetAllowedTouchBehavior() must be called after sending touch-start.
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
|
||||
}
|
||||
// Have content "respond" to the touchstart
|
||||
apzc->ContentReceivedInputBlock(blockId, false);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(check, Call("preHandleLongTap"));
|
||||
blockId++;
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
|
||||
EXPECT_CALL(check, Call("postHandleLongTap"));
|
||||
}
|
||||
|
||||
// Manually invoke the longpress while the touch is currently down.
|
||||
check.Call("preHandleLongTap");
|
||||
mcc->RunThroughDelayedTasks();
|
||||
check.Call("postHandleLongTap");
|
||||
|
||||
// There should be a TimeoutContentResponse task in the queue still,
|
||||
// waiting for the response from the longtap event dispatched above.
|
||||
// Send the signal that content has handled the long-tap, and then run
|
||||
// the timeout task (it will be a no-op because the content "wins" the
|
||||
// race. This takes the place of the "contextmenu" event.
|
||||
apzc->ContentReceivedInputBlock(blockId, true);
|
||||
mcc->AdvanceByMillis(1000);
|
||||
|
||||
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
|
||||
status = apzc->ReceiveInputEvent(mti, nullptr);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
|
||||
status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
|
||||
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
|
||||
EXPECT_EQ(ParentLayerPoint(), pointOut);
|
||||
EXPECT_EQ(AsyncTransform(), viewTransformOut);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZCLongPressTester, LongPress) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
DoLongPressTest(mozilla::layers::AllowedTouchBehavior::NONE);
|
||||
}
|
||||
|
||||
TEST_F(APZCLongPressTester, LongPressWithTouchAction) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
DoLongPressTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
}
|
||||
|
||||
TEST_F(APZCLongPressTester, LongPressPreventDefault) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::NONE);
|
||||
}
|
||||
|
||||
TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, DoubleTap) {
|
||||
MakeApzcWaitForMainThread();
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
|
||||
uint64_t blockIds[2];
|
||||
DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
|
||||
|
||||
// responses to the two touchstarts
|
||||
apzc->ContentReceivedInputBlock(blockIds[0], false);
|
||||
apzc->ContentReceivedInputBlock(blockIds[1], false);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
|
||||
MakeApzcWaitForMainThread();
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSecondTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
|
||||
|
||||
uint64_t blockIds[2];
|
||||
DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
|
||||
|
||||
// responses to the two touchstarts
|
||||
apzc->ContentReceivedInputBlock(blockIds[0], false);
|
||||
apzc->ContentReceivedInputBlock(blockIds[1], false);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
|
||||
MakeApzcWaitForMainThread();
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
|
||||
|
||||
uint64_t blockIds[2];
|
||||
DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
|
||||
|
||||
// responses to the two touchstarts
|
||||
apzc->ContentReceivedInputBlock(blockIds[0], true);
|
||||
apzc->ContentReceivedInputBlock(blockIds[1], false);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
|
||||
MakeApzcWaitForMainThread();
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
|
||||
|
||||
uint64_t blockIds[2];
|
||||
DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
|
||||
|
||||
// responses to the two touchstarts
|
||||
apzc->ContentReceivedInputBlock(blockIds[0], true);
|
||||
apzc->ContentReceivedInputBlock(blockIds[1], true);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
// Test for bug 947892
|
||||
// We test whether we dispatch tap event when the tap is followed by pinch.
|
||||
TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
|
||||
Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
|
||||
|
||||
int inputId = 0;
|
||||
MultiTouchInput mti;
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
|
||||
Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
|
||||
|
||||
int inputId = 0;
|
||||
MultiTouchInput mti;
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
|
||||
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
|
||||
apzc->ReceiveInputEvent(mti, nullptr);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) {
|
||||
// Since we try to allow concurrent input blocks of different types to
|
||||
// co-exist, the wheel block shouldn't interrupt the long-press detection.
|
||||
// But more importantly, this shouldn't crash, which is what it did at one
|
||||
// point in time.
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(1);
|
||||
|
||||
uint64_t touchBlockId = 0;
|
||||
uint64_t wheelBlockId = 0;
|
||||
nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &touchBlockId);
|
||||
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
|
||||
SetDefaultAllowedTouchBehavior(apzc, touchBlockId);
|
||||
}
|
||||
mcc->AdvanceByMillis(10);
|
||||
Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
|
||||
EXPECT_NE(touchBlockId, wheelBlockId);
|
||||
mcc->AdvanceByMillis(1000);
|
||||
}
|
||||
|
||||
TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
|
||||
// In this test, even though the wheel block comes right after the tap, the
|
||||
// tap should still be dispatched because it completes fully before the wheel
|
||||
// block arrived.
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
|
||||
|
||||
// We make the APZC zoomable so the gesture detector needs to wait to
|
||||
// distinguish between tap and double-tap. During that timeout is when we
|
||||
// insert the wheel event.
|
||||
MakeApzcZoomable();
|
||||
|
||||
uint64_t touchBlockId = 0;
|
||||
uint64_t wheelBlockId = 0;
|
||||
Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100),
|
||||
nullptr, &touchBlockId);
|
||||
mcc->AdvanceByMillis(10);
|
||||
Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time(), &wheelBlockId);
|
||||
EXPECT_NE(touchBlockId, wheelBlockId);
|
||||
while (mcc->RunThroughDelayedTasks());
|
||||
}
|
||||
578
gfx/layers/apz/test/gtest/TestHitTesting.cpp
Normal file
578
gfx/layers/apz/test/gtest/TestHitTesting.cpp
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZHitTestingTester : public APZCTreeManagerTester {
|
||||
protected:
|
||||
ScreenToParentLayerMatrix4x4 transformToApzc;
|
||||
ParentLayerToScreenMatrix4x4 transformToGecko;
|
||||
|
||||
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
|
||||
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
|
||||
if (hit) {
|
||||
transformToApzc = manager->GetScreenToApzcTransform(hit.get());
|
||||
transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
|
||||
}
|
||||
return hit.forget();
|
||||
}
|
||||
|
||||
protected:
|
||||
void CreateHitTesting1LayerTree() {
|
||||
const char* layerTreeSyntax = "c(tttt)";
|
||||
// LayerID 0 1234
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
nsIntRegion(IntRect(10,10,20,20)),
|
||||
nsIntRegion(IntRect(10,10,20,20)),
|
||||
nsIntRegion(IntRect(5,5,20,20)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
}
|
||||
|
||||
void CreateHitTesting2LayerTree() {
|
||||
const char* layerTreeSyntax = "c(tc(t))";
|
||||
// LayerID 0 12 3
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,100,100)),
|
||||
nsIntRegion(IntRect(10,10,40,40)),
|
||||
nsIntRegion(IntRect(10,60,40,40)),
|
||||
nsIntRegion(IntRect(10,60,40,40)),
|
||||
};
|
||||
Matrix4x4 transforms[] = {
|
||||
Matrix4x4(),
|
||||
Matrix4x4(),
|
||||
Matrix4x4::Scaling(2, 1, 1),
|
||||
Matrix4x4(),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
|
||||
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
|
||||
}
|
||||
|
||||
void DisableApzOn(Layer* aLayer) {
|
||||
ScrollMetadata m = aLayer->GetScrollMetadata(0);
|
||||
m.SetForceDisableApz(true);
|
||||
aLayer->SetScrollMetadata(m);
|
||||
}
|
||||
|
||||
void CreateComplexMultiLayerTree() {
|
||||
const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
|
||||
// LayerID 0 12 3 45 6 7 89
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,300,400)), // root(0)
|
||||
nsIntRegion(IntRect(0,0,100,100)), // thebes(1) in top-left
|
||||
nsIntRegion(IntRect(50,50,200,300)), // container(2) centered in root(0)
|
||||
nsIntRegion(IntRect(50,50,200,300)), // thebes(3) fully occupying parent container(2)
|
||||
nsIntRegion(IntRect(0,200,100,100)), // thebes(4) in bottom-left
|
||||
nsIntRegion(IntRect(200,0,100,400)), // container(5) along the right 100px of root(0)
|
||||
nsIntRegion(IntRect(200,0,100,200)), // container(6) taking up the top half of parent container(5)
|
||||
nsIntRegion(IntRect(200,0,100,200)), // thebes(7) fully occupying parent container(6)
|
||||
nsIntRegion(IntRect(200,200,100,100)), // thebes(8) in bottom-right (below (6))
|
||||
nsIntRegion(IntRect(200,300,100,100)), // thebes(9) in bottom-right (below (8))
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2);
|
||||
SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1);
|
||||
SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3);
|
||||
}
|
||||
|
||||
void CreateBug1148350LayerTree() {
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
// LayerID 0 1
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
|
||||
}
|
||||
};
|
||||
|
||||
// A simple hit testing test that doesn't involve any transforms on layers.
|
||||
TEST_F(APZHitTestingTester, HitTesting1) {
|
||||
CreateHitTesting1LayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
|
||||
// No APZC attached so hit testing will return no APZC at (20,20)
|
||||
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
|
||||
TestAsyncPanZoomController* nullAPZC = nullptr;
|
||||
EXPECT_EQ(nullAPZC, hit.get());
|
||||
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
|
||||
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
|
||||
|
||||
uint32_t paintSequenceNumber = 0;
|
||||
|
||||
// Now we have a root APZC that will match the page
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
|
||||
hit = GetTargetAPZC(ScreenPoint(15, 15));
|
||||
EXPECT_EQ(ApzcOf(root), hit.get());
|
||||
// expect hit point at LayerIntPoint(15, 15)
|
||||
EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
|
||||
EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
|
||||
|
||||
// Now we have a sub APZC with a better fit
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
|
||||
EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(ApzcOf(layers[3]), hit.get());
|
||||
// expect hit point at LayerIntPoint(25, 25)
|
||||
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
|
||||
|
||||
// At this point, layers[4] obscures layers[3] at the point (15, 15) so
|
||||
// hitting there should hit the root APZC
|
||||
hit = GetTargetAPZC(ScreenPoint(15, 15));
|
||||
EXPECT_EQ(ApzcOf(root), hit.get());
|
||||
|
||||
// Now test hit testing when we have two scrollable layers
|
||||
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
|
||||
hit = GetTargetAPZC(ScreenPoint(15, 15));
|
||||
EXPECT_EQ(ApzcOf(layers[4]), hit.get());
|
||||
// expect hit point at LayerIntPoint(15, 15)
|
||||
EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
|
||||
EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
|
||||
|
||||
// Hit test ouside the reach of layer[3,4] but inside root
|
||||
hit = GetTargetAPZC(ScreenPoint(90, 90));
|
||||
EXPECT_EQ(ApzcOf(root), hit.get());
|
||||
// expect hit point at LayerIntPoint(90, 90)
|
||||
EXPECT_EQ(ParentLayerPoint(90, 90), transformToApzc.TransformPoint(ScreenPoint(90, 90)));
|
||||
EXPECT_EQ(ScreenPoint(90, 90), transformToGecko.TransformPoint(ParentLayerPoint(90, 90)));
|
||||
|
||||
// Hit test ouside the reach of any layer
|
||||
hit = GetTargetAPZC(ScreenPoint(1000, 10));
|
||||
EXPECT_EQ(nullAPZC, hit.get());
|
||||
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
|
||||
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
|
||||
hit = GetTargetAPZC(ScreenPoint(-1000, 10));
|
||||
EXPECT_EQ(nullAPZC, hit.get());
|
||||
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
|
||||
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
|
||||
}
|
||||
|
||||
// A more involved hit testing test that involves css and async transforms.
|
||||
TEST_F(APZHitTestingTester, HitTesting2) {
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
|
||||
CreateHitTesting2LayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
// At this point, the following holds (all coordinates in screen pixels):
|
||||
// layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
|
||||
// layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
|
||||
// layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
|
||||
// layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
|
||||
|
||||
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
|
||||
TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
|
||||
TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
|
||||
|
||||
// Hit an area that's clearly on the root layer but not any of the child layers.
|
||||
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc.TransformPoint(ScreenPoint(75, 25)));
|
||||
EXPECT_EQ(ScreenPoint(75, 25), transformToGecko.TransformPoint(ParentLayerPoint(75, 25)));
|
||||
|
||||
// Hit an area on the root that would be on layers[3] if layers[2]
|
||||
// weren't transformed.
|
||||
// Note that if layers[2] were scrollable, then this would hit layers[2]
|
||||
// because its composition bounds would be at (10,60)-(50,100) (and the
|
||||
// scale-only transform that we set on layers[2] would be invalid because
|
||||
// it would place the layer into overscroll, as its composition bounds
|
||||
// start at x=10 but its content at x=20).
|
||||
hit = GetTargetAPZC(ScreenPoint(15, 75));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc.TransformPoint(ScreenPoint(15, 75)));
|
||||
EXPECT_EQ(ScreenPoint(15, 75), transformToGecko.TransformPoint(ParentLayerPoint(15, 75)));
|
||||
|
||||
// Hit an area on layers[1].
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(apzc1, hit.get());
|
||||
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
|
||||
|
||||
// Hit an area on layers[3].
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 75));
|
||||
EXPECT_EQ(apzc3, hit.get());
|
||||
// transformToApzc should unapply layers[2]'s transform
|
||||
EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 75)));
|
||||
// and transformToGecko should reapply it
|
||||
EXPECT_EQ(ScreenPoint(25, 75), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
|
||||
|
||||
// Hit an area on layers[3] that would be on the root if layers[2]
|
||||
// weren't transformed.
|
||||
hit = GetTargetAPZC(ScreenPoint(75, 75));
|
||||
EXPECT_EQ(apzc3, hit.get());
|
||||
// transformToApzc should unapply layers[2]'s transform
|
||||
EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
|
||||
// and transformToGecko should reapply it
|
||||
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(37.5, 75)));
|
||||
|
||||
// Pan the root layer upward by 50 pixels.
|
||||
// This causes layers[1] to scroll out of view, and an async transform
|
||||
// of -50 to be set on the root layer.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
|
||||
// This first pan will move the APZC by 50 pixels, and dispatch a paint request.
|
||||
// Since this paint request is in the queue to Gecko, transformToGecko will
|
||||
// take it into account.
|
||||
ApzcPanNoFling(apzcroot, 100, 50);
|
||||
|
||||
// Hit where layers[3] used to be. It should now hit the root.
|
||||
hit = GetTargetAPZC(ScreenPoint(75, 75));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
// transformToApzc doesn't unapply the root's own async transform
|
||||
EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
|
||||
// and transformToGecko unapplies it and then reapplies it, because by the
|
||||
// time the event being transformed reaches Gecko the new paint request will
|
||||
// have been handled.
|
||||
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
|
||||
|
||||
// Hit where layers[1] used to be and where layers[3] should now be.
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(apzc3, hit.get());
|
||||
// transformToApzc unapplies both layers[2]'s css transform and the root's
|
||||
// async transform
|
||||
EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
|
||||
// transformToGecko reapplies both the css transform and the async transform
|
||||
// because we have already issued a paint request with it.
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
|
||||
|
||||
// This second pan will move the APZC by another 50 pixels.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
ApzcPanNoFling(apzcroot, 100, 50);
|
||||
|
||||
// Hit where layers[3] used to be. It should now hit the root.
|
||||
hit = GetTargetAPZC(ScreenPoint(75, 75));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
// transformToApzc doesn't unapply the root's own async transform
|
||||
EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc.TransformPoint(ScreenPoint(75, 75)));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels
|
||||
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
|
||||
|
||||
// Hit where layers[1] used to be. It should now hit the root.
|
||||
hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(apzcroot, hit.get());
|
||||
// transformToApzc doesn't unapply the root's own async transform
|
||||
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
|
||||
// transformToGecko unapplies the full async transform of -100 pixels
|
||||
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
|
||||
CreateComplexMultiLayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
/* The layer tree looks like this:
|
||||
|
||||
0
|
||||
|----|--+--|----|
|
||||
1 2 4 5
|
||||
| /|\
|
||||
3 6 8 9
|
||||
|
|
||||
7
|
||||
|
||||
Layers 1,2 have the same APZC
|
||||
Layers 4,6,8 have the same APZC
|
||||
Layer 7 has an APZC
|
||||
Layer 9 has an APZC
|
||||
*/
|
||||
|
||||
TestAsyncPanZoomController* nullAPZC = nullptr;
|
||||
// Ensure all the scrollable layers have an APZC
|
||||
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
|
||||
EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
|
||||
EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
|
||||
// Ensure those that scroll together have the same APZCs
|
||||
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
|
||||
EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
|
||||
EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
|
||||
// Ensure those that don't scroll together have different APZCs
|
||||
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
|
||||
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
|
||||
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
|
||||
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
|
||||
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
|
||||
EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
|
||||
// Ensure the APZC parent chains are set up correctly
|
||||
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
|
||||
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
|
||||
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
|
||||
TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
|
||||
EXPECT_EQ(nullptr, layers1_2->GetParent());
|
||||
EXPECT_EQ(nullptr, layers4_6_8->GetParent());
|
||||
EXPECT_EQ(layers4_6_8, layer7->GetParent());
|
||||
EXPECT_EQ(nullptr, layer9->GetParent());
|
||||
// Ensure the hit-testing tree looks like the layer tree
|
||||
RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
|
||||
RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
|
||||
RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
|
||||
RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
|
||||
RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
|
||||
RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
|
||||
RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
|
||||
RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
|
||||
RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
|
||||
RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
|
||||
EXPECT_EQ(nullptr, node1->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node3->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node6->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node7->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node1->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node3->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node4->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node7->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node8->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node9->GetLastChild());
|
||||
|
||||
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(ApzcOf(layers[1]), hit.get());
|
||||
hit = GetTargetAPZC(ScreenPoint(275, 375));
|
||||
EXPECT_EQ(ApzcOf(layers[9]), hit.get());
|
||||
hit = GetTargetAPZC(ScreenPoint(250, 100));
|
||||
EXPECT_EQ(ApzcOf(layers[7]), hit.get());
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
|
||||
// The main purpose of this test is to verify that touch-start events (or anything
|
||||
// that starts a new input block) don't ever get untransformed. This should always
|
||||
// hold because the APZ code should flush repaints when we start a new input block
|
||||
// and the transform to gecko space should be empty.
|
||||
|
||||
CreateSimpleScrollingLayer();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
|
||||
|
||||
// At this point, the following holds (all coordinates in screen pixels):
|
||||
// layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(check, Call("post-first-touch-start"));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(check, Call("post-second-fling"));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(check, Call("post-second-touch-start"));
|
||||
}
|
||||
|
||||
// This first pan will move the APZC by 50 pixels, and dispatch a paint request.
|
||||
ApzcPanNoFling(apzcroot, 100, 50);
|
||||
|
||||
// Verify that a touch start doesn't get untransformed
|
||||
ScreenIntPoint touchPoint(50, 50);
|
||||
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
|
||||
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
|
||||
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
|
||||
check.Call("post-first-touch-start");
|
||||
|
||||
// Send a touchend to clear state
|
||||
mti.mType = MultiTouchInput::MULTITOUCH_END;
|
||||
manager->ReceiveInputEvent(mti, nullptr, nullptr);
|
||||
|
||||
mcc->AdvanceByMillis(1000);
|
||||
|
||||
// Now do two pans. The first of these will dispatch a repaint request, as above.
|
||||
// The second will get stuck in the paint throttler because the first one doesn't
|
||||
// get marked as "completed", so this will result in a non-empty LD transform.
|
||||
// (Note that any outstanding repaint requests from the first half of this test
|
||||
// don't impact this half because we advance the time by 1 second, which will trigger
|
||||
// the max-wait-exceeded codepath in the paint throttler).
|
||||
ApzcPanNoFling(apzcroot, 100, 50);
|
||||
check.Call("post-second-fling");
|
||||
ApzcPanNoFling(apzcroot, 100, 50);
|
||||
|
||||
// Ensure that a touch start again doesn't get untransformed by flushing
|
||||
// a repaint
|
||||
mti.mType = MultiTouchInput::MULTITOUCH_START;
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
|
||||
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
|
||||
check.Call("post-second-touch-start");
|
||||
|
||||
mti.mType = MultiTouchInput::MULTITOUCH_END;
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
|
||||
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
|
||||
// The purpose of this test is to ensure that wheel events trigger a repaint
|
||||
// flush as per bug 1166871, and that the wheel event untransform is a no-op.
|
||||
|
||||
CreateSimpleScrollingLayer();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
|
||||
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
|
||||
ScreenPoint origin(100, 50);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
|
||||
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
|
||||
origin, 0, 10, false);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
|
||||
EXPECT_EQ(origin, swi.mOrigin);
|
||||
|
||||
AsyncTransform viewTransform;
|
||||
ParentLayerPoint point;
|
||||
apzcroot->SampleContentTransformForFrame(&viewTransform, point);
|
||||
EXPECT_EQ(0, point.x);
|
||||
EXPECT_EQ((i + 1) * 10, point.y);
|
||||
EXPECT_EQ(0, viewTransform.mTranslation.x);
|
||||
EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
|
||||
|
||||
mcc->AdvanceByMillis(5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, TestForceDisableApz) {
|
||||
CreateSimpleScrollingLayer();
|
||||
DisableApzOn(root);
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
|
||||
|
||||
ScreenPoint origin(100, 50);
|
||||
ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
|
||||
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
|
||||
origin, 0, 10, false);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
|
||||
EXPECT_EQ(origin, swi.mOrigin);
|
||||
|
||||
AsyncTransform viewTransform;
|
||||
ParentLayerPoint point;
|
||||
apzcroot->SampleContentTransformForFrame(&viewTransform, point);
|
||||
// Since APZ is force-disabled, we expect to see the async transform via
|
||||
// the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
|
||||
EXPECT_EQ(0, point.x);
|
||||
EXPECT_EQ(10, point.y);
|
||||
EXPECT_EQ(0, viewTransform.mTranslation.x);
|
||||
EXPECT_EQ(-10, viewTransform.mTranslation.y);
|
||||
viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
|
||||
point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
|
||||
EXPECT_EQ(0, point.x);
|
||||
EXPECT_EQ(0, point.y);
|
||||
EXPECT_EQ(0, viewTransform.mTranslation.x);
|
||||
EXPECT_EQ(0, viewTransform.mTranslation.y);
|
||||
|
||||
mcc->AdvanceByMillis(10);
|
||||
|
||||
// With untransforming events we should get normal behaviour (in this case,
|
||||
// no noticeable untransform, because the repaint request already got
|
||||
// flushed).
|
||||
swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
|
||||
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
|
||||
origin, 0, 0, false);
|
||||
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
|
||||
EXPECT_EQ(origin, swi.mOrigin);
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, Bug1148350) {
|
||||
CreateBug1148350LayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
MockFunction<void(std::string checkPointName)> check;
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped without transform"));
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
|
||||
EXPECT_CALL(check, Call("Tapped with interleaved transform"));
|
||||
}
|
||||
|
||||
Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
|
||||
mcc->RunThroughDelayedTasks();
|
||||
check.Call("Tapped without transform");
|
||||
|
||||
uint64_t blockId;
|
||||
TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
|
||||
if (gfxPrefs::TouchActionEnabled()) {
|
||||
SetDefaultAllowedTouchBehavior(manager, blockId);
|
||||
}
|
||||
mcc->AdvanceByMillis(100);
|
||||
|
||||
layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
|
||||
layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
|
||||
mcc->RunThroughDelayedTasks();
|
||||
check.Call("Tapped with interleaved transform");
|
||||
}
|
||||
|
||||
TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
|
||||
// Create the layer tree.
|
||||
const char* layerTreeSyntax = "c(tt)";
|
||||
// LayerID 0 12
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
nsIntRegion(IntRect(0,0,200,200)),
|
||||
nsIntRegion(IntRect(0,0,200,100))
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
|
||||
// Add root scroll metadata to the first painted layer.
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200));
|
||||
|
||||
// Add root and subframe scroll metadata to the second painted layer.
|
||||
// Give the subframe metadata a scroll clip corresponding to the subframe's
|
||||
// composition bounds.
|
||||
// Importantly, give the layer a layer clip which leaks outside of the
|
||||
// subframe's composition bounds.
|
||||
ScrollMetadata rootMetadata = BuildScrollMetadata(
|
||||
FrameMetrics::START_SCROLL_ID, CSSRect(0,0,200,200),
|
||||
ParentLayerRect(0,0,200,200));
|
||||
ScrollMetadata subframeMetadata = BuildScrollMetadata(
|
||||
FrameMetrics::START_SCROLL_ID + 1, CSSRect(0,0,200,200),
|
||||
ParentLayerRect(0,0,200,100));
|
||||
subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100))));
|
||||
layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
|
||||
layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200)));
|
||||
SetEventRegionsBasedOnBottommostMetrics(layers[2]);
|
||||
|
||||
// Build the hit testing tree.
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
// Pan on a region that's inside layers[2]'s layer clip, but outside
|
||||
// its subframe metadata's scroll clip.
|
||||
Pan(manager, 120, 110);
|
||||
|
||||
// Test that the subframe hasn't scrolled.
|
||||
EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset());
|
||||
}
|
||||
44
gfx/layers/apz/test/gtest/TestInputQueue.cpp
Normal file
44
gfx/layers/apz/test/gtest/TestInputQueue.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
// Test of scenario described in bug 1269067 - that a continuing mouse drag
|
||||
// doesn't interrupt a wheel scrolling animation
|
||||
TEST_F(APZCTreeManagerTester, WheelInterruptedByMouseDrag) {
|
||||
// Set up a scrollable layer
|
||||
CreateSimpleScrollingLayer();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
|
||||
|
||||
uint64_t dragBlockId = 0;
|
||||
uint64_t wheelBlockId = 0;
|
||||
uint64_t tmpBlockId = 0;
|
||||
|
||||
// First start the mouse drag
|
||||
MouseDown(apzc, ScreenIntPoint(5, 5), mcc->Time(), &dragBlockId);
|
||||
MouseMove(apzc, ScreenIntPoint(6, 6), mcc->Time(), &tmpBlockId);
|
||||
EXPECT_EQ(dragBlockId, tmpBlockId);
|
||||
|
||||
// Insert the wheel event, check that it has a new block id
|
||||
SmoothWheel(apzc, ScreenIntPoint(6, 6), ScreenPoint(0, 1), mcc->Time(), &wheelBlockId);
|
||||
EXPECT_NE(dragBlockId, wheelBlockId);
|
||||
|
||||
// Continue the drag, check that the block id is the same as before
|
||||
MouseMove(apzc, ScreenIntPoint(7, 5), mcc->Time(), &tmpBlockId);
|
||||
EXPECT_EQ(dragBlockId, tmpBlockId);
|
||||
|
||||
// Finish the wheel animation
|
||||
apzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Check that it scrolled
|
||||
ParentLayerPoint scroll = apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
|
||||
EXPECT_EQ(scroll.x, 0);
|
||||
EXPECT_EQ(scroll.y, 10); // We scrolled 1 "line" or 10 pixels
|
||||
}
|
||||
128
gfx/layers/apz/test/gtest/TestPanning.cpp
Normal file
128
gfx/layers/apz/test/gtest/TestPanning.cpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCBasicTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZCPanningTester : public APZCBasicTester {
|
||||
protected:
|
||||
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
|
||||
{
|
||||
if (aShouldTriggerScroll) {
|
||||
// One repaint request for each pan.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(aBehavior);
|
||||
|
||||
// Pan down
|
||||
PanAndCheckStatus(apzc, touchStart, touchEnd, aShouldBeConsumed, &allowedTouchBehaviors);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
|
||||
if (aShouldTriggerScroll) {
|
||||
EXPECT_EQ(ParentLayerPoint(0, -(touchEnd-touchStart)), pointOut);
|
||||
EXPECT_NE(AsyncTransform(), viewTransformOut);
|
||||
} else {
|
||||
EXPECT_EQ(ParentLayerPoint(), pointOut);
|
||||
EXPECT_EQ(AsyncTransform(), viewTransformOut);
|
||||
}
|
||||
|
||||
// Clear the fling from the previous pan, or stopping it will
|
||||
// consume the next touchstart
|
||||
apzc->CancelAnimation();
|
||||
|
||||
// Pan back
|
||||
PanAndCheckStatus(apzc, touchEnd, touchStart, aShouldBeConsumed, &allowedTouchBehaviors);
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
|
||||
EXPECT_EQ(ParentLayerPoint(), pointOut);
|
||||
EXPECT_EQ(AsyncTransform(), viewTransformOut);
|
||||
}
|
||||
|
||||
void DoPanWithPreventDefaultTest()
|
||||
{
|
||||
MakeApzcWaitForMainThread();
|
||||
|
||||
int touchStart = 50;
|
||||
int touchEnd = 10;
|
||||
ParentLayerPoint pointOut;
|
||||
AsyncTransform viewTransformOut;
|
||||
uint64_t blockId = 0;
|
||||
|
||||
// Pan down
|
||||
nsTArray<uint32_t> allowedTouchBehaviors;
|
||||
allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
|
||||
PanAndCheckStatus(apzc, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
|
||||
|
||||
// Send the signal that content has handled and preventDefaulted the touch
|
||||
// events. This flushes the event queue.
|
||||
apzc->ContentReceivedInputBlock(blockId, true);
|
||||
|
||||
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
|
||||
EXPECT_EQ(ParentLayerPoint(), pointOut);
|
||||
EXPECT_EQ(AsyncTransform(), viewTransformOut);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZCPanningTester, Pan) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
|
||||
}
|
||||
|
||||
// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
|
||||
// to bottom and back - from bottom to top.
|
||||
// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
|
||||
// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
|
||||
// behavior.
|
||||
// However, the events will be marked as consumed even if the behavior in PAN_X, because the user could
|
||||
// move their finger horizontally too - APZ has no way of knowing beforehand and so must consume the
|
||||
// events.
|
||||
TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
|
||||
}
|
||||
|
||||
TEST_F(APZCPanningTester, PanWithTouchActionNone) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
DoPanTest(false, false, 0);
|
||||
}
|
||||
|
||||
TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
|
||||
}
|
||||
|
||||
TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
|
||||
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
|
||||
}
|
||||
|
||||
TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
DoPanWithPreventDefaultTest();
|
||||
}
|
||||
|
||||
TEST_F(APZCPanningTester, PanWithPreventDefault) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
DoPanWithPreventDefaultTest();
|
||||
}
|
||||
294
gfx/layers/apz/test/gtest/TestPinching.cpp
Normal file
294
gfx/layers/apz/test/gtest/TestPinching.cpp
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCBasicTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZCPinchTester : public APZCBasicTester {
|
||||
public:
|
||||
explicit APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
|
||||
: APZCBasicTester(aGestureBehavior)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
FrameMetrics GetPinchableFrameMetrics()
|
||||
{
|
||||
FrameMetrics fm;
|
||||
fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
|
||||
fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
|
||||
fm.SetScrollOffset(CSSPoint(300, 300));
|
||||
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
|
||||
// APZC only allows zooming on the root scrollable frame.
|
||||
fm.SetIsRootContent(true);
|
||||
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
|
||||
return fm;
|
||||
}
|
||||
|
||||
void DoPinchTest(bool aShouldTriggerPinch,
|
||||
nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
|
||||
{
|
||||
apzc->SetFrameMetrics(GetPinchableFrameMetrics());
|
||||
MakeApzcZoomable();
|
||||
|
||||
if (aShouldTriggerPinch) {
|
||||
// One repaint request for each gesture.
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
|
||||
int touchInputId = 0;
|
||||
if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
|
||||
PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
|
||||
touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
|
||||
} else {
|
||||
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
|
||||
aShouldTriggerPinch);
|
||||
}
|
||||
|
||||
FrameMetrics fm = apzc->GetFrameMetrics();
|
||||
|
||||
if (aShouldTriggerPinch) {
|
||||
// the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
|
||||
EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
|
||||
EXPECT_EQ(305, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(310, fm.GetScrollOffset().y);
|
||||
} else {
|
||||
// The frame metrics should stay the same since touch-action:none makes
|
||||
// apzc ignore pinch gestures.
|
||||
EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
|
||||
EXPECT_EQ(300, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(300, fm.GetScrollOffset().y);
|
||||
}
|
||||
|
||||
// part 2 of the test, move to the top-right corner of the page and pinch and
|
||||
// make sure we stay in the correct spot
|
||||
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
|
||||
fm.SetScrollOffset(CSSPoint(930, 5));
|
||||
apzc->SetFrameMetrics(fm);
|
||||
// the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
|
||||
|
||||
if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
|
||||
PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
|
||||
touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
|
||||
} else {
|
||||
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
|
||||
aShouldTriggerPinch);
|
||||
}
|
||||
|
||||
fm = apzc->GetFrameMetrics();
|
||||
|
||||
if (aShouldTriggerPinch) {
|
||||
// the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
|
||||
EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
|
||||
EXPECT_EQ(880, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(0, fm.GetScrollOffset().y);
|
||||
} else {
|
||||
EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
|
||||
EXPECT_EQ(930, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(5, fm.GetScrollOffset().y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class APZCPinchGestureDetectorTester : public APZCPinchTester {
|
||||
public:
|
||||
APZCPinchGestureDetectorTester()
|
||||
: APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
|
||||
{
|
||||
}
|
||||
|
||||
void DoPinchWithPreventDefaultTest() {
|
||||
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
|
||||
apzc->SetFrameMetrics(originalMetrics);
|
||||
|
||||
MakeApzcWaitForMainThread();
|
||||
MakeApzcZoomable();
|
||||
|
||||
int touchInputId = 0;
|
||||
uint64_t blockId = 0;
|
||||
PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
|
||||
nullptr, nullptr, &blockId);
|
||||
|
||||
// Send the prevent-default notification for the touch block
|
||||
apzc->ContentReceivedInputBlock(blockId, true);
|
||||
|
||||
// verify the metrics didn't change (i.e. the pinch was ignored)
|
||||
FrameMetrics fm = apzc->GetFrameMetrics();
|
||||
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
DoPinchTest(true);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
DoPinchTest(true);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
|
||||
mozilla::layers::AllowedTouchBehavior::NONE };
|
||||
DoPinchTest(false, &behaviors);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
nsTArray<uint32_t> behaviors;
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
DoPinchTest(true, &behaviors);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
nsTArray<uint32_t> behaviors;
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
|
||||
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
DoPinchTest(false, &behaviors);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone_NoAPZZoom) {
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
|
||||
|
||||
// Since we are preventing the pinch action via touch-action we should not be
|
||||
// sending the pinch gesture notifications that would normally be sent when
|
||||
// APZAllowZooming is false.
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
|
||||
nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
|
||||
mozilla::layers::AllowedTouchBehavior::NONE };
|
||||
DoPinchTest(false, &behaviors);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
|
||||
DoPinchWithPreventDefaultTest();
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault_NoAPZZoom) {
|
||||
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
|
||||
|
||||
// Since we are preventing the pinch action we should not be sending the pinch
|
||||
// gesture notifications that would normally be sent when APZAllowZooming is
|
||||
// false.
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(_, _, _, _)).Times(0);
|
||||
|
||||
DoPinchWithPreventDefaultTest();
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
|
||||
// set up APZ
|
||||
apzc->SetFrameMetrics(GetPinchableFrameMetrics());
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
nsEventStatus statuses[3]; // scalebegin, scale, scaleend
|
||||
PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
|
||||
10, &statuses);
|
||||
|
||||
FrameMetrics fm = apzc->GetFrameMetrics();
|
||||
|
||||
// It starts from (300, 300), then moves the focus point from (250, 350) to
|
||||
// (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
|
||||
// causes the scroll offset to change by half of that (25, 25) pixels.
|
||||
EXPECT_EQ(325, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(325, fm.GetScrollOffset().y);
|
||||
EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_APZZoom_Disabled) {
|
||||
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
|
||||
|
||||
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
|
||||
apzc->SetFrameMetrics(originalMetrics);
|
||||
|
||||
// When APZAllowZooming is false, the ZoomConstraintsClient produces
|
||||
// ZoomConstraints with mAllowZoom set to false.
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
// With APZAllowZooming false, we expect the NotifyPinchGesture function to
|
||||
// get called as the pinch progresses, but the metrics shouldn't change.
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
|
||||
|
||||
int touchInputId = 0;
|
||||
uint64_t blockId = 0;
|
||||
PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
|
||||
nullptr, nullptr, &blockId);
|
||||
|
||||
// verify the metrics didn't change (i.e. the pinch was ignored inside APZ)
|
||||
FrameMetrics fm = apzc->GetFrameMetrics();
|
||||
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZCPinchGestureDetectorTester, Pinch_NoSpan) {
|
||||
SCOPED_GFX_PREF(APZAllowZooming, bool, false);
|
||||
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
|
||||
|
||||
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
|
||||
apzc->SetFrameMetrics(originalMetrics);
|
||||
|
||||
// When APZAllowZooming is false, the ZoomConstraintsClient produces
|
||||
// ZoomConstraints with mAllowZoom set to false.
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
// With APZAllowZooming false, we expect the NotifyPinchGesture function to
|
||||
// get called as the pinch progresses, but the metrics shouldn't change.
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_START, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_SCALE, apzc->GetGuid(), _, _)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, NotifyPinchGesture(PinchGestureInput::PINCHGESTURE_END, apzc->GetGuid(), LayoutDeviceCoord(0), _)).Times(1);
|
||||
|
||||
int inputId = 0;
|
||||
ScreenIntPoint focus(250, 300);
|
||||
|
||||
// Do a pinch holding a zero span and moving the focus by y=100
|
||||
|
||||
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
|
||||
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
|
||||
apzc->ReceiveInputEvent(mtiStart, nullptr);
|
||||
|
||||
focus.y -= 35 + 1; // this is to get over the PINCH_START_THRESHOLD in GestureEventListener.cpp
|
||||
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
|
||||
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
|
||||
apzc->ReceiveInputEvent(mtiMove1, nullptr);
|
||||
|
||||
focus.y -= 100; // do a two-finger scroll of 100 screen pixels
|
||||
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
|
||||
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
|
||||
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
|
||||
apzc->ReceiveInputEvent(mtiMove2, nullptr);
|
||||
|
||||
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
|
||||
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, focus));
|
||||
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, focus));
|
||||
apzc->ReceiveInputEvent(mtiEnd, nullptr);
|
||||
|
||||
// Done, check the metrics to make sure we scrolled by 100 screen pixels,
|
||||
// which is 50 CSS pixels for the pinchable frame metrics.
|
||||
|
||||
FrameMetrics fm = apzc->GetFrameMetrics();
|
||||
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
|
||||
EXPECT_EQ(originalMetrics.GetScrollOffset().y + 50, fm.GetScrollOffset().y);
|
||||
|
||||
apzc->AssertStateIsReset();
|
||||
}
|
||||
521
gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
Normal file
521
gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZScrollHandoffTester : public APZCTreeManagerTester {
|
||||
protected:
|
||||
UniquePtr<ScopedLayerTreeRegistration> registration;
|
||||
TestAsyncPanZoomController* rootApzc;
|
||||
|
||||
void CreateScrollHandoffLayerTree1() {
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 50, 100, 50))
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
rootApzc->GetFrameMetrics().SetIsRootContent(true); // make root APZC zoomable
|
||||
}
|
||||
|
||||
void CreateScrollHandoffLayerTree2() {
|
||||
const char* layerTreeSyntax = "c(c(t))";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 50, 100, 50))
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
SetScrollHandoff(layers[2], layers[1]);
|
||||
// No ScopedLayerTreeRegistration as that just needs to be done once per test
|
||||
// and this is the second layer tree for a particular test.
|
||||
MOZ_ASSERT(registration);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
}
|
||||
|
||||
void CreateScrollHandoffLayerTree3() {
|
||||
const char* layerTreeSyntax = "c(c(t)c(t))";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)), // root
|
||||
nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling parent 1
|
||||
nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling child 1
|
||||
nsIntRegion(IntRect(0, 50, 100, 50)), // scrolling parent 2
|
||||
nsIntRegion(IntRect(0, 50, 100, 50)) // scrolling child 2
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
|
||||
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 4, CSSRect(0, 50, 100, 100));
|
||||
SetScrollHandoff(layers[1], layers[0]);
|
||||
SetScrollHandoff(layers[3], layers[0]);
|
||||
SetScrollHandoff(layers[2], layers[1]);
|
||||
SetScrollHandoff(layers[4], layers[3]);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
}
|
||||
|
||||
void CreateScrollgrabLayerTree(bool makeParentScrollable = true) {
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)), // scroll-grabbing parent
|
||||
nsIntRegion(IntRect(0, 20, 100, 80)) // child
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
float parentHeight = makeParentScrollable ? 120 : 100;
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
rootApzc = ApzcOf(root);
|
||||
rootApzc->GetScrollMetadata().SetHasScrollgrab(true);
|
||||
}
|
||||
|
||||
void TestFlingAcceleration() {
|
||||
// Jack up the fling acceleration multiplier so we can easily determine
|
||||
// whether acceleration occured.
|
||||
const float kAcceleration = 100.0f;
|
||||
SCOPED_GFX_PREF(APZFlingAccelBaseMultiplier, float, kAcceleration);
|
||||
SCOPED_GFX_PREF(APZFlingAccelMinVelocity, float, 0.0);
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Pan once, enough to fully scroll the scrollgrab parent and then scroll
|
||||
// and fling the child.
|
||||
Pan(manager, 70, 40);
|
||||
|
||||
// Give the fling animation a chance to start.
|
||||
SampleAnimationsOnce();
|
||||
|
||||
float childVelocityAfterFling1 = childApzc->GetVelocityVector().y;
|
||||
|
||||
// Pan again.
|
||||
Pan(manager, 70, 40);
|
||||
|
||||
// Give the fling animation a chance to start.
|
||||
// This time it should be accelerated.
|
||||
SampleAnimationsOnce();
|
||||
|
||||
float childVelocityAfterFling2 = childApzc->GetVelocityVector().y;
|
||||
|
||||
// We should have accelerated once.
|
||||
// The division by 2 is to account for friction.
|
||||
EXPECT_GT(childVelocityAfterFling2,
|
||||
childVelocityAfterFling1 * kAcceleration / 2);
|
||||
|
||||
// We should not have accelerated twice.
|
||||
// The division by 4 is to account for friction.
|
||||
EXPECT_LE(childVelocityAfterFling2,
|
||||
childVelocityAfterFling1 * kAcceleration * kAcceleration / 4);
|
||||
}
|
||||
};
|
||||
|
||||
// Here we test that if the processing of a touch block is deferred while we
|
||||
// wait for content to send a prevent-default message, overscroll is still
|
||||
// handed off correctly when the block is processed.
|
||||
TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) {
|
||||
// Set up the APZC tree.
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Enable touch-listeners so that we can separate the queueing of input
|
||||
// events from them being processed.
|
||||
childApzc->SetWaitForMainThread();
|
||||
|
||||
// Queue input events for a pan.
|
||||
uint64_t blockId = 0;
|
||||
ApzcPanNoFling(childApzc, 90, 30, &blockId);
|
||||
|
||||
// Allow the pan to be processed.
|
||||
childApzc->ContentReceivedInputBlock(blockId, false);
|
||||
childApzc->ConfirmTarget(blockId);
|
||||
|
||||
// Make sure overscroll was handed off correctly.
|
||||
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
}
|
||||
|
||||
// Here we test that if the layer structure changes in between two input
|
||||
// blocks being queued, and the first block is only processed after the second
|
||||
// one has been queued, overscroll handoff for the first block follows
|
||||
// the original layer structure while overscroll handoff for the second block
|
||||
// follows the new layer structure.
|
||||
TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
|
||||
// Set up an initial APZC tree.
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Enable touch-listeners so that we can separate the queueing of input
|
||||
// events from them being processed.
|
||||
childApzc->SetWaitForMainThread();
|
||||
|
||||
// Queue input events for a pan.
|
||||
uint64_t blockId = 0;
|
||||
ApzcPanNoFling(childApzc, 90, 30, &blockId);
|
||||
|
||||
// Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
|
||||
// between the child and the root.
|
||||
CreateScrollHandoffLayerTree2();
|
||||
RefPtr<Layer> middle = layers[1];
|
||||
childApzc->SetWaitForMainThread();
|
||||
TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
|
||||
|
||||
// Queue input events for another pan.
|
||||
uint64_t secondBlockId = 0;
|
||||
ApzcPanNoFling(childApzc, 30, 90, &secondBlockId);
|
||||
|
||||
// Allow the first pan to be processed.
|
||||
childApzc->ContentReceivedInputBlock(blockId, false);
|
||||
childApzc->ConfirmTarget(blockId);
|
||||
|
||||
// Make sure things have scrolled according to the handoff chain in
|
||||
// place at the time the touch-start of the first pan was queued.
|
||||
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
|
||||
// Allow the second pan to be processed.
|
||||
childApzc->ContentReceivedInputBlock(secondBlockId, false);
|
||||
childApzc->ConfirmTarget(secondBlockId);
|
||||
|
||||
// Make sure things have scrolled according to the handoff chain in
|
||||
// place at the time the touch-start of the second pan was queued.
|
||||
EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
}
|
||||
|
||||
// Test that putting a second finger down on an APZC while a down-chain APZC
|
||||
// is overscrolled doesn't result in being stuck in overscroll.
|
||||
TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) {
|
||||
// Enable overscrolling.
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
|
||||
|
||||
// Pan, causing the parent APZC to overscroll.
|
||||
Pan(manager, 10, 40, true /* keep finger down */);
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_TRUE(rootApzc->IsOverscrolled());
|
||||
|
||||
// Put a second finger down.
|
||||
MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
// Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
|
||||
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
|
||||
|
||||
// Release the fingers.
|
||||
MultiTouchInput fingersUp = secondFingerDown;
|
||||
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
|
||||
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
|
||||
|
||||
// Allow any animations to run their course.
|
||||
child->AdvanceAnimationsUntilEnd();
|
||||
rootApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Make sure nothing is overscrolled.
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_FALSE(rootApzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
// This is almost exactly like StuckInOverscroll_Bug1073250, except the
|
||||
// APZC receiving the input events for the first touch block is the child
|
||||
// (and thus not the same APZC that overscrolls, which is the parent).
|
||||
TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) {
|
||||
// Enable overscrolling.
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
|
||||
|
||||
// Pan, causing the parent APZC to overscroll.
|
||||
Pan(manager, 60, 90, true /* keep finger down */);
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_TRUE(rootApzc->IsOverscrolled());
|
||||
|
||||
// Put a second finger down.
|
||||
MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
// Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
|
||||
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
|
||||
|
||||
// Release the fingers.
|
||||
MultiTouchInput fingersUp = secondFingerDown;
|
||||
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
|
||||
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
|
||||
|
||||
// Allow any animations to run their course.
|
||||
child->AdvanceAnimationsUntilEnd();
|
||||
rootApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Make sure nothing is overscrolled.
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_FALSE(rootApzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) {
|
||||
// Enable overscrolling.
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
|
||||
|
||||
// Pan, causing the parent APZC to overscroll.
|
||||
Pan(manager, 60, 90, true /* keep finger down */);
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_TRUE(rootApzc->IsOverscrolled());
|
||||
|
||||
// Lift the finger, triggering an overscroll animation
|
||||
// (but don't allow it to run).
|
||||
TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
|
||||
|
||||
// Put the finger down again, interrupting the animation
|
||||
// and entering the TOUCHING state.
|
||||
TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
|
||||
|
||||
// Lift the finger once again.
|
||||
TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
|
||||
|
||||
// Allow any animations to run their course.
|
||||
child->AdvanceAnimationsUntilEnd();
|
||||
rootApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Make sure nothing is overscrolled.
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_FALSE(rootApzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) {
|
||||
// Enable overscrolling.
|
||||
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
|
||||
|
||||
// Pan, causing the parent APZC to overscroll.
|
||||
Pan(manager, 60, 90, true /* keep finger down */);
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_TRUE(rootApzc->IsOverscrolled());
|
||||
|
||||
// Lift the finger, triggering an overscroll animation
|
||||
// (but don't allow it to run).
|
||||
TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
|
||||
|
||||
// Put the finger down again, interrupting the animation
|
||||
// and entering the TOUCHING state.
|
||||
TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
|
||||
|
||||
// Put a second finger down. Since we're in the TOUCHING state,
|
||||
// the "are we panned into overscroll" check will fail and we
|
||||
// will not ignore the second finger, instead entering the
|
||||
// PINCHING state.
|
||||
MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
|
||||
// Use the same touch identifier for the first touch (0) as TouchDown(). (A bit hacky.)
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0));
|
||||
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0));
|
||||
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
|
||||
|
||||
// Release the fingers.
|
||||
MultiTouchInput fingersUp = secondFingerDown;
|
||||
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
|
||||
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
|
||||
|
||||
// Allow any animations to run their course.
|
||||
child->AdvanceAnimationsUntilEnd();
|
||||
rootApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Make sure nothing is overscrolled.
|
||||
EXPECT_FALSE(child->IsOverscrolled());
|
||||
EXPECT_FALSE(rootApzc->IsOverscrolled());
|
||||
}
|
||||
|
||||
// Test that flinging in a direction where one component of the fling goes into
|
||||
// overscroll but the other doesn't, results in just the one component being
|
||||
// handed off to the parent, while the original APZC continues flinging in the
|
||||
// other direction.
|
||||
TEST_F(APZScrollHandoffTester, PartialFlingHandoff) {
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
// Fling up and to the left. The child APZC has room to scroll up, but not
|
||||
// to the left, so the horizontal component of the fling should be handed
|
||||
// off to the parent APZC.
|
||||
Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55));
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root);
|
||||
RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]);
|
||||
|
||||
// Advance the child's fling animation once to give the partial handoff
|
||||
// a chance to occur.
|
||||
mcc->AdvanceByMillis(10);
|
||||
child->AdvanceAnimations(mcc->Time());
|
||||
|
||||
// Assert that partial handoff has occurred.
|
||||
child->AssertStateIsFling();
|
||||
parent->AssertStateIsFling();
|
||||
}
|
||||
|
||||
// Here we test that if two flings are happening simultaneously, overscroll
|
||||
// is handed off correctly for each.
|
||||
TEST_F(APZScrollHandoffTester, SimultaneousFlings) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
|
||||
// Set up an initial APZC tree.
|
||||
CreateScrollHandoffLayerTree3();
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]);
|
||||
RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]);
|
||||
RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]);
|
||||
RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]);
|
||||
|
||||
// Pan on the lower child.
|
||||
Pan(child2, 45, 5);
|
||||
|
||||
// Pan on the upper child.
|
||||
Pan(child1, 95, 55);
|
||||
|
||||
// Check that child1 and child2 are in a FLING state.
|
||||
child1->AssertStateIsFling();
|
||||
child2->AssertStateIsFling();
|
||||
|
||||
// Advance the animations on child1 and child2 until their end.
|
||||
child1->AdvanceAnimationsUntilEnd();
|
||||
child2->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Check that the flings have been handed off to the parents.
|
||||
child1->AssertStateIsReset();
|
||||
parent1->AssertStateIsFling();
|
||||
child2->AssertStateIsReset();
|
||||
parent2->AssertStateIsFling();
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, Scrollgrab) {
|
||||
// Set up the layer tree
|
||||
CreateScrollgrabLayerTree();
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Pan on the child, enough to fully scroll the scrollgrab parent (20 px)
|
||||
// and leave some more (another 15 px) for the child.
|
||||
Pan(childApzc, 80, 45);
|
||||
|
||||
// Check that the parent and child have scrolled as much as we expect.
|
||||
EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, ScrollgrabFling) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
// Set up the layer tree
|
||||
CreateScrollgrabLayerTree();
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Pan on the child, not enough to fully scroll the scrollgrab parent.
|
||||
Pan(childApzc, 80, 70);
|
||||
|
||||
// Check that it is the scrollgrab parent that's in a fling, not the child.
|
||||
rootApzc->AssertStateIsFling();
|
||||
childApzc->AssertStateIsReset();
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
CreateScrollgrabLayerTree(true /* make parent scrollable */);
|
||||
TestFlingAcceleration();
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) {
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
CreateScrollgrabLayerTree(false /* do not make parent scrollable */);
|
||||
TestFlingAcceleration();
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
|
||||
SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
|
||||
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Pan on the child, enough to scroll it to its end and have scroll
|
||||
// left to hand off. Since immediate handoff is disallowed, we expect
|
||||
// the leftover scroll not to be handed off.
|
||||
Pan(childApzc, 60, 5);
|
||||
|
||||
// Verify that the parent has not scrolled.
|
||||
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
|
||||
// Pan again on the child. This time, since the child was scrolled to
|
||||
// its end when the gesture began, we expect the scroll to be handed off.
|
||||
Pan(childApzc, 60, 50);
|
||||
|
||||
// Verify that the parent scrolled.
|
||||
EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
}
|
||||
|
||||
TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
|
||||
SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
|
||||
SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f);
|
||||
|
||||
CreateScrollHandoffLayerTree1();
|
||||
|
||||
RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
|
||||
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
|
||||
|
||||
// Pan on the child, enough to get very close to the end, so that the
|
||||
// subsequent fling reaches the end and has leftover velocity to hand off.
|
||||
Pan(childApzc, 60, 12);
|
||||
|
||||
// Allow the fling to run its course.
|
||||
childApzc->AdvanceAnimationsUntilEnd();
|
||||
parentApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Verify that the parent has not scrolled.
|
||||
// The first comparison needs to be an ASSERT_NEAR because the fling
|
||||
// computations are such that the final scroll position can be within
|
||||
// COORDINATE_EPSILON of the end rather than right at the end.
|
||||
ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetScrollOffset().y, COORDINATE_EPSILON);
|
||||
EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
|
||||
|
||||
// Pan again on the child. This time, since the child was scrolled to
|
||||
// its end when the gesture began, we expect the scroll to be handed off.
|
||||
Pan(childApzc, 60, 50);
|
||||
|
||||
// Allow the fling to run its course. The fling should also be handed off.
|
||||
childApzc->AdvanceAnimationsUntilEnd();
|
||||
parentApzc->AdvanceAnimationsUntilEnd();
|
||||
|
||||
// Verify that the parent scrolled from the fling.
|
||||
EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10);
|
||||
}
|
||||
64
gfx/layers/apz/test/gtest/TestSnapping.cpp
Normal file
64
gfx/layers/apz/test/gtest/TestSnapping.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
class APZCSnappingTester : public APZCTreeManagerTester
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(APZCSnappingTester, Bug1265510)
|
||||
{
|
||||
const char* layerTreeSyntax = "c(t)";
|
||||
nsIntRegion layerVisibleRegion[] = {
|
||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||
nsIntRegion(IntRect(0, 100, 100, 100))
|
||||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
|
||||
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200));
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
|
||||
SetScrollHandoff(layers[1], root);
|
||||
|
||||
ScrollSnapInfo snap;
|
||||
snap.mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY;
|
||||
snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel());
|
||||
|
||||
ScrollMetadata metadata = root->GetScrollMetadata(0);
|
||||
metadata.SetSnapInfo(ScrollSnapInfo(snap));
|
||||
root->SetScrollMetadata(metadata);
|
||||
|
||||
UniquePtr<ScopedLayerTreeRegistration> registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
TestAsyncPanZoomController* outer = ApzcOf(layers[0]);
|
||||
TestAsyncPanZoomController* inner = ApzcOf(layers[1]);
|
||||
|
||||
// Position the mouse near the bottom of the outer frame and scroll by 60px.
|
||||
// (6 lines of 10px each). APZC will actually scroll to y=100 because of the
|
||||
// mandatory snap coordinate there.
|
||||
TimeStamp now = mcc->Time();
|
||||
SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), now);
|
||||
// Advance in 5ms increments until we've scrolled by 70px. At this point, the
|
||||
// closest snap point is y=100, and the inner frame should be under the mouse
|
||||
// cursor.
|
||||
while (outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y < 70) {
|
||||
mcc->AdvanceByMillis(5);
|
||||
outer->AdvanceAnimations(mcc->Time());
|
||||
}
|
||||
// Now do another wheel in a new transaction. This should start scrolling the
|
||||
// inner frame; we verify that it does by checking the inner scroll position.
|
||||
TimeStamp newTransactionTime = now + TimeDuration::FromMilliseconds(gfxPrefs::MouseWheelTransactionTimeoutMs() + 100);
|
||||
SmoothWheel(manager, ScreenIntPoint(50, 80), ScreenPoint(0, 6), newTransactionTime);
|
||||
inner->AdvanceAnimationsUntilEnd();
|
||||
EXPECT_LT(0.0f, inner->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
|
||||
|
||||
// However, the outer frame should also continue to the snap point, otherwise
|
||||
// it is demonstrating incorrect behaviour by violating the mandatory snapping.
|
||||
outer->AdvanceAnimationsUntilEnd();
|
||||
EXPECT_EQ(100.0f, outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncMode::NORMAL).y);
|
||||
}
|
||||
112
gfx/layers/apz/test/gtest/TestTreeManager.cpp
Normal file
112
gfx/layers/apz/test/gtest/TestTreeManager.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et 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 "APZCTreeManagerTester.h"
|
||||
#include "APZTestCommon.h"
|
||||
#include "InputUtils.h"
|
||||
|
||||
TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
|
||||
CreateSimpleMultiLayerTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
|
||||
// both layers have the same scrollId
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
TestAsyncPanZoomController* nullAPZC = nullptr;
|
||||
// so they should have the same APZC
|
||||
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
|
||||
EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
|
||||
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
|
||||
|
||||
// Change the scrollId of layers[1], and verify the APZC changes
|
||||
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[2]));
|
||||
|
||||
// Change the scrollId of layers[2] to match that of layers[1], ensure we get the same
|
||||
// APZC for both again
|
||||
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
|
||||
}
|
||||
|
||||
TEST_F(APZCTreeManagerTester, Bug1068268) {
|
||||
CreatePotentiallyLeakingTree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
|
||||
RefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
|
||||
RefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
|
||||
|
||||
EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
|
||||
EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
|
||||
EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
|
||||
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
|
||||
|
||||
EXPECT_EQ(node2->GetFirstChild(), node2->GetLastChild());
|
||||
EXPECT_EQ(ApzcOf(layers[3]), node2->GetLastChild()->GetApzc());
|
||||
EXPECT_EQ(node5->GetFirstChild(), node5->GetLastChild());
|
||||
EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->GetApzc());
|
||||
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
|
||||
EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
|
||||
}
|
||||
|
||||
TEST_F(APZCTreeManagerTester, Bug1194876) {
|
||||
CreateBug1194876Tree();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
uint64_t blockId;
|
||||
nsTArray<ScrollableLayerGuid> targets;
|
||||
|
||||
// First touch goes down, APZCTM will hit layers[1] because it is on top of
|
||||
// layers[0], but we tell it the real target APZC is layers[0].
|
||||
MultiTouchInput mti;
|
||||
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
|
||||
mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(25, 50), ScreenSize(0, 0), 0, 0));
|
||||
manager->ReceiveInputEvent(mti, nullptr, &blockId);
|
||||
manager->ContentReceivedInputBlock(blockId, false);
|
||||
targets.AppendElement(ApzcOf(layers[0])->GetGuid());
|
||||
manager->SetTargetAPZC(blockId, targets);
|
||||
|
||||
// Around here, the above touch will get processed by ApzcOf(layers[0])
|
||||
|
||||
// Second touch goes down (first touch remains down), APZCTM will again hit
|
||||
// layers[1]. Again we tell it both touches landed on layers[0], but because
|
||||
// layers[1] is the RCD layer, it will end up being the multitouch target.
|
||||
mti.mTouches.AppendElement(SingleTouchData(1, ParentLayerPoint(75, 50), ScreenSize(0, 0), 0, 0));
|
||||
manager->ReceiveInputEvent(mti, nullptr, &blockId);
|
||||
manager->ContentReceivedInputBlock(blockId, false);
|
||||
targets.AppendElement(ApzcOf(layers[0])->GetGuid());
|
||||
manager->SetTargetAPZC(blockId, targets);
|
||||
|
||||
// Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
|
||||
// We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
|
||||
// otherwise it will do things like dispatch spurious long-tap events.
|
||||
|
||||
EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
|
||||
}
|
||||
|
||||
TEST_F(APZCTreeManagerTester, Bug1198900) {
|
||||
// This is just a test that cancels a wheel event to make sure it doesn't
|
||||
// crash.
|
||||
CreateSimpleDTCScrollingLayer();
|
||||
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
|
||||
manager->UpdateHitTestingTree(0, root, false, 0, 0);
|
||||
|
||||
ScreenPoint origin(100, 50);
|
||||
ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
|
||||
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
|
||||
origin, 0, 10, false);
|
||||
uint64_t blockId;
|
||||
manager->ReceiveInputEvent(swi, nullptr, &blockId);
|
||||
manager->ContentReceivedInputBlock(blockId, /* preventDefault= */ true);
|
||||
}
|
||||
|
||||
33
gfx/layers/apz/test/gtest/moz.build
Normal file
33
gfx/layers/apz/test/gtest/moz.build
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestBasic.cpp',
|
||||
'TestEventRegions.cpp',
|
||||
'TestGestureDetector.cpp',
|
||||
'TestHitTesting.cpp',
|
||||
'TestInputQueue.cpp',
|
||||
'TestPanning.cpp',
|
||||
'TestPinching.cpp',
|
||||
'TestScrollHandoff.cpp',
|
||||
'TestSnapping.cpp',
|
||||
'TestTreeManager.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/gfx/2d',
|
||||
'/gfx/layers',
|
||||
'/gfx/tests/gtest' # for TestLayers.h, which is shared with the gfx gtests
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
261
gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
Normal file
261
gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
// Utilities for synthesizing of native events.
|
||||
|
||||
function getPlatform() {
|
||||
if (navigator.platform.indexOf("Win") == 0) {
|
||||
return "windows";
|
||||
}
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
return "mac";
|
||||
}
|
||||
// Check for Android before Linux
|
||||
if (navigator.appVersion.indexOf("Android") >= 0) {
|
||||
return "android"
|
||||
}
|
||||
if (navigator.platform.indexOf("Linux") == 0) {
|
||||
return "linux";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function nativeVerticalWheelEventMsg() {
|
||||
switch (getPlatform()) {
|
||||
case "windows": return 0x020A; // WM_MOUSEWHEEL
|
||||
case "mac": return 0; // value is unused, can be anything
|
||||
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
|
||||
}
|
||||
throw "Native wheel events not supported on platform " + getPlatform();
|
||||
}
|
||||
|
||||
function nativeHorizontalWheelEventMsg() {
|
||||
switch (getPlatform()) {
|
||||
case "windows": return 0x020E; // WM_MOUSEHWHEEL
|
||||
case "mac": return 0; // value is unused, can be anything
|
||||
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
|
||||
}
|
||||
throw "Native wheel events not supported on platform " + getPlatform();
|
||||
}
|
||||
|
||||
// Given a pixel scrolling delta, converts it to the platform's native units.
|
||||
function nativeScrollUnits(aElement, aDimen) {
|
||||
switch (getPlatform()) {
|
||||
case "linux": {
|
||||
// GTK deltas are treated as line height divided by 3 by gecko.
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"];
|
||||
return aDimen / (parseInt(lineHeight) * 3);
|
||||
}
|
||||
}
|
||||
return aDimen;
|
||||
}
|
||||
|
||||
function nativeMouseDownEventMsg() {
|
||||
switch (getPlatform()) {
|
||||
case "windows": return 2; // MOUSEEVENTF_LEFTDOWN
|
||||
case "mac": return 1; // NSLeftMouseDown
|
||||
case "linux": return 4; // GDK_BUTTON_PRESS
|
||||
case "android": return 5; // ACTION_POINTER_DOWN
|
||||
}
|
||||
throw "Native mouse-down events not supported on platform " + getPlatform();
|
||||
}
|
||||
|
||||
function nativeMouseMoveEventMsg() {
|
||||
switch (getPlatform()) {
|
||||
case "windows": return 1; // MOUSEEVENTF_MOVE
|
||||
case "mac": return 5; // NSMouseMoved
|
||||
case "linux": return 3; // GDK_MOTION_NOTIFY
|
||||
case "android": return 7; // ACTION_HOVER_MOVE
|
||||
}
|
||||
throw "Native mouse-move events not supported on platform " + getPlatform();
|
||||
}
|
||||
|
||||
function nativeMouseUpEventMsg() {
|
||||
switch (getPlatform()) {
|
||||
case "windows": return 4; // MOUSEEVENTF_LEFTUP
|
||||
case "mac": return 2; // NSLeftMouseUp
|
||||
case "linux": return 7; // GDK_BUTTON_RELEASE
|
||||
case "android": return 6; // ACTION_POINTER_UP
|
||||
}
|
||||
throw "Native mouse-up events not supported on platform " + getPlatform();
|
||||
}
|
||||
|
||||
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
|
||||
// to device pixels relative to the screen.
|
||||
function coordinatesRelativeToScreen(aX, aY, aElement) {
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
var scale = targetWindow.devicePixelRatio;
|
||||
var rect = aElement.getBoundingClientRect();
|
||||
return {
|
||||
x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale,
|
||||
y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale
|
||||
};
|
||||
}
|
||||
|
||||
// Get the bounding box of aElement, and return it in device pixels
|
||||
// relative to the screen.
|
||||
function rectRelativeToScreen(aElement) {
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
var scale = targetWindow.devicePixelRatio;
|
||||
var rect = aElement.getBoundingClientRect();
|
||||
return {
|
||||
x: (targetWindow.mozInnerScreenX + rect.left) * scale,
|
||||
y: (targetWindow.mozInnerScreenY + rect.top) * scale,
|
||||
w: (rect.width * scale),
|
||||
h: (rect.height * scale)
|
||||
};
|
||||
}
|
||||
|
||||
// Synthesizes a native mousewheel event and returns immediately. This does not
|
||||
// guarantee anything; you probably want to use one of the other functions below
|
||||
// which actually wait for results.
|
||||
// aX and aY are relative to the top-left of |aElement|'s containing window.
|
||||
// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined
|
||||
// if not needed.
|
||||
function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
if (aDeltaX && aDeltaY) {
|
||||
throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
|
||||
}
|
||||
aDeltaX = nativeScrollUnits(aElement, aDeltaX);
|
||||
aDeltaY = nativeScrollUnits(aElement, aDeltaY);
|
||||
var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Synthesizes a native mousewheel event and invokes the callback once the
|
||||
// request has been successfully made to the OS. This does not necessarily
|
||||
// guarantee that the OS generates the event we requested. See
|
||||
// synthesizeNativeWheel for details on the parameters.
|
||||
function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
|
||||
var observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aCallback && aTopic == "mousescrollevent") {
|
||||
setTimeout(aCallback, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer);
|
||||
}
|
||||
|
||||
// Synthesizes a native mousewheel event and invokes the callback once the
|
||||
// wheel event is dispatched to |aElement|'s containing window. If the event
|
||||
// targets content in a subdocument, |aElement| should be inside the
|
||||
// subdocument. See synthesizeNativeWheel for details on the other parameters.
|
||||
function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
targetWindow.addEventListener("wheel", function wheelWaiter(e) {
|
||||
targetWindow.removeEventListener("wheel", wheelWaiter);
|
||||
setTimeout(aCallback, 0);
|
||||
});
|
||||
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
|
||||
}
|
||||
|
||||
// Synthesizes a native mousewheel event and invokes the callback once the
|
||||
// first resulting scroll event is dispatched to |aElement|'s containing window.
|
||||
// If the event targets content in a subdocument, |aElement| should be inside
|
||||
// the subdocument. See synthesizeNativeWheel for details on the other
|
||||
// parameters.
|
||||
function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
var useCapture = true; // scroll events don't always bubble
|
||||
targetWindow.addEventListener("scroll", function scrollWaiter(e) {
|
||||
targetWindow.removeEventListener("scroll", scrollWaiter, useCapture);
|
||||
setTimeout(aCallback, 0);
|
||||
}, useCapture);
|
||||
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
|
||||
}
|
||||
|
||||
// Synthesizes a native mouse move event and returns immediately.
|
||||
// aX and aY are relative to the top-left of |aElement|'s containing window.
|
||||
function synthesizeNativeMouseMove(aElement, aX, aY) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Synthesizes a native mouse move event and invokes the callback once the
|
||||
// mouse move event is dispatched to |aElement|'s containing window. If the event
|
||||
// targets content in a subdocument, |aElement| should be inside the
|
||||
// subdocument. See synthesizeNativeMouseMove for details on the other
|
||||
// parameters.
|
||||
function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) {
|
||||
var targetWindow = aElement.ownerDocument.defaultView;
|
||||
targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
|
||||
targetWindow.removeEventListener("mousemove", mousemoveWaiter);
|
||||
setTimeout(aCallback, 0);
|
||||
});
|
||||
return synthesizeNativeMouseMove(aElement, aX, aY);
|
||||
}
|
||||
|
||||
// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
|
||||
// relative to the top-left of |aElement|'s bounding rect.
|
||||
function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
// A handy constant when synthesizing native touch drag events with the pref
|
||||
// "apz.touch_start_tolerance" set to 0. In this case, the first touchmove with
|
||||
// a nonzero pixel movement is consumed by the APZ to transition from the
|
||||
// "touching" state to the "panning" state, so calls to synthesizeNativeTouchDrag
|
||||
// should add an extra pixel pixel for this purpose. The TOUCH_SLOP provides
|
||||
// a constant that can be used for this purpose. Note that if the touch start
|
||||
// tolerance is set to something higher, the touch slop amount used must be
|
||||
// correspondingly increased so as to be higher than the tolerance.
|
||||
const TOUCH_SLOP = 1;
|
||||
function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
|
||||
synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
|
||||
var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY));
|
||||
for (var i = 1; i < steps; i++) {
|
||||
var dx = i * (aDeltaX / steps);
|
||||
var dy = i * (aDeltaY / steps);
|
||||
synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
|
||||
}
|
||||
synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
|
||||
return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId);
|
||||
}
|
||||
|
||||
function synthesizeNativeTap(aElement, aX, aY, aObserver = null) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
function synthesizeNativeMouseEvent(aElement, aX, aY, aType, aObserver = null) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeMouseEvent(pt.x, pt.y, aType, 0, aElement, aObserver);
|
||||
return true;
|
||||
}
|
||||
|
||||
function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
|
||||
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
|
||||
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
|
||||
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseDownEventMsg(), 0, aElement, function() {
|
||||
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseUpEventMsg(), 0, aElement, aObserver);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move the mouse to (dx, dy) relative to |element|, and scroll the wheel
|
||||
// at that location.
|
||||
// Moving the mouse is necessary to avoid wheel events from two consecutive
|
||||
// moveMouseAndScrollWheelOver() calls on different elements being incorrectly
|
||||
// considered as part of the same wheel transaction.
|
||||
// We also wait for the mouse move event to be processed before sending the
|
||||
// wheel event, otherwise there is a chance they might get reordered, and
|
||||
// we have the transaction problem again.
|
||||
function moveMouseAndScrollWheelOver(element, dx, dy, testDriver, waitForScroll = true) {
|
||||
return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
|
||||
if (waitForScroll) {
|
||||
synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
|
||||
} else {
|
||||
synthesizeNativeWheelAndWaitForWheelEvent(element, dx, dy, 0, -10, testDriver);
|
||||
}
|
||||
});
|
||||
}
|
||||
403
gfx/layers/apz/test/mochitest/apz_test_utils.js
Normal file
403
gfx/layers/apz/test/mochitest/apz_test_utils.js
Normal file
|
|
@ -0,0 +1,403 @@
|
|||
// Utilities for writing APZ tests using the framework added in bug 961289
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Functions that convert the APZ test data into a more usable form.
|
||||
// Every place we have a WebIDL sequence whose elements are dictionaries
|
||||
// with two elements, a key, and a value, we convert this into a JS
|
||||
// object with a property for each key/value pair. (This is the structure
|
||||
// we really want, but we can't express in directly in WebIDL.)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
function convertEntries(entries) {
|
||||
var result = {};
|
||||
for (var i = 0; i < entries.length; ++i) {
|
||||
result[entries[i].key] = entries[i].value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getPropertyAsRect(scrollFrames, scrollId, prop) {
|
||||
SimpleTest.ok(scrollId in scrollFrames,
|
||||
'expected scroll frame data for scroll id ' + scrollId);
|
||||
var scrollFrameData = scrollFrames[scrollId];
|
||||
SimpleTest.ok('displayport' in scrollFrameData,
|
||||
'expected a ' + prop + ' for scroll id ' + scrollId);
|
||||
var value = scrollFrameData[prop];
|
||||
var pieces = value.replace(/[()\s]+/g, '').split(',');
|
||||
SimpleTest.is(pieces.length, 4, "expected string of form (x,y,w,h)");
|
||||
return { x: parseInt(pieces[0]),
|
||||
y: parseInt(pieces[1]),
|
||||
w: parseInt(pieces[2]),
|
||||
h: parseInt(pieces[3]) };
|
||||
}
|
||||
|
||||
function convertScrollFrameData(scrollFrames) {
|
||||
var result = {};
|
||||
for (var i = 0; i < scrollFrames.length; ++i) {
|
||||
result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertBuckets(buckets) {
|
||||
var result = {};
|
||||
for (var i = 0; i < buckets.length; ++i) {
|
||||
result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertTestData(testData) {
|
||||
var result = {};
|
||||
result.paints = convertBuckets(testData.paints);
|
||||
result.repaintRequests = convertBuckets(testData.repaintRequests);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given APZ test data for a single paint on the compositor side,
|
||||
// reconstruct the APZC tree structure from the 'parentScrollId'
|
||||
// entries that were logged. More specifically, the subset of the
|
||||
// APZC tree structure corresponding to the layer subtree for the
|
||||
// content process that triggered the paint, is reconstructed (as
|
||||
// the APZ test data only contains information abot this subtree).
|
||||
function buildApzcTree(paint) {
|
||||
// The APZC tree can potentially have multiple root nodes,
|
||||
// so we invent a node that is the parent of all roots.
|
||||
// This 'root' does not correspond to an APZC.
|
||||
var root = {scrollId: -1, children: []};
|
||||
for (var scrollId in paint) {
|
||||
paint[scrollId].children = [];
|
||||
paint[scrollId].scrollId = scrollId;
|
||||
}
|
||||
for (var scrollId in paint) {
|
||||
var parentNode = null;
|
||||
if ("hasNoParentWithSameLayersId" in paint[scrollId]) {
|
||||
parentNode = root;
|
||||
} else if ("parentScrollId" in paint[scrollId]) {
|
||||
parentNode = paint[paint[scrollId].parentScrollId];
|
||||
}
|
||||
parentNode.children.push(paint[scrollId]);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
// Given an APZC tree produced by buildApzcTree, return the RCD node in
|
||||
// the tree, or null if there was none.
|
||||
function findRcdNode(apzcTree) {
|
||||
if (!!apzcTree.isRootContent) { // isRootContent will be undefined or "1"
|
||||
return apzcTree;
|
||||
}
|
||||
for (var i = 0; i < apzcTree.children.length; i++) {
|
||||
var rcd = findRcdNode(apzcTree.children[i]);
|
||||
if (rcd != null) {
|
||||
return rcd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return whether an element whose id includes |elementId| has been layerized.
|
||||
// Assumes |elementId| will be present in the content description for the
|
||||
// element, and not in the content descriptions of other elements.
|
||||
function isLayerized(elementId) {
|
||||
var contentTestData = SpecialPowers.getDOMWindowUtils(window).getContentAPZTestData();
|
||||
ok(contentTestData.paints.length > 0, "expected at least one paint");
|
||||
var seqno = contentTestData.paints[contentTestData.paints.length - 1].sequenceNumber;
|
||||
contentTestData = convertTestData(contentTestData);
|
||||
var paint = contentTestData.paints[seqno];
|
||||
for (var scrollId in paint) {
|
||||
if ("contentDescription" in paint[scrollId]) {
|
||||
if (paint[scrollId]["contentDescription"].includes(elementId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function flushApzRepaints(aCallback, aWindow = window) {
|
||||
if (!aCallback) {
|
||||
throw "A callback must be provided!";
|
||||
}
|
||||
var repaintDone = function() {
|
||||
SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
|
||||
setTimeout(aCallback, 0);
|
||||
};
|
||||
SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
|
||||
if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) {
|
||||
dump("Flushed APZ repaints, waiting for callback...\n");
|
||||
} else {
|
||||
dump("Flushing APZ repaints was a no-op, triggering callback directly...\n");
|
||||
repaintDone();
|
||||
}
|
||||
}
|
||||
|
||||
// Flush repaints, APZ pending repaints, and any repaints resulting from that
|
||||
// flush. This is particularly useful if the test needs to reach some sort of
|
||||
// "idle" state in terms of repaints. Usually just doing waitForAllPaints
|
||||
// followed by flushApzRepaints is sufficient to flush all APZ state back to
|
||||
// the main thread, but it can leave a paint scheduled which will get triggered
|
||||
// at some later time. For tests that specifically test for painting at
|
||||
// specific times, this method is the way to go. Even if in doubt, this is the
|
||||
// preferred method as the extra step is "safe" and shouldn't interfere with
|
||||
// most tests.
|
||||
function waitForApzFlushedRepaints(aCallback) {
|
||||
// First flush the main-thread paints and send transactions to the APZ
|
||||
waitForAllPaints(function() {
|
||||
// Then flush the APZ to make sure any repaint requests have been sent
|
||||
// back to the main thread
|
||||
flushApzRepaints(function() {
|
||||
// Then flush the main-thread again to process the repaint requests.
|
||||
// Once this is done, we should be in a stable state with nothing
|
||||
// pending, so we can trigger the callback.
|
||||
waitForAllPaints(aCallback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// This function takes a set of subtests to run one at a time in new top-level
|
||||
// windows, and returns a Promise that is resolved once all the subtests are
|
||||
// done running.
|
||||
//
|
||||
// The aSubtests array is an array of objects with the following keys:
|
||||
// file: required, the filename of the subtest.
|
||||
// prefs: optional, an array of arrays containing key-value prefs to set.
|
||||
// dp_suppression: optional, a boolean on whether or not to respect displayport
|
||||
// suppression during the test.
|
||||
// onload: optional, a function that will be registered as a load event listener
|
||||
// for the child window that will hold the subtest. the function will be
|
||||
// passed exactly one argument, which will be the child window.
|
||||
// An example of an array is:
|
||||
// aSubtests = [
|
||||
// { 'file': 'test_file_name.html' },
|
||||
// { 'file': 'test_file_2.html', 'prefs': [['pref.name', true], ['other.pref', 1000]], 'dp_suppression': false }
|
||||
// { 'file': 'file_3.html', 'onload': function(w) { w.subtestDone(); } }
|
||||
// ];
|
||||
//
|
||||
// Each subtest should call the subtestDone() function when it is done, to
|
||||
// indicate that the window should be torn down and the next text should run.
|
||||
// The subtestDone() function is injected into the subtest's window by this
|
||||
// function prior to loading the subtest. For convenience, the |is| and |ok|
|
||||
// functions provided by SimpleTest are also mapped into the subtest's window.
|
||||
// For other things from the parent, the subtest can use window.opener.<whatever>
|
||||
// to access objects.
|
||||
function runSubtestsSeriallyInFreshWindows(aSubtests) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var testIndex = -1;
|
||||
var w = null;
|
||||
|
||||
function advanceSubtestExecution() {
|
||||
var test = aSubtests[testIndex];
|
||||
if (w) {
|
||||
if (typeof test.dp_suppression != 'undefined') {
|
||||
// We modified the suppression when starting the test, so now undo that.
|
||||
SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(!test.dp_suppression);
|
||||
}
|
||||
if (test.prefs) {
|
||||
// We pushed some prefs for this test, pop them, and re-invoke
|
||||
// advanceSubtestExecution() after that's been processed
|
||||
SpecialPowers.popPrefEnv(function() {
|
||||
w.close();
|
||||
w = null;
|
||||
advanceSubtestExecution();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
w.close();
|
||||
}
|
||||
|
||||
testIndex++;
|
||||
if (testIndex >= aSubtests.length) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
test = aSubtests[testIndex];
|
||||
if (typeof test.dp_suppression != 'undefined') {
|
||||
// Normally during a test, the displayport will get suppressed during page
|
||||
// load, and unsuppressed at a non-deterministic time during the test. The
|
||||
// unsuppression can trigger a repaint which interferes with the test, so
|
||||
// to avoid that we can force the displayport to be unsuppressed for the
|
||||
// entire test which is more deterministic.
|
||||
SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(test.dp_suppression);
|
||||
}
|
||||
|
||||
function spawnTest(aFile) {
|
||||
w = window.open('', "_blank");
|
||||
w.subtestDone = advanceSubtestExecution;
|
||||
w.SimpleTest = SimpleTest;
|
||||
w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
|
||||
w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
|
||||
if (test.onload) {
|
||||
w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
|
||||
}
|
||||
w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
|
||||
return w;
|
||||
}
|
||||
|
||||
if (test.prefs) {
|
||||
// Got some prefs for this subtest, push them
|
||||
SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
|
||||
w = spawnTest(test.file);
|
||||
});
|
||||
} else {
|
||||
w = spawnTest(test.file);
|
||||
}
|
||||
}
|
||||
|
||||
advanceSubtestExecution();
|
||||
});
|
||||
}
|
||||
|
||||
function pushPrefs(prefs) {
|
||||
return SpecialPowers.pushPrefEnv({'set': prefs});
|
||||
}
|
||||
|
||||
function waitUntilApzStable() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
SimpleTest.waitForFocus(function() {
|
||||
waitForAllPaints(function() {
|
||||
flushApzRepaints(resolve);
|
||||
});
|
||||
}, window);
|
||||
});
|
||||
}
|
||||
|
||||
function isApzEnabled() {
|
||||
var enabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
|
||||
if (!enabled) {
|
||||
// All tests are required to have at least one assertion. Since APZ is
|
||||
// disabled, and the main test is presumably not going to run, we stick in
|
||||
// a dummy assertion here to keep the test passing.
|
||||
SimpleTest.ok(true, "APZ is not enabled; this test will be skipped");
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// Despite what this function name says, this does not *directly* run the
|
||||
// provided continuation testFunction. Instead, it returns a function that
|
||||
// can be used to run the continuation. The extra level of indirection allows
|
||||
// it to be more easily added to a promise chain, like so:
|
||||
// waitUntilApzStable().then(runContinuation(myTest));
|
||||
//
|
||||
// If you want to run the continuation directly, outside of a promise chain,
|
||||
// you can invoke the return value of this function, like so:
|
||||
// runContinuation(myTest)();
|
||||
function runContinuation(testFunction) {
|
||||
// We need to wrap this in an extra function, so that the call site can
|
||||
// be more readable without running the promise too early. In other words,
|
||||
// if we didn't have this extra function, the promise would start running
|
||||
// during construction of the promise chain, concurrently with the first
|
||||
// promise in the chain.
|
||||
return function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var testContinuation = null;
|
||||
|
||||
function driveTest() {
|
||||
if (!testContinuation) {
|
||||
testContinuation = testFunction(driveTest);
|
||||
}
|
||||
var ret = testContinuation.next();
|
||||
if (ret.done) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
driveTest();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Take a snapshot of the given rect, *including compositor transforms* (i.e.
|
||||
// includes async scroll transforms applied by APZ). If you don't need the
|
||||
// compositor transforms, you can probably get away with using
|
||||
// SpecialPowers.snapshotWindowWithOptions or one of the friendlier wrappers.
|
||||
// The rect provided is expected to be relative to the screen, for example as
|
||||
// returned by rectRelativeToScreen in apz_test_native_event_utils.js.
|
||||
// Example usage:
|
||||
// var snapshot = getSnapshot(rectRelativeToScreen(myDiv));
|
||||
// which will take a snapshot of the 'myDiv' element. Note that if part of the
|
||||
// element is obscured by other things on top, the snapshot will include those
|
||||
// things. If it is clipped by a scroll container, the snapshot will include
|
||||
// that area anyway, so you will probably get parts of the scroll container in
|
||||
// the snapshot. If the rect extends outside the browser window then the
|
||||
// results are undefined.
|
||||
// The snapshot is returned in the form of a data URL.
|
||||
function getSnapshot(rect) {
|
||||
function parentProcessSnapshot() {
|
||||
addMessageListener('snapshot', function(rect) {
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
|
||||
// reposition the rect relative to the top-level browser window
|
||||
rect = JSON.parse(rect);
|
||||
rect.x -= topWin.mozInnerScreenX;
|
||||
rect.y -= topWin.mozInnerScreenY;
|
||||
|
||||
// take the snapshot
|
||||
var canvas = topWin.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
canvas.width = rect.w;
|
||||
canvas.height = rect.h;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawWindow(topWin, rect.x, rect.y, rect.w, rect.h, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_CARET);
|
||||
return canvas.toDataURL();
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof getSnapshot.chromeHelper == 'undefined') {
|
||||
// This is the first time getSnapshot is being called; do initialization
|
||||
getSnapshot.chromeHelper = SpecialPowers.loadChromeScript(parentProcessSnapshot);
|
||||
SimpleTest.registerCleanupFunction(function() { getSnapshot.chromeHelper.destroy() });
|
||||
}
|
||||
|
||||
return getSnapshot.chromeHelper.sendSyncMessage('snapshot', JSON.stringify(rect)).toString();
|
||||
}
|
||||
|
||||
// Takes the document's query string and parses it, assuming the query string
|
||||
// is composed of key-value pairs where the value is in JSON format. The object
|
||||
// returned contains the various values indexed by their respective keys. In
|
||||
// case of duplicate keys, the last value be used.
|
||||
// Examples:
|
||||
// ?key="value"&key2=false&key3=500
|
||||
// produces { "key": "value", "key2": false, "key3": 500 }
|
||||
// ?key={"x":0,"y":50}&key2=[1,2,true]
|
||||
// produces { "key": { "x": 0, "y": 0 }, "key2": [1, 2, true] }
|
||||
function getQueryArgs() {
|
||||
var args = {};
|
||||
if (location.search.length > 0) {
|
||||
var params = location.search.substr(1).split('&');
|
||||
for (var p of params) {
|
||||
var [k, v] = p.split('=');
|
||||
args[k] = JSON.parse(v);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Return a function that returns a promise to create a script element with the
|
||||
// given URI and append it to the head of the document in the given window.
|
||||
// As with runContinuation(), the extra function wrapper is for convenience
|
||||
// at the call site, so that this can be chained with other promises:
|
||||
// waitUntilApzStable().then(injectScript('foo'))
|
||||
// .then(injectScript('bar'));
|
||||
// If you want to do the injection right away, run the function returned by
|
||||
// this function:
|
||||
// injectScript('foo')();
|
||||
function injectScript(aScript, aWindow = window) {
|
||||
return function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var e = aWindow.document.createElement('script');
|
||||
e.type = 'text/javascript';
|
||||
e.onload = function() {
|
||||
resolve();
|
||||
};
|
||||
e.onerror = function() {
|
||||
dump('Script [' + aScript + '] errored out\n');
|
||||
reject();
|
||||
};
|
||||
e.src = aScript;
|
||||
aWindow.document.getElementsByTagName('head')[0].appendChild(e);
|
||||
});
|
||||
};
|
||||
}
|
||||
9
gfx/layers/apz/test/mochitest/chrome.ini
Normal file
9
gfx/layers/apz/test/mochitest/chrome.ini
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
apz_test_native_event_utils.js
|
||||
tags = apz-chrome
|
||||
|
||||
[test_smoothness.html]
|
||||
# hardware vsync only on win/mac
|
||||
# e10s only since APZ is only enabled on e10s
|
||||
skip-if = debug || (os != 'mac' && os != 'win') || !e10s
|
||||
38
gfx/layers/apz/test/mochitest/helper_basic_pan.html
Normal file
38
gfx/layers/apz/test/mochitest/helper_basic_pan.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity panning test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function scrollPage() {
|
||||
var transformEnd = function() {
|
||||
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
dump("Transform complete; flushing repaints...\n");
|
||||
flushApzRepaints(checkScroll);
|
||||
};
|
||||
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
|
||||
synthesizeNativeTouchDrag(document.body, 10, 100, 0, -(50 + TOUCH_SLOP));
|
||||
dump("Finished native drag, waiting for transform-end observer...\n");
|
||||
}
|
||||
|
||||
function checkScroll() {
|
||||
is(window.scrollY, 50, "check that the window scrolled");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(scrollPage);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="height: 5000px; background-color: lightgreen;">
|
||||
This div makes the page scrollable.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
83
gfx/layers/apz/test/mochitest/helper_bug1151663.html
Normal file
83
gfx/layers/apz/test/mochitest/helper_bug1151663.html
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1151663, helper page</title>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Infrastructure to get the test assertions to run at the right time.
|
||||
// -------------------------------------------------------------------
|
||||
var SimpleTest = window.opener.SimpleTest;
|
||||
|
||||
window.onload = function() {
|
||||
window.addEventListener("MozAfterPaint", afterPaint, false);
|
||||
};
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
function afterPaint(e) {
|
||||
// If there is another paint pending, wait for it.
|
||||
if (utils.isMozAfterPaintPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once there are no more paints pending, remove the
|
||||
// MozAfterPaint listener and run the test logic.
|
||||
window.removeEventListener("MozAfterPaint", afterPaint, false);
|
||||
testBug1151663();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// The actual logic for testing bug 1151663.
|
||||
//
|
||||
// In this test we have a simple page which is scrollable, with a
|
||||
// scrollable <div> which is also scrollable. We test that the
|
||||
// <div> does not get an initial APZC, since primary scrollable
|
||||
// frame is the page's root scroll frame.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
function testBug1151663() {
|
||||
// Get the content- and compositor-side test data from nsIDOMWindowUtils.
|
||||
var contentTestData = utils.getContentAPZTestData();
|
||||
var compositorTestData = utils.getCompositorAPZTestData();
|
||||
|
||||
// Get the sequence number of the last paint on the compositor side.
|
||||
// We do this before converting the APZ test data because the conversion
|
||||
// loses the order of the paints.
|
||||
SimpleTest.ok(compositorTestData.paints.length > 0,
|
||||
"expected at least one paint in compositor test data");
|
||||
var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
|
||||
var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
|
||||
|
||||
// Convert the test data into a representation that's easier to navigate.
|
||||
contentTestData = convertTestData(contentTestData);
|
||||
compositorTestData = convertTestData(compositorTestData);
|
||||
var paint = compositorTestData.paints[lastCompositorPaintSeqNo];
|
||||
|
||||
// Reconstruct the APZC tree structure in the last paint.
|
||||
var apzcTree = buildApzcTree(paint);
|
||||
|
||||
// The apzc tree for this page should consist of a single root APZC,
|
||||
// which either is the RCD with no child APZCs (e10s/B2G case) or has a
|
||||
// single child APZC which is for the RCD (fennec case).
|
||||
var rcd = findRcdNode(apzcTree);
|
||||
SimpleTest.ok(rcd != null, "found the RCD node");
|
||||
SimpleTest.is(rcd.children.length, 0, "expected no children on the RCD");
|
||||
|
||||
window.opener.finishTest();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="height: 500px; overflow: scroll">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
|
||||
<div style="height: 50px; width: 50px; overflow: scroll">
|
||||
<!-- Put enough content into the subframe to make it have a nonzero scroll range -->
|
||||
<div style="height: 100px; width: 50px"></div>
|
||||
</div>
|
||||
<!-- Put enough content into the page to make it have a nonzero scroll range -->
|
||||
<div style="height: 1000px"></div>
|
||||
</body>
|
||||
</html>
|
||||
104
gfx/layers/apz/test/mochitest/helper_bug1162771.html
Normal file
104
gfx/layers/apz/test/mochitest/helper_bug1162771.html
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Test for touchend on media elements</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var v = document.getElementById('video');
|
||||
var a = document.getElementById('audio');
|
||||
var d = document.getElementById('div');
|
||||
|
||||
document.body.ontouchstart = function(e) {
|
||||
if (e.target === v || e.target === a || e.target === d) {
|
||||
e.target.style.display = 'none';
|
||||
ok(true, 'Set display to none on #' + e.target.id);
|
||||
} else {
|
||||
ok(false, 'Got unexpected touchstart on ' + e.target);
|
||||
}
|
||||
waitForAllPaints(testDriver);
|
||||
};
|
||||
|
||||
document.body.ontouchend = function(e) {
|
||||
if (e.target === v || e.target === a || e.target === d) {
|
||||
e.target._gotTouchend = true;
|
||||
ok(true, 'Got touchend event on #' + e.target.id);
|
||||
}
|
||||
testDriver();
|
||||
};
|
||||
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
|
||||
var pt = coordinatesRelativeToScreen(25, 5, v);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
|
||||
ok(v._gotTouchend, 'Touchend was received on video element');
|
||||
|
||||
pt = coordinatesRelativeToScreen(25, 5, a);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
|
||||
ok(a._gotTouchend, 'Touchend was received on audio element');
|
||||
|
||||
pt = coordinatesRelativeToScreen(25, 5, d);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
|
||||
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
|
||||
ok(d._gotTouchend, 'Touchend was received on div element');
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
<style>
|
||||
* {
|
||||
font-size: 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#video {
|
||||
display:block;
|
||||
position:absolute;
|
||||
top: 100px;
|
||||
left:0;
|
||||
width: 33%;
|
||||
height: 100px;
|
||||
border:solid black 1px;
|
||||
background-color: #8a8;
|
||||
}
|
||||
|
||||
#audio {
|
||||
display:block;
|
||||
position:absolute;
|
||||
top: 100px;
|
||||
left:33%;
|
||||
width: 33%;
|
||||
height: 100px;
|
||||
border:solid black 1px;
|
||||
background-color: #a88;
|
||||
}
|
||||
|
||||
#div {
|
||||
display:block;
|
||||
position:absolute;
|
||||
top: 100px;
|
||||
left: 66%;
|
||||
width: 34%;
|
||||
height: 100px;
|
||||
border:solid black 1px;
|
||||
background-color: #88a;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Tap on the colored boxes to hide them.</p>
|
||||
<video id="video"></video>
|
||||
<audio id="audio" controls></audio>
|
||||
<div id="div"></div>
|
||||
</body>
|
||||
</html>
|
||||
574
gfx/layers/apz/test/mochitest/helper_bug1271432.html
Normal file
574
gfx/layers/apz/test/mochitest/helper_bug1271432.html
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
<head>
|
||||
<title>Ensure that the hit region doesn't get unexpectedly expanded</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
function* test(testDriver) {
|
||||
var scroller = document.getElementById('scroller');
|
||||
var scrollerPos = scroller.scrollTop;
|
||||
var dx = 100, dy = 50;
|
||||
|
||||
is(window.scrollY, 0, "Initial page scroll position should be 0");
|
||||
is(scrollerPos, 0, "Initial scroller position should be 0");
|
||||
|
||||
yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
|
||||
|
||||
is(window.scrollY, 0, "Page scroll position should still be 0");
|
||||
ok(scroller.scrollTop > scrollerPos, "Scroller should have scrolled");
|
||||
|
||||
// wait for it to layerize fully and then try again
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
scrollerPos = scroller.scrollTop;
|
||||
|
||||
yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
|
||||
is(window.scrollY, 0, "Page scroll position should still be 0 after layerization");
|
||||
ok(scroller.scrollTop > scrollerPos, "Scroller should have continued scrolling");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
<style>
|
||||
a#with_after_content {
|
||||
background-color: #F16725;
|
||||
opacity: 0.8;
|
||||
display: inline-block;
|
||||
margin-top: 40px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
a#with_after_content::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
bottom: 40px;
|
||||
z-index: -1;
|
||||
right: 40px;
|
||||
background-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 15px 15px 15px 0;
|
||||
border-color: #d54e0e transparent transparent transparent;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
div#scroller {
|
||||
overflow-y: scroll;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a id="with_after_content">Some text</a>
|
||||
|
||||
<div id="scroller">
|
||||
Scrolling on the very left edge of this div will work.
|
||||
Scrolling on the right side of this div (starting with the left edge of the orange box above) should work, but doesn't.<br/>
|
||||
0<br>
|
||||
1<br>
|
||||
2<br>
|
||||
3<br>
|
||||
4<br>
|
||||
5<br>
|
||||
6<br>
|
||||
7<br>
|
||||
8<br>
|
||||
9<br>
|
||||
10<br>
|
||||
11<br>
|
||||
12<br>
|
||||
13<br>
|
||||
14<br>
|
||||
15<br>
|
||||
16<br>
|
||||
17<br>
|
||||
18<br>
|
||||
19<br>
|
||||
20<br>
|
||||
21<br>
|
||||
22<br>
|
||||
23<br>
|
||||
24<br>
|
||||
25<br>
|
||||
26<br>
|
||||
27<br>
|
||||
28<br>
|
||||
29<br>
|
||||
30<br>
|
||||
31<br>
|
||||
32<br>
|
||||
33<br>
|
||||
34<br>
|
||||
35<br>
|
||||
36<br>
|
||||
37<br>
|
||||
38<br>
|
||||
39<br>
|
||||
40<br>
|
||||
41<br>
|
||||
42<br>
|
||||
43<br>
|
||||
44<br>
|
||||
45<br>
|
||||
46<br>
|
||||
47<br>
|
||||
48<br>
|
||||
49<br>
|
||||
50<br>
|
||||
51<br>
|
||||
52<br>
|
||||
53<br>
|
||||
54<br>
|
||||
55<br>
|
||||
56<br>
|
||||
57<br>
|
||||
58<br>
|
||||
59<br>
|
||||
60<br>
|
||||
61<br>
|
||||
62<br>
|
||||
63<br>
|
||||
64<br>
|
||||
65<br>
|
||||
66<br>
|
||||
67<br>
|
||||
68<br>
|
||||
69<br>
|
||||
70<br>
|
||||
71<br>
|
||||
72<br>
|
||||
73<br>
|
||||
74<br>
|
||||
75<br>
|
||||
76<br>
|
||||
77<br>
|
||||
78<br>
|
||||
79<br>
|
||||
80<br>
|
||||
81<br>
|
||||
82<br>
|
||||
83<br>
|
||||
84<br>
|
||||
85<br>
|
||||
86<br>
|
||||
87<br>
|
||||
88<br>
|
||||
89<br>
|
||||
90<br>
|
||||
91<br>
|
||||
92<br>
|
||||
93<br>
|
||||
94<br>
|
||||
95<br>
|
||||
96<br>
|
||||
97<br>
|
||||
98<br>
|
||||
99<br>
|
||||
100<br>
|
||||
101<br>
|
||||
102<br>
|
||||
103<br>
|
||||
104<br>
|
||||
105<br>
|
||||
106<br>
|
||||
107<br>
|
||||
108<br>
|
||||
109<br>
|
||||
110<br>
|
||||
111<br>
|
||||
112<br>
|
||||
113<br>
|
||||
114<br>
|
||||
115<br>
|
||||
116<br>
|
||||
117<br>
|
||||
118<br>
|
||||
119<br>
|
||||
120<br>
|
||||
121<br>
|
||||
122<br>
|
||||
123<br>
|
||||
124<br>
|
||||
125<br>
|
||||
126<br>
|
||||
127<br>
|
||||
128<br>
|
||||
129<br>
|
||||
130<br>
|
||||
131<br>
|
||||
132<br>
|
||||
133<br>
|
||||
134<br>
|
||||
135<br>
|
||||
136<br>
|
||||
137<br>
|
||||
138<br>
|
||||
139<br>
|
||||
140<br>
|
||||
141<br>
|
||||
142<br>
|
||||
143<br>
|
||||
144<br>
|
||||
145<br>
|
||||
146<br>
|
||||
147<br>
|
||||
148<br>
|
||||
149<br>
|
||||
150<br>
|
||||
151<br>
|
||||
152<br>
|
||||
153<br>
|
||||
154<br>
|
||||
155<br>
|
||||
156<br>
|
||||
157<br>
|
||||
158<br>
|
||||
159<br>
|
||||
160<br>
|
||||
161<br>
|
||||
162<br>
|
||||
163<br>
|
||||
164<br>
|
||||
165<br>
|
||||
166<br>
|
||||
167<br>
|
||||
168<br>
|
||||
169<br>
|
||||
170<br>
|
||||
171<br>
|
||||
172<br>
|
||||
173<br>
|
||||
174<br>
|
||||
175<br>
|
||||
176<br>
|
||||
177<br>
|
||||
178<br>
|
||||
179<br>
|
||||
180<br>
|
||||
181<br>
|
||||
182<br>
|
||||
183<br>
|
||||
184<br>
|
||||
185<br>
|
||||
186<br>
|
||||
187<br>
|
||||
188<br>
|
||||
189<br>
|
||||
190<br>
|
||||
191<br>
|
||||
192<br>
|
||||
193<br>
|
||||
194<br>
|
||||
195<br>
|
||||
196<br>
|
||||
197<br>
|
||||
198<br>
|
||||
199<br>
|
||||
200<br>
|
||||
201<br>
|
||||
202<br>
|
||||
203<br>
|
||||
204<br>
|
||||
205<br>
|
||||
206<br>
|
||||
207<br>
|
||||
208<br>
|
||||
209<br>
|
||||
210<br>
|
||||
211<br>
|
||||
212<br>
|
||||
213<br>
|
||||
214<br>
|
||||
215<br>
|
||||
216<br>
|
||||
217<br>
|
||||
218<br>
|
||||
219<br>
|
||||
220<br>
|
||||
221<br>
|
||||
222<br>
|
||||
223<br>
|
||||
224<br>
|
||||
225<br>
|
||||
226<br>
|
||||
227<br>
|
||||
228<br>
|
||||
229<br>
|
||||
230<br>
|
||||
231<br>
|
||||
232<br>
|
||||
233<br>
|
||||
234<br>
|
||||
235<br>
|
||||
236<br>
|
||||
237<br>
|
||||
238<br>
|
||||
239<br>
|
||||
240<br>
|
||||
241<br>
|
||||
242<br>
|
||||
243<br>
|
||||
244<br>
|
||||
245<br>
|
||||
246<br>
|
||||
247<br>
|
||||
248<br>
|
||||
249<br>
|
||||
250<br>
|
||||
251<br>
|
||||
252<br>
|
||||
253<br>
|
||||
254<br>
|
||||
255<br>
|
||||
256<br>
|
||||
257<br>
|
||||
258<br>
|
||||
259<br>
|
||||
260<br>
|
||||
261<br>
|
||||
262<br>
|
||||
263<br>
|
||||
264<br>
|
||||
265<br>
|
||||
266<br>
|
||||
267<br>
|
||||
268<br>
|
||||
269<br>
|
||||
270<br>
|
||||
271<br>
|
||||
272<br>
|
||||
273<br>
|
||||
274<br>
|
||||
275<br>
|
||||
276<br>
|
||||
277<br>
|
||||
278<br>
|
||||
279<br>
|
||||
280<br>
|
||||
281<br>
|
||||
282<br>
|
||||
283<br>
|
||||
284<br>
|
||||
285<br>
|
||||
286<br>
|
||||
287<br>
|
||||
288<br>
|
||||
289<br>
|
||||
290<br>
|
||||
291<br>
|
||||
292<br>
|
||||
293<br>
|
||||
294<br>
|
||||
295<br>
|
||||
296<br>
|
||||
297<br>
|
||||
298<br>
|
||||
299<br>
|
||||
300<br>
|
||||
301<br>
|
||||
302<br>
|
||||
303<br>
|
||||
304<br>
|
||||
305<br>
|
||||
306<br>
|
||||
307<br>
|
||||
308<br>
|
||||
309<br>
|
||||
310<br>
|
||||
311<br>
|
||||
312<br>
|
||||
313<br>
|
||||
314<br>
|
||||
315<br>
|
||||
316<br>
|
||||
317<br>
|
||||
318<br>
|
||||
319<br>
|
||||
320<br>
|
||||
321<br>
|
||||
322<br>
|
||||
323<br>
|
||||
324<br>
|
||||
325<br>
|
||||
326<br>
|
||||
327<br>
|
||||
328<br>
|
||||
329<br>
|
||||
330<br>
|
||||
331<br>
|
||||
332<br>
|
||||
333<br>
|
||||
334<br>
|
||||
335<br>
|
||||
336<br>
|
||||
337<br>
|
||||
338<br>
|
||||
339<br>
|
||||
340<br>
|
||||
341<br>
|
||||
342<br>
|
||||
343<br>
|
||||
344<br>
|
||||
345<br>
|
||||
346<br>
|
||||
347<br>
|
||||
348<br>
|
||||
349<br>
|
||||
350<br>
|
||||
351<br>
|
||||
352<br>
|
||||
353<br>
|
||||
354<br>
|
||||
355<br>
|
||||
356<br>
|
||||
357<br>
|
||||
358<br>
|
||||
359<br>
|
||||
360<br>
|
||||
361<br>
|
||||
362<br>
|
||||
363<br>
|
||||
364<br>
|
||||
365<br>
|
||||
366<br>
|
||||
367<br>
|
||||
368<br>
|
||||
369<br>
|
||||
370<br>
|
||||
371<br>
|
||||
372<br>
|
||||
373<br>
|
||||
374<br>
|
||||
375<br>
|
||||
376<br>
|
||||
377<br>
|
||||
378<br>
|
||||
379<br>
|
||||
380<br>
|
||||
381<br>
|
||||
382<br>
|
||||
383<br>
|
||||
384<br>
|
||||
385<br>
|
||||
386<br>
|
||||
387<br>
|
||||
388<br>
|
||||
389<br>
|
||||
390<br>
|
||||
391<br>
|
||||
392<br>
|
||||
393<br>
|
||||
394<br>
|
||||
395<br>
|
||||
396<br>
|
||||
397<br>
|
||||
398<br>
|
||||
399<br>
|
||||
400<br>
|
||||
401<br>
|
||||
402<br>
|
||||
403<br>
|
||||
404<br>
|
||||
405<br>
|
||||
406<br>
|
||||
407<br>
|
||||
408<br>
|
||||
409<br>
|
||||
410<br>
|
||||
411<br>
|
||||
412<br>
|
||||
413<br>
|
||||
414<br>
|
||||
415<br>
|
||||
416<br>
|
||||
417<br>
|
||||
418<br>
|
||||
419<br>
|
||||
420<br>
|
||||
421<br>
|
||||
422<br>
|
||||
423<br>
|
||||
424<br>
|
||||
425<br>
|
||||
426<br>
|
||||
427<br>
|
||||
428<br>
|
||||
429<br>
|
||||
430<br>
|
||||
431<br>
|
||||
432<br>
|
||||
433<br>
|
||||
434<br>
|
||||
435<br>
|
||||
436<br>
|
||||
437<br>
|
||||
438<br>
|
||||
439<br>
|
||||
440<br>
|
||||
441<br>
|
||||
442<br>
|
||||
443<br>
|
||||
444<br>
|
||||
445<br>
|
||||
446<br>
|
||||
447<br>
|
||||
448<br>
|
||||
449<br>
|
||||
450<br>
|
||||
451<br>
|
||||
452<br>
|
||||
453<br>
|
||||
454<br>
|
||||
455<br>
|
||||
456<br>
|
||||
457<br>
|
||||
458<br>
|
||||
459<br>
|
||||
460<br>
|
||||
461<br>
|
||||
462<br>
|
||||
463<br>
|
||||
464<br>
|
||||
465<br>
|
||||
466<br>
|
||||
467<br>
|
||||
468<br>
|
||||
469<br>
|
||||
470<br>
|
||||
471<br>
|
||||
472<br>
|
||||
473<br>
|
||||
474<br>
|
||||
475<br>
|
||||
476<br>
|
||||
477<br>
|
||||
478<br>
|
||||
479<br>
|
||||
480<br>
|
||||
481<br>
|
||||
482<br>
|
||||
483<br>
|
||||
484<br>
|
||||
485<br>
|
||||
486<br>
|
||||
487<br>
|
||||
488<br>
|
||||
489<br>
|
||||
490<br>
|
||||
491<br>
|
||||
492<br>
|
||||
493<br>
|
||||
494<br>
|
||||
495<br>
|
||||
496<br>
|
||||
497<br>
|
||||
498<br>
|
||||
499<br>
|
||||
</div>
|
||||
<div style="height: 1000px">this div makes the page scrollable</div>
|
||||
</body>
|
||||
74
gfx/layers/apz/test/mochitest/helper_bug1280013.html
Normal file
74
gfx/layers/apz/test/mochitest/helper_bug1280013.html
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html style="overflow:hidden">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- The viewport tag will result in APZ being in a "zoomed-in" state, assuming
|
||||
the device width is less than 980px. -->
|
||||
<meta name="viewport" content="width=980; initial-scale=1.0">
|
||||
<title>Test for bug 1280013</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
function* test(testDriver) {
|
||||
SimpleTest.ok(screen.height > 500, "Screen height must be at least 500 pixels for this test to work");
|
||||
|
||||
// This listener will trigger the test to continue once APZ is done with
|
||||
// processing the scroll.
|
||||
SpecialPowers.Services.obs.addObserver(testDriver, "APZ:TransformEnd", false);
|
||||
|
||||
// Scroll down to the iframe. Do it in two drags instead of one in case the
|
||||
// device screen is short
|
||||
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
|
||||
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
|
||||
// Now the top of the visible area should be at y=700 of the top-level page,
|
||||
// so if the screen is >= 500px tall, the entire iframe should be visible, at
|
||||
// least vertically.
|
||||
|
||||
// However, because of the overflow:hidden on the root elements, all this
|
||||
// scrolling is happening in APZ and is not reflected in the main-thread
|
||||
// scroll position (it is stored in the callback transform instead). We check
|
||||
// this by checking the scroll offset.
|
||||
yield flushApzRepaints(testDriver);
|
||||
SimpleTest.is(window.scrollY, 0, "Main-thread scroll position is still at 0");
|
||||
|
||||
// Scroll the iframe by 300px. Note that since the main-thread scroll position
|
||||
// is still 0, the subframe's getBoundingClientRect is going to be off by
|
||||
// 700 pixels, so we compensate for that here.
|
||||
var subframe = document.getElementById('subframe');
|
||||
yield synthesizeNativeTouchDrag(subframe, 10, 200 - 700, 0, -(300 + TOUCH_SLOP));
|
||||
|
||||
// Remove the observer, we don't need it any more.
|
||||
SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
|
||||
|
||||
// Flush any pending paints
|
||||
yield flushApzRepaints(testDriver);
|
||||
|
||||
// get the displayport for the subframe
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
var contentPaints = utils.getContentAPZTestData().paints;
|
||||
var lastPaint = convertScrollFrameData(contentPaints[contentPaints.length - 1].scrollFrames);
|
||||
var foundIt = 0;
|
||||
for (var scrollId in lastPaint) {
|
||||
if (('contentDescription' in lastPaint[scrollId]) &&
|
||||
(lastPaint[scrollId]['contentDescription'].includes('tall_html'))) {
|
||||
var dp = getPropertyAsRect(lastPaint, scrollId, 'criticalDisplayport');
|
||||
SimpleTest.ok(dp.y <= 0, 'The critical displayport top should be less than or equal to zero to cover the visible part of the subframe; it is ' + dp.y);
|
||||
SimpleTest.ok(dp.y + dp.h >= subframe.clientHeight, 'The critical displayport bottom should be greater than the clientHeight; it is ' + (dp.y + dp.h));
|
||||
foundIt++;
|
||||
}
|
||||
}
|
||||
SimpleTest.is(foundIt, 1, "Found exactly one critical displayport for the subframe we were interested in.");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="overflow:hidden">
|
||||
The iframe below is at (0, 800). Scroll it into view, and then scroll the contents. The content should be fully rendered in high-resolution.
|
||||
<iframe id="subframe" style="position:absolute; left: 0px; top: 800px; width: 600px; height: 350px" src="helper_tall.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
44
gfx/layers/apz/test/mochitest/helper_bug1285070.html
Normal file
44
gfx/layers/apz/test/mochitest/helper_bug1285070.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Test pointer events are dispatched once for touch tap</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
function test() {
|
||||
let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
|
||||
"pointerup", "pointerleave", "pointerout"];
|
||||
let pointerEventsCount = {};
|
||||
|
||||
pointerEventsList.forEach((eventName) => {
|
||||
pointerEventsCount[eventName] = 0;
|
||||
document.getElementById('div1').addEventListener(eventName, (event) => {
|
||||
dump("Received event " + event.type + "\n");
|
||||
++pointerEventsCount[event.type];
|
||||
}, false);
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
is(event.target, document.getElementById('div1'), "Clicked on div (at " + event.clientX + "," + event.clientY + ")");
|
||||
for (var key in pointerEventsCount) {
|
||||
is(pointerEventsCount[key], 1, "Event " + key + " should be generated once");
|
||||
}
|
||||
subtestDone();
|
||||
}, false);
|
||||
|
||||
synthesizeNativeTap(document.getElementById('div1'), 100, 100, () => {
|
||||
dump("Finished synthesizing tap, waiting for div to be clicked...\n");
|
||||
});
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(test);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="div1" style="width: 200px; height: 200px; background: black"></div>
|
||||
</body>
|
||||
</html>
|
||||
47
gfx/layers/apz/test/mochitest/helper_bug1299195.html
Normal file
47
gfx/layers/apz/test/mochitest/helper_bug1299195.html
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Test pointer events are dispatched once for touch tap</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 1299195 **/
|
||||
function runTests() {
|
||||
let target0 = document.getElementById("target0");
|
||||
let mouseup_count = 0;
|
||||
let mousedown_count = 0;
|
||||
let pointerup_count = 0;
|
||||
let pointerdown_count = 0;
|
||||
|
||||
target0.addEventListener("mouseup", () => {
|
||||
++mouseup_count;
|
||||
if (mouseup_count == 2) {
|
||||
is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
|
||||
is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
|
||||
is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
|
||||
is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
|
||||
subtestDone();
|
||||
}
|
||||
});
|
||||
target0.addEventListener("mousedown", () => {
|
||||
++mousedown_count;
|
||||
});
|
||||
target0.addEventListener("pointerup", () => {
|
||||
++pointerup_count;
|
||||
});
|
||||
target0.addEventListener("pointerdown", () => {
|
||||
++pointerdown_count;
|
||||
});
|
||||
synthesizeNativeTap(document.getElementById('target0'), 100, 100);
|
||||
synthesizeNativeTap(document.getElementById('target0'), 100, 100);
|
||||
}
|
||||
waitUntilApzStable().then(runTests);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target0" style="width: 200px; height: 200px; background: green"></div>
|
||||
</body>
|
||||
</html>
|
||||
149
gfx/layers/apz/test/mochitest/helper_bug982141.html
Normal file
149
gfx/layers/apz/test/mochitest/helper_bug982141.html
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="user-scalable=no">
|
||||
<title>Test for Bug 982141, helper page</title>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Infrastructure to get the test assertions to run at the right time.
|
||||
// -------------------------------------------------------------------
|
||||
var SimpleTest = window.opener.SimpleTest;
|
||||
|
||||
window.onload = function() {
|
||||
window.addEventListener("MozAfterPaint", afterPaint, false);
|
||||
};
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
function afterPaint(e) {
|
||||
// If there is another paint pending, wait for it.
|
||||
if (utils.isMozAfterPaintPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once there are no more paints pending, remove the
|
||||
// MozAfterPaint listener and run the test logic.
|
||||
window.removeEventListener("MozAfterPaint", afterPaint, false);
|
||||
testBug982141();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// The actual logic for testing bug 982141.
|
||||
//
|
||||
// In this test we have a simple page with a scrollable <div> which has
|
||||
// enough content to make it scrollable. We test that this <div> got
|
||||
// a displayport.
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
function testBug982141() {
|
||||
// Get the content- and compositor-side test data from nsIDOMWindowUtils.
|
||||
var contentTestData = utils.getContentAPZTestData();
|
||||
var compositorTestData = utils.getCompositorAPZTestData();
|
||||
|
||||
// Get the sequence number of the last paint on the compositor side.
|
||||
// We do this before converting the APZ test data because the conversion
|
||||
// loses the order of the paints.
|
||||
SimpleTest.ok(compositorTestData.paints.length > 0,
|
||||
"expected at least one paint in compositor test data");
|
||||
var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
|
||||
var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
|
||||
|
||||
// Convert the test data into a representation that's easier to navigate.
|
||||
contentTestData = convertTestData(contentTestData);
|
||||
compositorTestData = convertTestData(compositorTestData);
|
||||
|
||||
// Reconstruct the APZC tree structure in the last paint.
|
||||
var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]);
|
||||
|
||||
// The apzc tree for this page should consist of a single child APZC on
|
||||
// the RCD node (the child is for scrollable <div>). Note that in e10s/B2G
|
||||
// cases the RCD will be the root of the tree but on Fennec it will not.
|
||||
var rcd = findRcdNode(apzcTree);
|
||||
SimpleTest.ok(rcd != null, "found the RCD node");
|
||||
SimpleTest.is(rcd.children.length, 1, "expected a single child APZC");
|
||||
var childScrollId = rcd.children[0].scrollId;
|
||||
|
||||
// We should have content-side data for the same paint.
|
||||
SimpleTest.ok(lastCompositorPaintSeqNo in contentTestData.paints,
|
||||
"expected a content paint with sequence number" + lastCompositorPaintSeqNo);
|
||||
var correspondingContentPaint = contentTestData.paints[lastCompositorPaintSeqNo];
|
||||
|
||||
var dp = getPropertyAsRect(correspondingContentPaint, childScrollId, 'displayport');
|
||||
var subframe = document.getElementById('subframe');
|
||||
// The clientWidth and clientHeight may be less than 50 if there are scrollbars showing.
|
||||
// In general they will be (50 - <scrollbarwidth>, 50 - <scrollbarheight>).
|
||||
SimpleTest.ok(subframe.clientWidth > 0, "Expected a non-zero clientWidth, got: " + subframe.clientWidth);
|
||||
SimpleTest.ok(subframe.clientHeight > 0, "Expected a non-zero clientHeight, got: " + subframe.clientHeight);
|
||||
SimpleTest.ok(dp.w >= subframe.clientWidth && dp.h >= subframe.clientHeight,
|
||||
"expected a displayport at least as large as the scrollable element, got " + JSON.stringify(dp));
|
||||
|
||||
window.opener.finishTest();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
|
||||
<!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
|
||||
<div id="subframe" style="height: 50px; width: 50px; overflow: scroll">
|
||||
<div style="width: 100px">
|
||||
Wide content so that the vertical scrollbar for the parent div
|
||||
doesn't eat into the 50px width and reduce the width of the
|
||||
displayport.
|
||||
</div>
|
||||
Line 1<br>
|
||||
Line 2<br>
|
||||
Line 3<br>
|
||||
Line 4<br>
|
||||
Line 5<br>
|
||||
Line 6<br>
|
||||
Line 7<br>
|
||||
Line 8<br>
|
||||
Line 9<br>
|
||||
Line 10<br>
|
||||
Line 11<br>
|
||||
Line 12<br>
|
||||
Line 13<br>
|
||||
Line 14<br>
|
||||
Line 15<br>
|
||||
Line 16<br>
|
||||
Line 17<br>
|
||||
Line 18<br>
|
||||
Line 19<br>
|
||||
Line 20<br>
|
||||
Line 21<br>
|
||||
Line 22<br>
|
||||
Line 23<br>
|
||||
Line 24<br>
|
||||
Line 25<br>
|
||||
Line 26<br>
|
||||
Line 27<br>
|
||||
Line 28<br>
|
||||
Line 29<br>
|
||||
Line 30<br>
|
||||
Line 31<br>
|
||||
Line 32<br>
|
||||
Line 33<br>
|
||||
Line 34<br>
|
||||
Line 35<br>
|
||||
Line 36<br>
|
||||
Line 37<br>
|
||||
Line 38<br>
|
||||
Line 39<br>
|
||||
Line 40<br>
|
||||
Line 41<br>
|
||||
Line 42<br>
|
||||
Line 43<br>
|
||||
Line 44<br>
|
||||
Line 45<br>
|
||||
Line 46<br>
|
||||
Line 40<br>
|
||||
Line 48<br>
|
||||
Line 49<br>
|
||||
Line 50<br>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
41
gfx/layers/apz/test/mochitest/helper_click.html
Normal file
41
gfx/layers/apz/test/mochitest/helper_click.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity mouse-clicking test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* clickButton(testDriver) {
|
||||
document.addEventListener('click', clicked, false);
|
||||
|
||||
if (getQueryArgs()['dtc']) {
|
||||
// force a dispatch-to-content region on the document
|
||||
document.addEventListener('wheel', function() { /* no-op */ }, { passive: false });
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
}
|
||||
|
||||
synthesizeNativeClick(document.getElementById('b'), 5, 5, function() {
|
||||
dump("Finished synthesizing click, waiting for button to be clicked...\n");
|
||||
});
|
||||
}
|
||||
|
||||
function clicked(e) {
|
||||
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(clickButton));
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="b" style="width: 10px; height: 10px"></button>
|
||||
</body>
|
||||
</html>
|
||||
44
gfx/layers/apz/test/mochitest/helper_div_pan.html
Normal file
44
gfx/layers/apz/test/mochitest/helper_div_pan.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity panning test for scrollable div</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function scrollOuter() {
|
||||
var transformEnd = function() {
|
||||
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
dump("Transform complete; flushing repaints...\n");
|
||||
flushApzRepaints(checkScroll);
|
||||
};
|
||||
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
|
||||
synthesizeNativeTouchDrag(document.getElementById('outer'), 10, 100, 0, -(50 + TOUCH_SLOP));
|
||||
dump("Finished native drag, waiting for transform-end observer...\n");
|
||||
}
|
||||
|
||||
function checkScroll() {
|
||||
var outerScroll = document.getElementById('outer').scrollTop;
|
||||
is(outerScroll, 50, "check that the div scrolled");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(scrollOuter);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="outer" style="height: 250px; border: solid 1px black; overflow:scroll">
|
||||
<div style="height: 5000px; background-color: lightblue">
|
||||
This div makes the |outer| div scrollable.
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 5000px; background-color: lightgreen;">
|
||||
This div makes the top-level page scrollable.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
43
gfx/layers/apz/test/mochitest/helper_drag_click.html
Normal file
43
gfx/layers/apz/test/mochitest/helper_drag_click.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity mouse-drag click test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
document.addEventListener('click', clicked, false);
|
||||
|
||||
// Ensure the pointer is inside the window
|
||||
yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseMoveEventMsg(), testDriver);
|
||||
// mouse down, move it around, and release it near where it went down. this
|
||||
// should generate a click at the release point
|
||||
yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseDownEventMsg(), testDriver);
|
||||
yield synthesizeNativeMouseEvent(document.getElementById('b'), 100, 100, nativeMouseMoveEventMsg(), testDriver);
|
||||
yield synthesizeNativeMouseEvent(document.getElementById('b'), 10, 10, nativeMouseMoveEventMsg(), testDriver);
|
||||
yield synthesizeNativeMouseEvent(document.getElementById('b'), 8, 8, nativeMouseUpEventMsg(), testDriver);
|
||||
dump("Finished synthesizing click with a drag in the middle\n");
|
||||
}
|
||||
|
||||
function clicked(e) {
|
||||
// The mouse down at (5, 5) should not have generated a click, but the up
|
||||
// at (8, 8) should have.
|
||||
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
|
||||
is(e.clientX, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().left), 'x-coord of click event looks sane');
|
||||
is(e.clientY, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().top), 'y-coord of click event looks sane');
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test));
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="b" style="width: 10px; height: 10px"></button>
|
||||
</body>
|
||||
</html>
|
||||
603
gfx/layers/apz/test/mochitest/helper_drag_scroll.html
Normal file
603
gfx/layers/apz/test/mochitest/helper_drag_scroll.html
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Dragging the mouse on a content-implemented scrollbar</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, red, blue);
|
||||
}
|
||||
#scrollbar {
|
||||
position:fixed;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
height: 100%;
|
||||
width: 150px;
|
||||
background-color: gray;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var bar = null;
|
||||
var mouseDown = false;
|
||||
|
||||
function moveTo(mouseY, testDriver) {
|
||||
var fraction = (mouseY - bar.getBoundingClientRect().top) / bar.getBoundingClientRect().height;
|
||||
fraction = Math.max(0, fraction);
|
||||
fraction = Math.min(1, fraction);
|
||||
var oldScrollPos = document.scrollingElement.scrollTop;
|
||||
var newScrollPos = fraction * window.scrollMaxY;
|
||||
SimpleTest.ok(newScrollPos > oldScrollPos, "Scroll position strictly increased");
|
||||
// split the scroll in two with a paint in between, just to increase the
|
||||
// complexity of the simulated web content, and to ensure this works as well.
|
||||
document.scrollingElement.scrollTop = (oldScrollPos + newScrollPos) / 2;
|
||||
waitForAllPaints(function() {
|
||||
document.scrollingElement.scrollTop = newScrollPos;
|
||||
testDriver();
|
||||
});
|
||||
}
|
||||
|
||||
function setupDragging(testDriver) {
|
||||
bar = document.getElementById('scrollbar');
|
||||
mouseDown = false;
|
||||
|
||||
bar.addEventListener('mousedown', function(e) {
|
||||
mouseDown = true;
|
||||
moveTo(e.clientY, testDriver);
|
||||
}, true);
|
||||
|
||||
bar.addEventListener('mousemove', function(e) {
|
||||
if (mouseDown) {
|
||||
dump("Got mousemove clientY " + e.clientY + "\n");
|
||||
moveTo(e.clientY, testDriver);
|
||||
e.stopPropagation();
|
||||
}
|
||||
}, true);
|
||||
|
||||
bar.addEventListener('mouseup', function(e) {
|
||||
mouseDown = false;
|
||||
}, true);
|
||||
|
||||
window.addEventListener('mousemove', function(e) {
|
||||
if (mouseDown) {
|
||||
SimpleTest.ok(false, "The mousemove at " + e.clientY + " was not stopped by the bar listener, and is a glitchy event!");
|
||||
setTimeout(testDriver, 0);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
setupDragging(testDriver);
|
||||
|
||||
// Move the mouse to the "scrollbar" (the div upon which dragging changes scroll position)
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseMoveEventMsg(), testDriver);
|
||||
// mouse down
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseDownEventMsg());
|
||||
// drag vertically by 400px, in 50px increments
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 60, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 110, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 160, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 210, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 260, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 310, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 360, nativeMouseMoveEventMsg());
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseMoveEventMsg());
|
||||
// and release
|
||||
yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseUpEventMsg(), testDriver);
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="scrollbar">Drag up and down on this bar. The background/scrollbar shouldn't glitch</div>
|
||||
This is a tall page<br/>
|
||||
1<br/>
|
||||
2<br/>
|
||||
3<br/>
|
||||
4<br/>
|
||||
5<br/>
|
||||
6<br/>
|
||||
7<br/>
|
||||
8<br/>
|
||||
9<br/>
|
||||
10<br/>
|
||||
11<br/>
|
||||
12<br/>
|
||||
13<br/>
|
||||
14<br/>
|
||||
15<br/>
|
||||
16<br/>
|
||||
17<br/>
|
||||
18<br/>
|
||||
19<br/>
|
||||
20<br/>
|
||||
21<br/>
|
||||
22<br/>
|
||||
23<br/>
|
||||
24<br/>
|
||||
25<br/>
|
||||
26<br/>
|
||||
27<br/>
|
||||
28<br/>
|
||||
29<br/>
|
||||
30<br/>
|
||||
31<br/>
|
||||
32<br/>
|
||||
33<br/>
|
||||
34<br/>
|
||||
35<br/>
|
||||
36<br/>
|
||||
37<br/>
|
||||
38<br/>
|
||||
39<br/>
|
||||
40<br/>
|
||||
41<br/>
|
||||
42<br/>
|
||||
43<br/>
|
||||
44<br/>
|
||||
45<br/>
|
||||
46<br/>
|
||||
47<br/>
|
||||
48<br/>
|
||||
49<br/>
|
||||
50<br/>
|
||||
51<br/>
|
||||
52<br/>
|
||||
53<br/>
|
||||
54<br/>
|
||||
55<br/>
|
||||
56<br/>
|
||||
57<br/>
|
||||
58<br/>
|
||||
59<br/>
|
||||
60<br/>
|
||||
61<br/>
|
||||
62<br/>
|
||||
63<br/>
|
||||
64<br/>
|
||||
65<br/>
|
||||
66<br/>
|
||||
67<br/>
|
||||
68<br/>
|
||||
69<br/>
|
||||
70<br/>
|
||||
71<br/>
|
||||
72<br/>
|
||||
73<br/>
|
||||
74<br/>
|
||||
75<br/>
|
||||
76<br/>
|
||||
77<br/>
|
||||
78<br/>
|
||||
79<br/>
|
||||
80<br/>
|
||||
81<br/>
|
||||
82<br/>
|
||||
83<br/>
|
||||
84<br/>
|
||||
85<br/>
|
||||
86<br/>
|
||||
87<br/>
|
||||
88<br/>
|
||||
89<br/>
|
||||
90<br/>
|
||||
91<br/>
|
||||
92<br/>
|
||||
93<br/>
|
||||
94<br/>
|
||||
95<br/>
|
||||
96<br/>
|
||||
97<br/>
|
||||
98<br/>
|
||||
99<br/>
|
||||
100<br/>
|
||||
101<br/>
|
||||
102<br/>
|
||||
103<br/>
|
||||
104<br/>
|
||||
105<br/>
|
||||
106<br/>
|
||||
107<br/>
|
||||
108<br/>
|
||||
109<br/>
|
||||
110<br/>
|
||||
111<br/>
|
||||
112<br/>
|
||||
113<br/>
|
||||
114<br/>
|
||||
115<br/>
|
||||
116<br/>
|
||||
117<br/>
|
||||
118<br/>
|
||||
119<br/>
|
||||
120<br/>
|
||||
121<br/>
|
||||
122<br/>
|
||||
123<br/>
|
||||
124<br/>
|
||||
125<br/>
|
||||
126<br/>
|
||||
127<br/>
|
||||
128<br/>
|
||||
129<br/>
|
||||
130<br/>
|
||||
131<br/>
|
||||
132<br/>
|
||||
133<br/>
|
||||
134<br/>
|
||||
135<br/>
|
||||
136<br/>
|
||||
137<br/>
|
||||
138<br/>
|
||||
139<br/>
|
||||
140<br/>
|
||||
141<br/>
|
||||
142<br/>
|
||||
143<br/>
|
||||
144<br/>
|
||||
145<br/>
|
||||
146<br/>
|
||||
147<br/>
|
||||
148<br/>
|
||||
149<br/>
|
||||
150<br/>
|
||||
151<br/>
|
||||
152<br/>
|
||||
153<br/>
|
||||
154<br/>
|
||||
155<br/>
|
||||
156<br/>
|
||||
157<br/>
|
||||
158<br/>
|
||||
159<br/>
|
||||
160<br/>
|
||||
161<br/>
|
||||
162<br/>
|
||||
163<br/>
|
||||
164<br/>
|
||||
165<br/>
|
||||
166<br/>
|
||||
167<br/>
|
||||
168<br/>
|
||||
169<br/>
|
||||
170<br/>
|
||||
171<br/>
|
||||
172<br/>
|
||||
173<br/>
|
||||
174<br/>
|
||||
175<br/>
|
||||
176<br/>
|
||||
177<br/>
|
||||
178<br/>
|
||||
179<br/>
|
||||
180<br/>
|
||||
181<br/>
|
||||
182<br/>
|
||||
183<br/>
|
||||
184<br/>
|
||||
185<br/>
|
||||
186<br/>
|
||||
187<br/>
|
||||
188<br/>
|
||||
189<br/>
|
||||
190<br/>
|
||||
191<br/>
|
||||
192<br/>
|
||||
193<br/>
|
||||
194<br/>
|
||||
195<br/>
|
||||
196<br/>
|
||||
197<br/>
|
||||
198<br/>
|
||||
199<br/>
|
||||
200<br/>
|
||||
201<br/>
|
||||
202<br/>
|
||||
203<br/>
|
||||
204<br/>
|
||||
205<br/>
|
||||
206<br/>
|
||||
207<br/>
|
||||
208<br/>
|
||||
209<br/>
|
||||
210<br/>
|
||||
211<br/>
|
||||
212<br/>
|
||||
213<br/>
|
||||
214<br/>
|
||||
215<br/>
|
||||
216<br/>
|
||||
217<br/>
|
||||
218<br/>
|
||||
219<br/>
|
||||
220<br/>
|
||||
221<br/>
|
||||
222<br/>
|
||||
223<br/>
|
||||
224<br/>
|
||||
225<br/>
|
||||
226<br/>
|
||||
227<br/>
|
||||
228<br/>
|
||||
229<br/>
|
||||
230<br/>
|
||||
231<br/>
|
||||
232<br/>
|
||||
233<br/>
|
||||
234<br/>
|
||||
235<br/>
|
||||
236<br/>
|
||||
237<br/>
|
||||
238<br/>
|
||||
239<br/>
|
||||
240<br/>
|
||||
241<br/>
|
||||
242<br/>
|
||||
243<br/>
|
||||
244<br/>
|
||||
245<br/>
|
||||
246<br/>
|
||||
247<br/>
|
||||
248<br/>
|
||||
249<br/>
|
||||
250<br/>
|
||||
251<br/>
|
||||
252<br/>
|
||||
253<br/>
|
||||
254<br/>
|
||||
255<br/>
|
||||
256<br/>
|
||||
257<br/>
|
||||
258<br/>
|
||||
259<br/>
|
||||
260<br/>
|
||||
261<br/>
|
||||
262<br/>
|
||||
263<br/>
|
||||
264<br/>
|
||||
265<br/>
|
||||
266<br/>
|
||||
267<br/>
|
||||
268<br/>
|
||||
269<br/>
|
||||
270<br/>
|
||||
271<br/>
|
||||
272<br/>
|
||||
273<br/>
|
||||
274<br/>
|
||||
275<br/>
|
||||
276<br/>
|
||||
277<br/>
|
||||
278<br/>
|
||||
279<br/>
|
||||
280<br/>
|
||||
281<br/>
|
||||
282<br/>
|
||||
283<br/>
|
||||
284<br/>
|
||||
285<br/>
|
||||
286<br/>
|
||||
287<br/>
|
||||
288<br/>
|
||||
289<br/>
|
||||
290<br/>
|
||||
291<br/>
|
||||
292<br/>
|
||||
293<br/>
|
||||
294<br/>
|
||||
295<br/>
|
||||
296<br/>
|
||||
297<br/>
|
||||
298<br/>
|
||||
299<br/>
|
||||
300<br/>
|
||||
301<br/>
|
||||
302<br/>
|
||||
303<br/>
|
||||
304<br/>
|
||||
305<br/>
|
||||
306<br/>
|
||||
307<br/>
|
||||
308<br/>
|
||||
309<br/>
|
||||
310<br/>
|
||||
311<br/>
|
||||
312<br/>
|
||||
313<br/>
|
||||
314<br/>
|
||||
315<br/>
|
||||
316<br/>
|
||||
317<br/>
|
||||
318<br/>
|
||||
319<br/>
|
||||
320<br/>
|
||||
321<br/>
|
||||
322<br/>
|
||||
323<br/>
|
||||
324<br/>
|
||||
325<br/>
|
||||
326<br/>
|
||||
327<br/>
|
||||
328<br/>
|
||||
329<br/>
|
||||
330<br/>
|
||||
331<br/>
|
||||
332<br/>
|
||||
333<br/>
|
||||
334<br/>
|
||||
335<br/>
|
||||
336<br/>
|
||||
337<br/>
|
||||
338<br/>
|
||||
339<br/>
|
||||
340<br/>
|
||||
341<br/>
|
||||
342<br/>
|
||||
343<br/>
|
||||
344<br/>
|
||||
345<br/>
|
||||
346<br/>
|
||||
347<br/>
|
||||
348<br/>
|
||||
349<br/>
|
||||
350<br/>
|
||||
351<br/>
|
||||
352<br/>
|
||||
353<br/>
|
||||
354<br/>
|
||||
355<br/>
|
||||
356<br/>
|
||||
357<br/>
|
||||
358<br/>
|
||||
359<br/>
|
||||
360<br/>
|
||||
361<br/>
|
||||
362<br/>
|
||||
363<br/>
|
||||
364<br/>
|
||||
365<br/>
|
||||
366<br/>
|
||||
367<br/>
|
||||
368<br/>
|
||||
369<br/>
|
||||
370<br/>
|
||||
371<br/>
|
||||
372<br/>
|
||||
373<br/>
|
||||
374<br/>
|
||||
375<br/>
|
||||
376<br/>
|
||||
377<br/>
|
||||
378<br/>
|
||||
379<br/>
|
||||
380<br/>
|
||||
381<br/>
|
||||
382<br/>
|
||||
383<br/>
|
||||
384<br/>
|
||||
385<br/>
|
||||
386<br/>
|
||||
387<br/>
|
||||
388<br/>
|
||||
389<br/>
|
||||
390<br/>
|
||||
391<br/>
|
||||
392<br/>
|
||||
393<br/>
|
||||
394<br/>
|
||||
395<br/>
|
||||
396<br/>
|
||||
397<br/>
|
||||
398<br/>
|
||||
399<br/>
|
||||
400<br/>
|
||||
401<br/>
|
||||
402<br/>
|
||||
403<br/>
|
||||
404<br/>
|
||||
405<br/>
|
||||
406<br/>
|
||||
407<br/>
|
||||
408<br/>
|
||||
409<br/>
|
||||
410<br/>
|
||||
411<br/>
|
||||
412<br/>
|
||||
413<br/>
|
||||
414<br/>
|
||||
415<br/>
|
||||
416<br/>
|
||||
417<br/>
|
||||
418<br/>
|
||||
419<br/>
|
||||
420<br/>
|
||||
421<br/>
|
||||
422<br/>
|
||||
423<br/>
|
||||
424<br/>
|
||||
425<br/>
|
||||
426<br/>
|
||||
427<br/>
|
||||
428<br/>
|
||||
429<br/>
|
||||
430<br/>
|
||||
431<br/>
|
||||
432<br/>
|
||||
433<br/>
|
||||
434<br/>
|
||||
435<br/>
|
||||
436<br/>
|
||||
437<br/>
|
||||
438<br/>
|
||||
439<br/>
|
||||
440<br/>
|
||||
441<br/>
|
||||
442<br/>
|
||||
443<br/>
|
||||
444<br/>
|
||||
445<br/>
|
||||
446<br/>
|
||||
447<br/>
|
||||
448<br/>
|
||||
449<br/>
|
||||
450<br/>
|
||||
451<br/>
|
||||
452<br/>
|
||||
453<br/>
|
||||
454<br/>
|
||||
455<br/>
|
||||
456<br/>
|
||||
457<br/>
|
||||
458<br/>
|
||||
459<br/>
|
||||
460<br/>
|
||||
461<br/>
|
||||
462<br/>
|
||||
463<br/>
|
||||
464<br/>
|
||||
465<br/>
|
||||
466<br/>
|
||||
467<br/>
|
||||
468<br/>
|
||||
469<br/>
|
||||
470<br/>
|
||||
471<br/>
|
||||
472<br/>
|
||||
473<br/>
|
||||
474<br/>
|
||||
475<br/>
|
||||
476<br/>
|
||||
477<br/>
|
||||
478<br/>
|
||||
479<br/>
|
||||
480<br/>
|
||||
481<br/>
|
||||
482<br/>
|
||||
483<br/>
|
||||
484<br/>
|
||||
485<br/>
|
||||
486<br/>
|
||||
487<br/>
|
||||
488<br/>
|
||||
489<br/>
|
||||
490<br/>
|
||||
491<br/>
|
||||
492<br/>
|
||||
493<br/>
|
||||
494<br/>
|
||||
495<br/>
|
||||
496<br/>
|
||||
497<br/>
|
||||
498<br/>
|
||||
499<br/>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
14
gfx/layers/apz/test/mochitest/helper_iframe1.html
Normal file
14
gfx/layers/apz/test/mochitest/helper_iframe1.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!-- The purpose of the 'id' on the HTML element is to get something
|
||||
identifiable to show up in the root scroll frame's content description,
|
||||
so we can check it for layerization. -->
|
||||
<html id="outer3">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="inner3" class="inner-frame">
|
||||
<div class="inner-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
14
gfx/layers/apz/test/mochitest/helper_iframe2.html
Normal file
14
gfx/layers/apz/test/mochitest/helper_iframe2.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!-- The purpose of the 'id' on the HTML element is to get something
|
||||
identifiable to show up in the root scroll frame's content description,
|
||||
so we can check it for layerization. -->
|
||||
<html id="outer4">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="inner4" class="inner-frame">
|
||||
<div class="inner-content"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
41
gfx/layers/apz/test/mochitest/helper_iframe_pan.html
Normal file
41
gfx/layers/apz/test/mochitest/helper_iframe_pan.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity panning test for scrollable div</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function scrollOuter() {
|
||||
var outer = document.getElementById('outer');
|
||||
var transformEnd = function() {
|
||||
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
dump("Transform complete; flushing repaints...\n");
|
||||
flushApzRepaints(checkScroll, outer.contentWindow);
|
||||
};
|
||||
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
|
||||
|
||||
synthesizeNativeTouchDrag(outer.contentDocument.body, 10, 100, 0, -(50 + TOUCH_SLOP));
|
||||
dump("Finished native drag, waiting for transform-end observer...\n");
|
||||
}
|
||||
|
||||
function checkScroll() {
|
||||
var outerScroll = document.getElementById('outer').contentWindow.scrollY;
|
||||
is(outerScroll, 50, "check that the iframe scrolled");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(scrollOuter);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="outer" style="height: 250px; border: solid 1px black" src="data:text/html,<body style='height:5000px'>"></iframe>
|
||||
<div style="height: 5000px; background-color: lightgreen;">
|
||||
This div makes the top-level page scrollable.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
81
gfx/layers/apz/test/mochitest/helper_long_tap.html
Normal file
81
gfx/layers/apz/test/mochitest/helper_long_tap.html
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Ensure we get a touch-cancel after a contextmenu comes up</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function longPressLink() {
|
||||
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
|
||||
dump("Finished synthesizing touch-start, waiting for events...\n");
|
||||
});
|
||||
}
|
||||
|
||||
var eventsFired = 0;
|
||||
function recordEvent(e) {
|
||||
if (getPlatform() == "windows") {
|
||||
// On Windows we get a mouselongtap event once the long-tap has been detected
|
||||
// by APZ, and that's what we use as the trigger to lift the finger. That then
|
||||
// triggers the contextmenu. This matches the platform convention.
|
||||
switch (eventsFired) {
|
||||
case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
|
||||
case 1:
|
||||
is(e.type, 'mouselongtap', 'Got a mouselongtap');
|
||||
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
|
||||
break;
|
||||
case 2: is(e.type, 'touchend', 'Got a touchend'); break;
|
||||
case 3: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
|
||||
default: ok(false, 'Got an unexpected event of type ' + e.type); break;
|
||||
}
|
||||
eventsFired++;
|
||||
|
||||
if (eventsFired == 4) {
|
||||
dump("Finished waiting for events, doing an APZ flush to see if any more unexpected events come through...\n");
|
||||
flushApzRepaints(function() {
|
||||
dump("Done APZ flush, ending test...\n");
|
||||
subtestDone();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// On non-Windows platforms we get a contextmenu event once the long-tap has
|
||||
// been detected. Since we prevent-default that, we don't get a mouselongtap
|
||||
// event at all, and instead get a touchcancel.
|
||||
switch (eventsFired) {
|
||||
case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
|
||||
case 1: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
|
||||
case 2: is(e.type, 'touchcancel', 'Got a touchcancel'); break;
|
||||
default: ok(false, 'Got an unexpected event of type ' + e.type); break;
|
||||
}
|
||||
eventsFired++;
|
||||
|
||||
if (eventsFired == 3) {
|
||||
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
|
||||
dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
|
||||
flushApzRepaints(function() {
|
||||
dump("Done APZ flush, ending test...\n");
|
||||
subtestDone();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
|
||||
window.addEventListener('touchend', recordEvent, { passive: true, capture: true });
|
||||
window.addEventListener('touchcancel', recordEvent, true);
|
||||
window.addEventListener('contextmenu', recordEvent, true);
|
||||
SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(longPressLink);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="b" href="#">Link to nowhere</a>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Wheel-scrolling over inactive subframe with perspective</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var subframe = document.getElementById('scroll');
|
||||
|
||||
// scroll over the middle of the subframe, to make sure it scrolls,
|
||||
// not the page
|
||||
var scrollPos = subframe.scrollTop;
|
||||
yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
|
||||
dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
|
||||
ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#scroll {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
perspective: 400px;
|
||||
}
|
||||
#scrolled {
|
||||
width: 200px;
|
||||
height: 1000px; /* so the subframe has room to scroll */
|
||||
background: linear-gradient(red, blue); /* so you can see it scroll */
|
||||
transform: translateZ(0px); /* so the perspective makes it to the display list */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll">
|
||||
<div id="scrolled"></div>
|
||||
</div>
|
||||
<div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
|
||||
</body>
|
||||
</head>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Wheel-scrolling over inactive subframe with z-index</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var subframe = document.getElementById('scroll');
|
||||
|
||||
// scroll over the middle of the subframe, and make sure that it scrolls,
|
||||
// not the page
|
||||
var scrollPos = subframe.scrollTop;
|
||||
yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
|
||||
dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
|
||||
ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#scroll {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: scroll;
|
||||
}
|
||||
#scrolled {
|
||||
width: 200px;
|
||||
height: 1000px; /* so the subframe has room to scroll */
|
||||
z-index: 2;
|
||||
background: linear-gradient(red, blue); /* so you can see it scroll */
|
||||
transform: translateZ(0px); /* to force active layers */
|
||||
will-change: transform; /* to force active layers */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroll">
|
||||
<div id="scrolled"></div>
|
||||
</div>
|
||||
<div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
|
||||
</body>
|
||||
</head>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var iframeWin = document.getElementById('iframe').contentWindow;
|
||||
|
||||
// scroll over the middle of the iframe's position:sticky element, check
|
||||
// that it scrolls the iframe
|
||||
var scrollPos = iframeWin.scrollY;
|
||||
yield moveMouseAndScrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
|
||||
ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
|
||||
|
||||
// same, but using the iframe's position:fixed element
|
||||
scrollPos = iframeWin.scrollY;
|
||||
yield moveMouseAndScrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
|
||||
ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
|
||||
|
||||
// same, but scrolling the scrollable frame *inside* the position:fixed item
|
||||
var fpos = document.getElementById('fpos_scrollable');
|
||||
scrollPos = fpos.scrollTop;
|
||||
yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
|
||||
ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
|
||||
// wait for it to layerize fully and then try again
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
scrollPos = fpos.scrollTop;
|
||||
yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
|
||||
ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
|
||||
|
||||
// same, but using the top-level window's position:sticky element
|
||||
scrollPos = window.scrollY;
|
||||
yield moveMouseAndScrollWheelOver(document.body, 50, 150, testDriver);
|
||||
ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
|
||||
|
||||
// same, but using the top-level window's position:fixed element
|
||||
scrollPos = window.scrollY;
|
||||
yield moveMouseAndScrollWheelOver(document.body, 250, 150, testDriver);
|
||||
ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:fixed element");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="height:5000px; margin:0">
|
||||
<div style="position:sticky; width: 100px; height: 300px; top: 0; background-color:red">sticky</div>
|
||||
<div style="position:fixed; width: 100px; height: 300px; top: 0; left: 200px; background-color: green">fixed</div>
|
||||
<iframe id='iframe' width="300" height="400" src="data:text/html,<body style='height:5000px; margin:0'><div style='position:sticky; width:100px; height:300px; top: 0; background-color:red'>sticky</div><div style='position:fixed; right:0; top: 0; width:100px; height:300px; background-color:green'>fixed</div>"></iframe>
|
||||
|
||||
<div id="fpos_scrollable" style="position:fixed; width: 100px; height: 300px; top: 0; left: 400px; background-color: red; overflow:scroll">
|
||||
<div style="background-color: blue; height: 1000px; margin: 3px">scrollable content inside a fixed-pos item</div>
|
||||
</div>
|
||||
</body>
|
||||
</head>
|
||||
61
gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
Normal file
61
gfx/layers/apz/test/mochitest/helper_scrollto_tap.html
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity touch-tapping test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function startTest() {
|
||||
if (window.scrollY == 0) {
|
||||
// the scrollframe is not yet marked as APZ-scrollable. Mark it so and
|
||||
// start over.
|
||||
window.scrollTo(0, 1);
|
||||
waitForApzFlushedRepaints(startTest);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a scroll by 20px that should use paint-skipping if possible.
|
||||
// If paint-skipping is enabled, this should not trigger a paint, but go
|
||||
// directly to the compositor using an empty transaction. We check for this
|
||||
// by ensuring the document element did not get painted.
|
||||
var utils = window.opener.SpecialPowers.getDOMWindowUtils(window);
|
||||
var elem = document.documentElement;
|
||||
var skipping = location.search == '?true';
|
||||
utils.checkAndClearPaintedState(elem);
|
||||
window.scrollTo(0, 20);
|
||||
waitForAllPaints(function() {
|
||||
if (skipping) {
|
||||
is(utils.checkAndClearPaintedState(elem), false, "Document element didn't get painted");
|
||||
}
|
||||
// After that's done, we click on the button to make sure the
|
||||
// skipped-paint codepath still has working APZ event transformations.
|
||||
clickButton();
|
||||
});
|
||||
}
|
||||
|
||||
function clickButton() {
|
||||
document.addEventListener('click', clicked, false);
|
||||
|
||||
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
|
||||
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
|
||||
});
|
||||
}
|
||||
|
||||
function clicked(e) {
|
||||
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(startTest);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="height: 5000px">
|
||||
<div style="height: 50px">spacer</div>
|
||||
<button id="b" style="width: 10px; height: 10px"></button>
|
||||
</body>
|
||||
</html>
|
||||
15
gfx/layers/apz/test/mochitest/helper_subframe_style.css
Normal file
15
gfx/layers/apz/test/mochitest/helper_subframe_style.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
body {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.inner-frame {
|
||||
margin-top: 50px; /* this should be at least 30px */
|
||||
height: 200%;
|
||||
width: 75%;
|
||||
overflow: scroll;
|
||||
}
|
||||
.inner-content {
|
||||
height: 200%;
|
||||
width: 200%;
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
504
gfx/layers/apz/test/mochitest/helper_tall.html
Normal file
504
gfx/layers/apz/test/mochitest/helper_tall.html
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
<html id="tall_html">
|
||||
<body>
|
||||
This is a tall page<br/>
|
||||
1<br/>
|
||||
2<br/>
|
||||
3<br/>
|
||||
4<br/>
|
||||
5<br/>
|
||||
6<br/>
|
||||
7<br/>
|
||||
8<br/>
|
||||
9<br/>
|
||||
10<br/>
|
||||
11<br/>
|
||||
12<br/>
|
||||
13<br/>
|
||||
14<br/>
|
||||
15<br/>
|
||||
16<br/>
|
||||
17<br/>
|
||||
18<br/>
|
||||
19<br/>
|
||||
20<br/>
|
||||
21<br/>
|
||||
22<br/>
|
||||
23<br/>
|
||||
24<br/>
|
||||
25<br/>
|
||||
26<br/>
|
||||
27<br/>
|
||||
28<br/>
|
||||
29<br/>
|
||||
30<br/>
|
||||
31<br/>
|
||||
32<br/>
|
||||
33<br/>
|
||||
34<br/>
|
||||
35<br/>
|
||||
36<br/>
|
||||
37<br/>
|
||||
38<br/>
|
||||
39<br/>
|
||||
40<br/>
|
||||
41<br/>
|
||||
42<br/>
|
||||
43<br/>
|
||||
44<br/>
|
||||
45<br/>
|
||||
46<br/>
|
||||
47<br/>
|
||||
48<br/>
|
||||
49<br/>
|
||||
50<br/>
|
||||
51<br/>
|
||||
52<br/>
|
||||
53<br/>
|
||||
54<br/>
|
||||
55<br/>
|
||||
56<br/>
|
||||
57<br/>
|
||||
58<br/>
|
||||
59<br/>
|
||||
60<br/>
|
||||
61<br/>
|
||||
62<br/>
|
||||
63<br/>
|
||||
64<br/>
|
||||
65<br/>
|
||||
66<br/>
|
||||
67<br/>
|
||||
68<br/>
|
||||
69<br/>
|
||||
70<br/>
|
||||
71<br/>
|
||||
72<br/>
|
||||
73<br/>
|
||||
74<br/>
|
||||
75<br/>
|
||||
76<br/>
|
||||
77<br/>
|
||||
78<br/>
|
||||
79<br/>
|
||||
80<br/>
|
||||
81<br/>
|
||||
82<br/>
|
||||
83<br/>
|
||||
84<br/>
|
||||
85<br/>
|
||||
86<br/>
|
||||
87<br/>
|
||||
88<br/>
|
||||
89<br/>
|
||||
90<br/>
|
||||
91<br/>
|
||||
92<br/>
|
||||
93<br/>
|
||||
94<br/>
|
||||
95<br/>
|
||||
96<br/>
|
||||
97<br/>
|
||||
98<br/>
|
||||
99<br/>
|
||||
100<br/>
|
||||
101<br/>
|
||||
102<br/>
|
||||
103<br/>
|
||||
104<br/>
|
||||
105<br/>
|
||||
106<br/>
|
||||
107<br/>
|
||||
108<br/>
|
||||
109<br/>
|
||||
110<br/>
|
||||
111<br/>
|
||||
112<br/>
|
||||
113<br/>
|
||||
114<br/>
|
||||
115<br/>
|
||||
116<br/>
|
||||
117<br/>
|
||||
118<br/>
|
||||
119<br/>
|
||||
120<br/>
|
||||
121<br/>
|
||||
122<br/>
|
||||
123<br/>
|
||||
124<br/>
|
||||
125<br/>
|
||||
126<br/>
|
||||
127<br/>
|
||||
128<br/>
|
||||
129<br/>
|
||||
130<br/>
|
||||
131<br/>
|
||||
132<br/>
|
||||
133<br/>
|
||||
134<br/>
|
||||
135<br/>
|
||||
136<br/>
|
||||
137<br/>
|
||||
138<br/>
|
||||
139<br/>
|
||||
140<br/>
|
||||
141<br/>
|
||||
142<br/>
|
||||
143<br/>
|
||||
144<br/>
|
||||
145<br/>
|
||||
146<br/>
|
||||
147<br/>
|
||||
148<br/>
|
||||
149<br/>
|
||||
150<br/>
|
||||
151<br/>
|
||||
152<br/>
|
||||
153<br/>
|
||||
154<br/>
|
||||
155<br/>
|
||||
156<br/>
|
||||
157<br/>
|
||||
158<br/>
|
||||
159<br/>
|
||||
160<br/>
|
||||
161<br/>
|
||||
162<br/>
|
||||
163<br/>
|
||||
164<br/>
|
||||
165<br/>
|
||||
166<br/>
|
||||
167<br/>
|
||||
168<br/>
|
||||
169<br/>
|
||||
170<br/>
|
||||
171<br/>
|
||||
172<br/>
|
||||
173<br/>
|
||||
174<br/>
|
||||
175<br/>
|
||||
176<br/>
|
||||
177<br/>
|
||||
178<br/>
|
||||
179<br/>
|
||||
180<br/>
|
||||
181<br/>
|
||||
182<br/>
|
||||
183<br/>
|
||||
184<br/>
|
||||
185<br/>
|
||||
186<br/>
|
||||
187<br/>
|
||||
188<br/>
|
||||
189<br/>
|
||||
190<br/>
|
||||
191<br/>
|
||||
192<br/>
|
||||
193<br/>
|
||||
194<br/>
|
||||
195<br/>
|
||||
196<br/>
|
||||
197<br/>
|
||||
198<br/>
|
||||
199<br/>
|
||||
200<br/>
|
||||
201<br/>
|
||||
202<br/>
|
||||
203<br/>
|
||||
204<br/>
|
||||
205<br/>
|
||||
206<br/>
|
||||
207<br/>
|
||||
208<br/>
|
||||
209<br/>
|
||||
210<br/>
|
||||
211<br/>
|
||||
212<br/>
|
||||
213<br/>
|
||||
214<br/>
|
||||
215<br/>
|
||||
216<br/>
|
||||
217<br/>
|
||||
218<br/>
|
||||
219<br/>
|
||||
220<br/>
|
||||
221<br/>
|
||||
222<br/>
|
||||
223<br/>
|
||||
224<br/>
|
||||
225<br/>
|
||||
226<br/>
|
||||
227<br/>
|
||||
228<br/>
|
||||
229<br/>
|
||||
230<br/>
|
||||
231<br/>
|
||||
232<br/>
|
||||
233<br/>
|
||||
234<br/>
|
||||
235<br/>
|
||||
236<br/>
|
||||
237<br/>
|
||||
238<br/>
|
||||
239<br/>
|
||||
240<br/>
|
||||
241<br/>
|
||||
242<br/>
|
||||
243<br/>
|
||||
244<br/>
|
||||
245<br/>
|
||||
246<br/>
|
||||
247<br/>
|
||||
248<br/>
|
||||
249<br/>
|
||||
250<br/>
|
||||
251<br/>
|
||||
252<br/>
|
||||
253<br/>
|
||||
254<br/>
|
||||
255<br/>
|
||||
256<br/>
|
||||
257<br/>
|
||||
258<br/>
|
||||
259<br/>
|
||||
260<br/>
|
||||
261<br/>
|
||||
262<br/>
|
||||
263<br/>
|
||||
264<br/>
|
||||
265<br/>
|
||||
266<br/>
|
||||
267<br/>
|
||||
268<br/>
|
||||
269<br/>
|
||||
270<br/>
|
||||
271<br/>
|
||||
272<br/>
|
||||
273<br/>
|
||||
274<br/>
|
||||
275<br/>
|
||||
276<br/>
|
||||
277<br/>
|
||||
278<br/>
|
||||
279<br/>
|
||||
280<br/>
|
||||
281<br/>
|
||||
282<br/>
|
||||
283<br/>
|
||||
284<br/>
|
||||
285<br/>
|
||||
286<br/>
|
||||
287<br/>
|
||||
288<br/>
|
||||
289<br/>
|
||||
290<br/>
|
||||
291<br/>
|
||||
292<br/>
|
||||
293<br/>
|
||||
294<br/>
|
||||
295<br/>
|
||||
296<br/>
|
||||
297<br/>
|
||||
298<br/>
|
||||
299<br/>
|
||||
300<br/>
|
||||
301<br/>
|
||||
302<br/>
|
||||
303<br/>
|
||||
304<br/>
|
||||
305<br/>
|
||||
306<br/>
|
||||
307<br/>
|
||||
308<br/>
|
||||
309<br/>
|
||||
310<br/>
|
||||
311<br/>
|
||||
312<br/>
|
||||
313<br/>
|
||||
314<br/>
|
||||
315<br/>
|
||||
316<br/>
|
||||
317<br/>
|
||||
318<br/>
|
||||
319<br/>
|
||||
320<br/>
|
||||
321<br/>
|
||||
322<br/>
|
||||
323<br/>
|
||||
324<br/>
|
||||
325<br/>
|
||||
326<br/>
|
||||
327<br/>
|
||||
328<br/>
|
||||
329<br/>
|
||||
330<br/>
|
||||
331<br/>
|
||||
332<br/>
|
||||
333<br/>
|
||||
334<br/>
|
||||
335<br/>
|
||||
336<br/>
|
||||
337<br/>
|
||||
338<br/>
|
||||
339<br/>
|
||||
340<br/>
|
||||
341<br/>
|
||||
342<br/>
|
||||
343<br/>
|
||||
344<br/>
|
||||
345<br/>
|
||||
346<br/>
|
||||
347<br/>
|
||||
348<br/>
|
||||
349<br/>
|
||||
350<br/>
|
||||
351<br/>
|
||||
352<br/>
|
||||
353<br/>
|
||||
354<br/>
|
||||
355<br/>
|
||||
356<br/>
|
||||
357<br/>
|
||||
358<br/>
|
||||
359<br/>
|
||||
360<br/>
|
||||
361<br/>
|
||||
362<br/>
|
||||
363<br/>
|
||||
364<br/>
|
||||
365<br/>
|
||||
366<br/>
|
||||
367<br/>
|
||||
368<br/>
|
||||
369<br/>
|
||||
370<br/>
|
||||
371<br/>
|
||||
372<br/>
|
||||
373<br/>
|
||||
374<br/>
|
||||
375<br/>
|
||||
376<br/>
|
||||
377<br/>
|
||||
378<br/>
|
||||
379<br/>
|
||||
380<br/>
|
||||
381<br/>
|
||||
382<br/>
|
||||
383<br/>
|
||||
384<br/>
|
||||
385<br/>
|
||||
386<br/>
|
||||
387<br/>
|
||||
388<br/>
|
||||
389<br/>
|
||||
390<br/>
|
||||
391<br/>
|
||||
392<br/>
|
||||
393<br/>
|
||||
394<br/>
|
||||
395<br/>
|
||||
396<br/>
|
||||
397<br/>
|
||||
398<br/>
|
||||
399<br/>
|
||||
400<br/>
|
||||
401<br/>
|
||||
402<br/>
|
||||
403<br/>
|
||||
404<br/>
|
||||
405<br/>
|
||||
406<br/>
|
||||
407<br/>
|
||||
408<br/>
|
||||
409<br/>
|
||||
410<br/>
|
||||
411<br/>
|
||||
412<br/>
|
||||
413<br/>
|
||||
414<br/>
|
||||
415<br/>
|
||||
416<br/>
|
||||
417<br/>
|
||||
418<br/>
|
||||
419<br/>
|
||||
420<br/>
|
||||
421<br/>
|
||||
422<br/>
|
||||
423<br/>
|
||||
424<br/>
|
||||
425<br/>
|
||||
426<br/>
|
||||
427<br/>
|
||||
428<br/>
|
||||
429<br/>
|
||||
430<br/>
|
||||
431<br/>
|
||||
432<br/>
|
||||
433<br/>
|
||||
434<br/>
|
||||
435<br/>
|
||||
436<br/>
|
||||
437<br/>
|
||||
438<br/>
|
||||
439<br/>
|
||||
440<br/>
|
||||
441<br/>
|
||||
442<br/>
|
||||
443<br/>
|
||||
444<br/>
|
||||
445<br/>
|
||||
446<br/>
|
||||
447<br/>
|
||||
448<br/>
|
||||
449<br/>
|
||||
450<br/>
|
||||
451<br/>
|
||||
452<br/>
|
||||
453<br/>
|
||||
454<br/>
|
||||
455<br/>
|
||||
456<br/>
|
||||
457<br/>
|
||||
458<br/>
|
||||
459<br/>
|
||||
460<br/>
|
||||
461<br/>
|
||||
462<br/>
|
||||
463<br/>
|
||||
464<br/>
|
||||
465<br/>
|
||||
466<br/>
|
||||
467<br/>
|
||||
468<br/>
|
||||
469<br/>
|
||||
470<br/>
|
||||
471<br/>
|
||||
472<br/>
|
||||
473<br/>
|
||||
474<br/>
|
||||
475<br/>
|
||||
476<br/>
|
||||
477<br/>
|
||||
478<br/>
|
||||
479<br/>
|
||||
480<br/>
|
||||
481<br/>
|
||||
482<br/>
|
||||
483<br/>
|
||||
484<br/>
|
||||
485<br/>
|
||||
486<br/>
|
||||
487<br/>
|
||||
488<br/>
|
||||
489<br/>
|
||||
490<br/>
|
||||
491<br/>
|
||||
492<br/>
|
||||
493<br/>
|
||||
494<br/>
|
||||
495<br/>
|
||||
496<br/>
|
||||
497<br/>
|
||||
498<br/>
|
||||
499<br/>
|
||||
</body>
|
||||
</html>
|
||||
32
gfx/layers/apz/test/mochitest/helper_tap.html
Normal file
32
gfx/layers/apz/test/mochitest/helper_tap.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity touch-tapping test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function clickButton() {
|
||||
document.addEventListener('click', clicked, false);
|
||||
|
||||
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
|
||||
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
|
||||
});
|
||||
}
|
||||
|
||||
function clicked(e) {
|
||||
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
waitUntilApzStable().then(clickButton);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="b" style="width: 10px; height: 10px"></button>
|
||||
</body>
|
||||
</html>
|
||||
33
gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html
Normal file
33
gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity touch-tapping test with fullzoom</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function clickButton() {
|
||||
document.addEventListener('click', clicked, false);
|
||||
|
||||
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
|
||||
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
|
||||
});
|
||||
}
|
||||
|
||||
function clicked(e) {
|
||||
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
|
||||
subtestDone();
|
||||
}
|
||||
|
||||
SpecialPowers.setFullZoom(window, 2.0);
|
||||
waitUntilApzStable().then(clickButton);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="b" style="width: 10px; height: 10px; position: relative; top: 100px"></button>
|
||||
</body>
|
||||
</html>
|
||||
64
gfx/layers/apz/test/mochitest/helper_tap_passive.html
Normal file
64
gfx/layers/apz/test/mochitest/helper_tap_passive.html
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Ensure APZ doesn't wait for passive listeners</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
var touchdownTime;
|
||||
|
||||
function longPressLink() {
|
||||
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
|
||||
dump("Finished synthesizing touch-start, waiting for events...\n");
|
||||
});
|
||||
}
|
||||
|
||||
var touchstartReceived = false;
|
||||
function recordEvent(e) {
|
||||
if (!touchstartReceived) {
|
||||
touchstartReceived = true;
|
||||
is(e.type, 'touchstart', 'Got a touchstart');
|
||||
e.preventDefault(); // should be a no-op because it's a passive listener
|
||||
return;
|
||||
}
|
||||
|
||||
// If APZ decides to wait for the content response on a particular input block,
|
||||
// it needs to wait until both the touchstart and touchmove event are handled
|
||||
// by the main thread. In this case there is no touchmove at all, so APZ would
|
||||
// end up waiting indefinitely and time out the test. The fact that we get this
|
||||
// contextmenu event (mouselongtap on Windows) at all means that APZ decided
|
||||
// not to wait for the content response, which is the desired behaviour, since
|
||||
// the touchstart listener was registered as a passive listener.
|
||||
if (getPlatform() == "windows") {
|
||||
is(e.type, 'mouselongtap', 'Got a mouselongtap');
|
||||
} else {
|
||||
is(e.type, 'contextmenu', 'Got a contextmenu');
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
|
||||
dump("Finished synthesizing touch-end to clear state; finishing test...\n");
|
||||
subtestDone();
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
|
||||
if (getPlatform() == "windows") {
|
||||
SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
|
||||
} else {
|
||||
window.addEventListener('contextmenu', recordEvent, true);
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(longPressLink);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a id="b" href="#">Link to nowhere</a>
|
||||
</body>
|
||||
</html>
|
||||
115
gfx/layers/apz/test/mochitest/helper_touch_action.html
Normal file
115
gfx/layers/apz/test/mochitest/helper_touch_action.html
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Sanity touch-action test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function checkScroll(x, y, desc) {
|
||||
is(window.scrollX, x, desc + " - x axis");
|
||||
is(window.scrollY, y, desc + " - y axis");
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
var target = document.getElementById('target');
|
||||
|
||||
document.body.addEventListener('touchend', testDriver, { passive: true });
|
||||
|
||||
// drag the page up to scroll down by 50px
|
||||
yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
|
||||
"Synthesized native vertical drag (1), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(0, 50, "After first vertical drag, with pan-y" );
|
||||
|
||||
// switch style to pan-x
|
||||
document.body.style.touchAction = 'pan-x';
|
||||
ok(true, "Waiting for pan-x to propagate...");
|
||||
yield waitForAllPaintsFlushed(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// drag the page up to scroll down by 50px, but it won't happen because pan-x
|
||||
yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
|
||||
"Synthesized native vertical drag (2), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(0, 50, "After second vertical drag, with pan-x");
|
||||
|
||||
// drag the page left to scroll right by 50px
|
||||
yield ok(synthesizeNativeTouchDrag(target, 100, 10, -(50 + TOUCH_SLOP), 0),
|
||||
"Synthesized horizontal drag (1), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(50, 50, "After first horizontal drag, with pan-x");
|
||||
|
||||
// drag the page diagonally right/down to scroll up/left by 40px each axis;
|
||||
// only the x-axis will actually scroll because pan-x
|
||||
yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
|
||||
"Synthesized diagonal drag (1), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(10, 50, "After first diagonal drag, with pan-x");
|
||||
|
||||
// switch style back to pan-y
|
||||
document.body.style.touchAction = 'pan-y';
|
||||
ok(true, "Waiting for pan-y to propagate...");
|
||||
yield waitForAllPaintsFlushed(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// drag the page diagonally right/down to scroll up/left by 40px each axis;
|
||||
// only the y-axis will actually scroll because pan-y
|
||||
yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
|
||||
"Synthesized diagonal drag (2), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(10, 10, "After second diagonal drag, with pan-y");
|
||||
|
||||
// switch style to none
|
||||
document.body.style.touchAction = 'none';
|
||||
ok(true, "Waiting for none to propagate...");
|
||||
yield waitForAllPaintsFlushed(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// drag the page diagonally up/left to scroll down/right by 40px each axis;
|
||||
// neither will scroll because of touch-action
|
||||
yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
|
||||
"Synthesized diagonal drag (3), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(10, 10, "After third diagonal drag, with none");
|
||||
|
||||
document.body.style.touchAction = 'manipulation';
|
||||
ok(true, "Waiting for manipulation to propagate...");
|
||||
yield waitForAllPaintsFlushed(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// drag the page diagonally up/left to scroll down/right by 40px each axis;
|
||||
// both will scroll because of touch-action
|
||||
yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
|
||||
"Synthesized diagonal drag (4), waiting for touch-end event...");
|
||||
yield flushApzRepaints(testDriver);
|
||||
checkScroll(50, 50, "After fourth diagonal drag, with manipulation");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style="touch-action: pan-y">
|
||||
<div style="width: 5000px; height: 5000px; background-color: lightgreen;">
|
||||
This div makes the page scrollable on both axes.<br>
|
||||
This is the second line of text.<br>
|
||||
This is the third line of text.<br>
|
||||
This is the fourth line of text.
|
||||
</div>
|
||||
<!-- This fixed-position div remains in the same place relative to the browser chrome, so we
|
||||
can use it as a targeting device for synthetic touch events. The body will move around
|
||||
as we scroll, so we'd have to be constantly adjusting the synthetic drag coordinates
|
||||
if we used that as the target element. -->
|
||||
<div style="position:fixed; left: 10px; top: 10px; width: 1px; height: 1px" id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
143
gfx/layers/apz/test/mochitest/helper_touch_action_complex.html
Normal file
143
gfx/layers/apz/test/mochitest/helper_touch_action_complex.html
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Complex touch-action test</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function checkScroll(target, x, y, desc) {
|
||||
is(target.scrollLeft, x, desc + " - x axis");
|
||||
is(target.scrollTop, y, desc + " - y axis");
|
||||
}
|
||||
|
||||
function resetConfiguration(config, testDriver) {
|
||||
// Cycle through all the configuration_X elements, setting them to display:none
|
||||
// except for when X == config, in which case set it to display:block
|
||||
var i = 0;
|
||||
while (true) {
|
||||
i++;
|
||||
var element = document.getElementById('configuration_' + i);
|
||||
if (element == null) {
|
||||
if (i <= config) {
|
||||
ok(false, "The configuration requested was not encountered!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == config) {
|
||||
element.style.display = 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Also reset the scroll position on the scrollframe
|
||||
var s = document.getElementById('scrollframe');
|
||||
s.scrollLeft = 0;
|
||||
s.scrollTop = 0;
|
||||
|
||||
return waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
var scrollframe = document.getElementById('scrollframe');
|
||||
|
||||
document.body.addEventListener('touchend', testDriver, { passive: true });
|
||||
|
||||
// Helper function for the tests below.
|
||||
// Touch-pan configuration |configuration| towards scroll offset (dx, dy) with
|
||||
// the pan touching down at (x, y). Check that the final scroll offset is
|
||||
// (ex, ey). |desc| is some description string.
|
||||
function* scrollAndCheck(configuration, x, y, dx, dy, ex, ey, desc) {
|
||||
// Start with a clean slate
|
||||
yield resetConfiguration(configuration, testDriver);
|
||||
// Figure out the panning deltas
|
||||
if (dx != 0) {
|
||||
dx = -(dx + TOUCH_SLOP);
|
||||
}
|
||||
if (dy != 0) {
|
||||
dy = -(dy + TOUCH_SLOP);
|
||||
}
|
||||
// Do the pan
|
||||
yield ok(synthesizeNativeTouchDrag(scrollframe, x, y, dx, dy),
|
||||
"Synthesized drag of (" + dx + ", " + dy + ") on configuration " + configuration);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
// Check for expected scroll position
|
||||
checkScroll(scrollframe, ex, ey, 'configuration ' + configuration + ' ' + desc);
|
||||
}
|
||||
|
||||
// Test configuration_1, which contains two sibling elements that are
|
||||
// overlapping. The touch-action from the second sibling (which is on top)
|
||||
// should be used for the overlapping area.
|
||||
yield* scrollAndCheck(1, 25, 75, 20, 0, 20, 0, "first element horizontal scroll");
|
||||
yield* scrollAndCheck(1, 25, 75, 0, 50, 0, 0, "first element vertical scroll");
|
||||
yield* scrollAndCheck(1, 75, 75, 50, 0, 0, 0, "overlap horizontal scroll");
|
||||
yield* scrollAndCheck(1, 75, 75, 0, 50, 0, 50, "overlap vertical scroll");
|
||||
yield* scrollAndCheck(1, 125, 75, 20, 0, 0, 0, "second element horizontal scroll");
|
||||
yield* scrollAndCheck(1, 125, 75, 0, 50, 0, 50, "second element vertical scroll");
|
||||
|
||||
// Test configuration_2, which contains two overlapping elements with a
|
||||
// parent/child relationship. The parent has pan-x and the child has pan-y,
|
||||
// which means that panning on the parent should work horizontally only, and
|
||||
// on the child no panning should occur at all.
|
||||
yield* scrollAndCheck(2, 125, 125, 50, 50, 0, 0, "child scroll");
|
||||
yield* scrollAndCheck(2, 75, 75, 50, 50, 0, 0, "overlap scroll");
|
||||
yield* scrollAndCheck(2, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
|
||||
yield* scrollAndCheck(2, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
|
||||
|
||||
// Test configuration_3, which is the same as configuration_2, except the child
|
||||
// has a rotation transform applied. This forces the event regions on the two
|
||||
// elements to be built separately and then get merged.
|
||||
yield* scrollAndCheck(3, 125, 125, 50, 50, 0, 0, "child scroll");
|
||||
yield* scrollAndCheck(3, 75, 75, 50, 50, 0, 0, "overlap scroll");
|
||||
yield* scrollAndCheck(3, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
|
||||
yield* scrollAndCheck(3, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
|
||||
|
||||
// Test configuration_4 has two elements, one above the other, not overlapping,
|
||||
// and the second element is a child of the first. The parent has pan-x, the
|
||||
// child has pan-y, but that means panning horizontally on the parent should
|
||||
// work and panning in any direction on the child should not do anything.
|
||||
yield* scrollAndCheck(4, 75, 75, 50, 50, 50, 0, "parent diagonal scroll");
|
||||
yield* scrollAndCheck(4, 75, 150, 50, 50, 0, 0, "child diagonal scroll");
|
||||
}
|
||||
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scrollframe" style="width: 300px; height: 300px; overflow:scroll">
|
||||
<div id="scrolled_content" style="width: 1000px; height: 1000px; background-color: green">
|
||||
</div>
|
||||
<div id="configuration_1" style="display:none; position: relative; top: -1000px">
|
||||
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue"></div>
|
||||
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: -100px; left: 50px; background-color: yellow"></div>
|
||||
</div>
|
||||
<div id="configuration_2" style="display:none; position: relative; top: -1000px">
|
||||
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
|
||||
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="configuration_3" style="display:none; position: relative; top: -1000px">
|
||||
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
|
||||
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow; transform: rotate(90deg)"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="configuration_4" style="display:none; position: relative; top: -1000px">
|
||||
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
|
||||
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 125px; background-color: yellow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
246
gfx/layers/apz/test/mochitest/helper_touch_action_regions.html
Normal file
246
gfx/layers/apz/test/mochitest/helper_touch_action_regions.html
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width; initial-scale=1.0">
|
||||
<title>Test to ensure APZ doesn't always wait for touch-action</title>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function failure(e) {
|
||||
ok(false, "This event listener should not have triggered: " + e.type);
|
||||
}
|
||||
|
||||
function success(e) {
|
||||
success.triggered = true;
|
||||
}
|
||||
|
||||
// This helper function provides a way for the child process to synchronously
|
||||
// check how many touch events the chrome process main-thread has processed. This
|
||||
// function can be called with three values: 'start', 'report', and 'end'.
|
||||
// The 'start' invocation sets up the listeners, and should be invoked before
|
||||
// the touch events of interest are generated. This should only be called once.
|
||||
// This returns true on success, and false on failure.
|
||||
// The 'report' invocation can be invoked multiple times, and returns an object
|
||||
// (in JSON string format) containing the counters.
|
||||
// The 'end' invocation tears down the listeners, and should be invoked once
|
||||
// at the end to clean up. Returns true on success, false on failure.
|
||||
function chromeTouchEventCounter(operation) {
|
||||
function chromeProcessCounter() {
|
||||
addMessageListener('start', function() {
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
if (typeof topWin.eventCounts != 'undefined') {
|
||||
dump('Found pre-existing eventCounts object on the top window!\n');
|
||||
return false;
|
||||
}
|
||||
topWin.eventCounts = { 'touchstart': 0, 'touchmove': 0, 'touchend': 0 };
|
||||
topWin.counter = function(e) {
|
||||
topWin.eventCounts[e.type]++;
|
||||
}
|
||||
|
||||
topWin.addEventListener('touchstart', topWin.counter, { passive: true });
|
||||
topWin.addEventListener('touchmove', topWin.counter, { passive: true });
|
||||
topWin.addEventListener('touchend', topWin.counter, { passive: true });
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
addMessageListener('report', function() {
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
return JSON.stringify(topWin.eventCounts);
|
||||
});
|
||||
|
||||
addMessageListener('end', function() {
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
|
||||
if (typeof topWin.eventCounts == 'undefined') {
|
||||
dump('The eventCounts object was not found on the top window!\n');
|
||||
return false;
|
||||
}
|
||||
topWin.removeEventListener('touchstart', topWin.counter);
|
||||
topWin.removeEventListener('touchmove', topWin.counter);
|
||||
topWin.removeEventListener('touchend', topWin.counter);
|
||||
delete topWin.counter;
|
||||
delete topWin.eventCounts;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof chromeTouchEventCounter.chromeHelper == 'undefined') {
|
||||
// This is the first time getSnapshot is being called; do initialization
|
||||
chromeTouchEventCounter.chromeHelper = SpecialPowers.loadChromeScript(chromeProcessCounter);
|
||||
SimpleTest.registerCleanupFunction(function() { chromeTouchEventCounter.chromeHelper.destroy() });
|
||||
}
|
||||
|
||||
return chromeTouchEventCounter.chromeHelper.sendSyncMessage(operation, "");
|
||||
}
|
||||
|
||||
// Simple wrapper that waits until the chrome process has seen |count| instances
|
||||
// of the |eventType| event. Returns true on success, and false if 10 seconds
|
||||
// go by without the condition being satisfied.
|
||||
function waitFor(eventType, count) {
|
||||
var start = Date.now();
|
||||
while (JSON.parse(chromeTouchEventCounter('report'))[eventType] != count) {
|
||||
if (Date.now() - start > 10000) {
|
||||
// It's taking too long, let's abort
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
// The main part of this test should run completely before the child process'
|
||||
// main-thread deals with the touch event, so check to make sure that happens.
|
||||
document.body.addEventListener('touchstart', failure, { passive: true });
|
||||
|
||||
// What we want here is to synthesize all of the touch events (from this code in
|
||||
// the child process), and have the chrome process generate and process them,
|
||||
// but not allow the events to be dispatched back into the child process until
|
||||
// later. This allows us to ensure that the APZ in the chrome process is not
|
||||
// waiting for the child process to send notifications upon processing the
|
||||
// events. If it were doing so, the APZ would block and this test would fail.
|
||||
|
||||
// In order to actually implement this, we call the synthesize functions with
|
||||
// a async callback in between. The synthesize functions just queue up a
|
||||
// runnable on the child process main thread and return immediately, so with
|
||||
// the async callbacks, the child process main thread queue looks like
|
||||
// this after we're done setting it up:
|
||||
// synthesizeTouchStart
|
||||
// callback testDriver
|
||||
// synthesizeTouchMove
|
||||
// callback testDriver
|
||||
// ...
|
||||
// synthesizeTouchEnd
|
||||
// callback testDriver
|
||||
//
|
||||
// If, after setting up this queue, we yield once, the first synthesization and
|
||||
// callback will run - this will send a synthesization message to the chrome
|
||||
// process, and return control back to us right away. When the chrome process
|
||||
// processes with the synthesized event, it will dispatch the DOM touch event
|
||||
// back to the child process over IPC, which will go into the end of the child
|
||||
// process main thread queue, like so:
|
||||
// synthesizeTouchStart (done)
|
||||
// invoke testDriver (done)
|
||||
// synthesizeTouchMove
|
||||
// invoke testDriver
|
||||
// ...
|
||||
// synthesizeTouchEnd
|
||||
// invoke testDriver
|
||||
// handle DOM touchstart <-- touchstart goes at end of queue
|
||||
//
|
||||
// As we continue yielding one at a time, the synthesizations run, and the
|
||||
// touch events get added to the end of the queue. As we yield, we take
|
||||
// snapshots in the chrome process, to make sure that the APZ has started
|
||||
// scrolling even though we know we haven't yet processed the DOM touch events
|
||||
// in the child process yet.
|
||||
//
|
||||
// Note that the "async callback" we use here is SpecialPowers.executeSoon,
|
||||
// because nothing else does exactly what we want:
|
||||
// - setTimeout(..., 0) does not maintain ordering, because it respects the
|
||||
// time delta provided (i.e. the callback can jump the queue to meet its
|
||||
// deadline).
|
||||
// - SpecialPowers.spinEventLoop and SpecialPowers.executeAfterFlushingMessageQueue
|
||||
// are not e10s friendly, and can get arbitrarily delayed due to IPC
|
||||
// round-trip time.
|
||||
// - SimpleTest.executeSoon has a codepath that delegates to setTimeout, so
|
||||
// is less reliable if it ever decides to switch to that codepath.
|
||||
|
||||
// The other problem we need to deal with is the asynchronicity in the chrome
|
||||
// process. That is, we might request a snapshot before the chrome process has
|
||||
// actually synthesized the event and processed it. To guard against this, we
|
||||
// register a thing in the chrome process that counts the touch events that
|
||||
// have been dispatched, and poll that thing synchronously in order to make
|
||||
// sure we only snapshot after the event in question has been processed.
|
||||
// That's what the chromeTouchEventCounter business is all about. The sync
|
||||
// polling looks bad but in practice only ends up needing to poll once or
|
||||
// twice before the condition is satisfied, and as an extra precaution we add
|
||||
// a time guard so it fails after 10s of polling.
|
||||
|
||||
// So, here we go...
|
||||
|
||||
// Set up the chrome process touch listener
|
||||
ok(chromeTouchEventCounter('start'), "Chrome touch counter registered");
|
||||
|
||||
// Set up the child process events and callbacks
|
||||
var scroller = document.getElementById('scroller');
|
||||
synthesizeNativeTouch(scroller, 10, 110, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
|
||||
SpecialPowers.executeSoon(testDriver);
|
||||
for (var i = 1; i < 10; i++) {
|
||||
synthesizeNativeTouch(scroller, 10, 110 - (i * 10), SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
|
||||
SpecialPowers.executeSoon(testDriver);
|
||||
}
|
||||
synthesizeNativeTouch(scroller, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, null, 0);
|
||||
SpecialPowers.executeSoon(testDriver);
|
||||
ok(true, "Finished setting up event queue");
|
||||
|
||||
// Get our baseline snapshot
|
||||
var rect = rectRelativeToScreen(scroller);
|
||||
var lastSnapshot = getSnapshot(rect);
|
||||
ok(true, "Got baseline snapshot");
|
||||
|
||||
yield; // this will tell the chrome process to synthesize the touchstart event
|
||||
// and then we wait to make sure it got processed:
|
||||
ok(waitFor('touchstart', 1), "Touchstart processed in chrome process");
|
||||
|
||||
// Loop through the touchmove events
|
||||
for (var i = 1; i < 10; i++) {
|
||||
yield;
|
||||
ok(waitFor('touchmove', i), "Touchmove processed in chrome process");
|
||||
|
||||
var snapshot = getSnapshot(rect);
|
||||
if (i == 1) {
|
||||
// The first touchmove is consumed to get us into the panning state, so
|
||||
// no actual panning occurs
|
||||
ok(lastSnapshot == snapshot, "Snapshot 1 was the same as baseline");
|
||||
} else {
|
||||
ok(lastSnapshot != snapshot, "Snapshot " + i + " was different from the previous one");
|
||||
}
|
||||
lastSnapshot = snapshot;
|
||||
}
|
||||
|
||||
// Wait for the touchend as well, just for good form
|
||||
yield;
|
||||
ok(waitFor('touchend', 1), "Touchend processed in chrome process");
|
||||
|
||||
// Clean up the chrome process hooks
|
||||
chromeTouchEventCounter('end');
|
||||
|
||||
// Now we are going to release our grip on the child process main thread,
|
||||
// so that all the DOM events that were queued up can be processed. We
|
||||
// register a touchstart listener to make sure this happens.
|
||||
document.body.removeEventListener('touchstart', failure);
|
||||
document.body.addEventListener('touchstart', success, { passive: true });
|
||||
yield flushApzRepaints(testDriver);
|
||||
ok(success.triggered, "The touchstart event handler was triggered after snapshotting completed");
|
||||
document.body.removeEventListener('touchstart', success);
|
||||
}
|
||||
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
// This is probably android, where everything is single-process. The
|
||||
// test structure depends on e10s, so the test won't run properly on
|
||||
// this platform. Skip it
|
||||
ok(true, "Skipping test because it is designed to run from the content process");
|
||||
subtestDone();
|
||||
} else {
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(subtestDone);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="scroller" style="width: 400px; height: 400px; overflow: scroll; touch-action: pan-y">
|
||||
<div style="width: 200px; height: 200px; background-color: lightgreen;">
|
||||
This is a colored div that will move on the screen as the scroller scrolls.
|
||||
</div>
|
||||
<div style="width: 1000px; height: 1000px; background-color: lightblue">
|
||||
This is a large div to make the scroller scrollable.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
67
gfx/layers/apz/test/mochitest/mochitest.ini
Normal file
67
gfx/layers/apz/test/mochitest/mochitest.ini
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
apz_test_native_event_utils.js
|
||||
apz_test_utils.js
|
||||
helper_basic_pan.html
|
||||
helper_bug982141.html
|
||||
helper_bug1151663.html
|
||||
helper_bug1162771.html
|
||||
helper_bug1271432.html
|
||||
helper_bug1280013.html
|
||||
helper_bug1285070.html
|
||||
helper_bug1299195.html
|
||||
helper_click.html
|
||||
helper_div_pan.html
|
||||
helper_drag_click.html
|
||||
helper_drag_scroll.html
|
||||
helper_iframe_pan.html
|
||||
helper_iframe1.html
|
||||
helper_iframe2.html
|
||||
helper_long_tap.html
|
||||
helper_scroll_inactive_perspective.html
|
||||
helper_scroll_inactive_zindex.html
|
||||
helper_scroll_on_position_fixed.html
|
||||
helper_scrollto_tap.html
|
||||
helper_subframe_style.css
|
||||
helper_tall.html
|
||||
helper_tap.html
|
||||
helper_tap_fullzoom.html
|
||||
helper_tap_passive.html
|
||||
helper_touch_action.html
|
||||
helper_touch_action_regions.html
|
||||
helper_touch_action_complex.html
|
||||
tags = apz
|
||||
[test_bug982141.html]
|
||||
[test_bug1151663.html]
|
||||
[test_bug1151667.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_bug1253683.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_bug1277814.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_bug1304689.html]
|
||||
[test_bug1304689-2.html]
|
||||
[test_frame_reconstruction.html]
|
||||
[test_group_mouseevents.html]
|
||||
skip-if = (toolkit == 'android') # mouse events not supported on mobile
|
||||
[test_group_pointerevents.html]
|
||||
[test_group_touchevents.html]
|
||||
[test_group_wheelevents.html]
|
||||
skip-if = (toolkit == 'android') # wheel events not supported on mobile
|
||||
[test_group_zoom.html]
|
||||
skip-if = (toolkit != 'android') # only android supports zoom
|
||||
[test_interrupted_reflow.html]
|
||||
[test_layerization.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_scroll_inactive_bug1190112.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_scroll_inactive_flattened_frame.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_scroll_subframe_scrollbar.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_touch_listeners_impacting_wheel.html]
|
||||
skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
|
||||
[test_wheel_scroll.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
[test_wheel_transactions.html]
|
||||
skip-if = (os == 'android') # wheel events not supported on mobile
|
||||
38
gfx/layers/apz/test/mochitest/test_bug1151663.html
Normal file
38
gfx/layers/apz/test/mochitest/test_bug1151663.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1151663</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Run the actual test in its own window, because it requires that the
|
||||
// root APZC be scrollable. Mochitest pages themselves often run
|
||||
// inside an iframe which means we have no control over the root APZC.
|
||||
var w = null;
|
||||
window.onload = function() {
|
||||
pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
|
||||
w = window.open("helper_bug1151663.html", "_blank");
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
w.close();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
|
||||
</body>
|
||||
</html>
|
||||
65
gfx/layers/apz/test/mochitest/test_bug1151667.html
Normal file
65
gfx/layers/apz/test/mochitest/test_bug1151667.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1151667
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1151667</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#subframe {
|
||||
margin-top: 100px;
|
||||
height: 500px;
|
||||
width: 500px;
|
||||
overflow: scroll;
|
||||
}
|
||||
#subframe-content {
|
||||
height: 1000px;
|
||||
width: 500px;
|
||||
/* the background is so that we can see it scroll*/
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
#page-content {
|
||||
height: 5000px;
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151667">Mozilla Bug 1151667</a>
|
||||
<p id="display"></p>
|
||||
<div id="subframe">
|
||||
<!-- This makes sure the subframe is scrollable -->
|
||||
<div id="subframe-content"></div>
|
||||
</div>
|
||||
<!-- This makes sure the page is also scrollable, so it (rather than the subframe)
|
||||
is considered the primary async-scrollable frame, and so the subframe isn't
|
||||
layerized upon page load. -->
|
||||
<div id="page-content"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function startTest() {
|
||||
var subframe = document.getElementById('subframe');
|
||||
synthesizeNativeWheelAndWaitForScrollEvent(subframe, 100, 150, 0, -10, continueTest);
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
var subframe = document.getElementById('subframe');
|
||||
is(subframe.scrollTop > 0, true, "We should have scrolled the subframe down");
|
||||
is(document.documentElement.scrollTop, 0, "We should not have scrolled the page");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
waitUntilApzStable().then(startTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
59
gfx/layers/apz/test/mochitest/test_bug1253683.html
Normal file
59
gfx/layers/apz/test/mochitest/test_bug1253683.html
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1253683
|
||||
-->
|
||||
<head>
|
||||
<title>Test to ensure non-scrollable frames don't get layerized</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="container" style="height: 500px; overflow:scroll">
|
||||
<pre id="no_layer" style="background-color: #f5f5f5; margin: 15px; padding: 15px; margin-top: 100px; border: 1px solid #eee; overflow:scroll">sample code here</pre>
|
||||
<div style="height: 5000px">spacer to make the 'container' div the root scrollable element</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function* test(testDriver) {
|
||||
var container = document.getElementById('container');
|
||||
var no_layer = document.getElementById('no_layer');
|
||||
|
||||
// Check initial state
|
||||
is(container.scrollTop, 0, "Initial scrollY should be 0");
|
||||
ok(!isLayerized('no_layer'), "initially 'no_layer' should not be layerized");
|
||||
|
||||
// Scrolling over outer1 should layerize outer1, but not inner1.
|
||||
yield moveMouseAndScrollWheelOver(no_layer, 10, 10, testDriver, true);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
ok(container.scrollTop > 0, "We should have scrolled the body");
|
||||
ok(!isLayerized('no_layer'), "no_layer should still not be layerized");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off displayport expiry so that we don't miss failures where the
|
||||
// displayport is set and expired before we check for layerization.
|
||||
// Also enable APZ test logging, since we use that data to determine whether
|
||||
// a scroll frame was layerized.
|
||||
pushPrefs([["apz.displayport_expiry_ms", 0],
|
||||
["apz.test.logging_enabled", true]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
106
gfx/layers/apz/test/mochitest/test_bug1277814.html
Normal file
106
gfx/layers/apz/test/mochitest/test_bug1277814.html
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1277814
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1277814</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
function* test(testDriver) {
|
||||
// Trigger the buggy scenario
|
||||
var subframe = document.getElementById('bug1277814-div');
|
||||
subframe.classList.add('a');
|
||||
|
||||
// The transform change is animated, so let's step through 1s of animation
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
for (var i = 0; i < 60; i++) {
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
}
|
||||
utils.restoreNormalRefresh();
|
||||
|
||||
// Wait for the layer tree with any updated dispatch-to-content region to
|
||||
// get pushed over to the APZ
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// Trigger layerization of the subframe by scrolling the wheel over it
|
||||
yield moveMouseAndScrollWheelOver(subframe, 10, 10, testDriver);
|
||||
|
||||
// Give APZ the chance to compute a displayport, and content
|
||||
// to render based on it.
|
||||
yield waitForApzFlushedRepaints(testDriver);
|
||||
|
||||
// Examine the content-side APZ test data
|
||||
var contentTestData = utils.getContentAPZTestData();
|
||||
|
||||
// Test that the scroll frame for the div 'bug1277814-div' appears in
|
||||
// the APZ test data. The bug this test is for causes the displayport
|
||||
// calculation for this scroll frame to go wrong, causing it not to
|
||||
// become layerized.
|
||||
contentTestData = convertTestData(contentTestData);
|
||||
var foundIt = false;
|
||||
for (var seqNo in contentTestData.paints) {
|
||||
var paint = contentTestData.paints[seqNo];
|
||||
for (var scrollId in paint) {
|
||||
var scrollFrame = paint[scrollId];
|
||||
if ('contentDescription' in scrollFrame &&
|
||||
scrollFrame['contentDescription'].includes('bug1277814-div')) {
|
||||
foundIt = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
SimpleTest.ok(foundIt, "expected to find APZ test data for bug1277814-div");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
pushPrefs([["apz.test.logging_enabled", true]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#bug1277814-div
|
||||
{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: .5em;
|
||||
overflow: auto;
|
||||
color: white;
|
||||
background: green;
|
||||
max-width: 30em;
|
||||
max-height: 6em;
|
||||
visibility: hidden;
|
||||
transform: scaleY(0);
|
||||
transition: transform .15s ease-out, visibility 0s ease .15s;
|
||||
}
|
||||
#bug1277814-div.a
|
||||
{
|
||||
visibility: visible;
|
||||
transform: scaleY(1);
|
||||
transition: transform .15s ease-out;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Use a unique id because we'll be checking for it in the content
|
||||
description logged in the APZ test data -->
|
||||
<div id="bug1277814-div">
|
||||
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
|
||||
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
|
||||
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
|
||||
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
|
||||
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
|
||||
<button>click me</button>
|
||||
</body>
|
||||
</html>
|
||||
131
gfx/layers/apz/test/mochitest/test_bug1304689-2.html
Normal file
131
gfx/layers/apz/test/mochitest/test_bug1304689-2.html
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1285070</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
#outer {
|
||||
height: 400px;
|
||||
width: 415px;
|
||||
overflow: scroll;
|
||||
position: relative;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
#outer.contentBefore::before {
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
var elm = document.getElementById('outer');
|
||||
|
||||
// Set margins on the element, to ensure it is layerized
|
||||
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// Take control of the refresh driver
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
|
||||
// Start a smooth-scroll animation in the compositor and let it go a few
|
||||
// frames, so that there is some "user scrolling" going on (per the comment
|
||||
// in AsyncPanZoomController::NotifyLayersUpdated)
|
||||
elm.scrollTop = 10;
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
|
||||
// Do another scroll update but also do a frame reconstruction within the same
|
||||
// tick of the refresh driver.
|
||||
elm.scrollTop = 100;
|
||||
elm.classList.add('contentBefore');
|
||||
|
||||
// Now let everything settle and all the animations run out
|
||||
for (var i = 0; i < 60; i++) {
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
}
|
||||
utils.restoreNormalRefresh();
|
||||
|
||||
yield flushApzRepaints(testDriver);
|
||||
is(elm.scrollTop, 100, "The scrollTop now should be y=100");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
pushPrefs([["apz.displayport_expiry_ms", 0]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="outer">
|
||||
<div id="inner">
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
135
gfx/layers/apz/test/mochitest/test_bug1304689.html
Normal file
135
gfx/layers/apz/test/mochitest/test_bug1304689.html
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1285070</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
#outer {
|
||||
height: 400px;
|
||||
width: 415px;
|
||||
overflow: scroll;
|
||||
position: relative;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
#outer.instant {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
#outer.contentBefore::before {
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript">
|
||||
|
||||
function* test(testDriver) {
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
var elm = document.getElementById('outer');
|
||||
|
||||
// Set margins on the element, to ensure it is layerized
|
||||
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// Take control of the refresh driver
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
|
||||
// Start a smooth-scroll animation in the compositor and let it go a few
|
||||
// frames, so that there is some "user scrolling" going on (per the comment
|
||||
// in AsyncPanZoomController::NotifyLayersUpdated)
|
||||
elm.scrollTop = 10;
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
|
||||
// Do another scroll update but also do a frame reconstruction within the same
|
||||
// tick of the refresh driver.
|
||||
elm.classList.add('instant');
|
||||
elm.scrollTop = 100;
|
||||
elm.classList.add('contentBefore');
|
||||
|
||||
// Now let everything settle and all the animations run out
|
||||
for (var i = 0; i < 60; i++) {
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
}
|
||||
utils.restoreNormalRefresh();
|
||||
|
||||
yield flushApzRepaints(testDriver);
|
||||
is(elm.scrollTop, 100, "The scrollTop now should be y=100");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
pushPrefs([["apz.displayport_expiry_ms", 0]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="outer">
|
||||
<div id="inner">
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
this is some scrollable text.<br>
|
||||
this is a second line to make the scrolling more obvious.<br>
|
||||
and a third for good measure.<br>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
38
gfx/layers/apz/test/mochitest/test_bug982141.html
Normal file
38
gfx/layers/apz/test/mochitest/test_bug982141.html
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 982141</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Run the actual test in its own window, because it requires that the
|
||||
// root APZC not be scrollable. Mochitest pages themselves often run
|
||||
// inside an iframe which means we have no control over the root APZC.
|
||||
var w = null;
|
||||
window.onload = function() {
|
||||
pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
|
||||
w = window.open("helper_bug982141.html", "_blank");
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
w.close();
|
||||
SimpleTest.finish();
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
|
||||
</body>
|
||||
</html>
|
||||
218
gfx/layers/apz/test/mochitest/test_frame_reconstruction.html
Normal file
218
gfx/layers/apz/test/mochitest/test_frame_reconstruction.html
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1235899
|
||||
-->
|
||||
<head>
|
||||
<title>Test for bug 1235899</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
.outer {
|
||||
height: 400px;
|
||||
width: 415px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.inner {
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
.outer.contentBefore::before {
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1235899">Mozilla Bug 1235899</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p>You should be able to fling this list without it stopping abruptly</p>
|
||||
<div class="outer">
|
||||
<div class="inner">
|
||||
<ol>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
<li>Some text</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
function* test(testDriver) {
|
||||
var elm = document.getElementsByClassName('inner')[0];
|
||||
elm.scrollTop = 0;
|
||||
yield flushApzRepaints(testDriver);
|
||||
|
||||
// Take over control of the refresh driver and compositor
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
|
||||
// Kick off an APZ smooth-scroll to 0,200
|
||||
elm.scrollTo(0, 200);
|
||||
yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
|
||||
|
||||
// Let's do a couple of frames of the animation, and make sure it's going
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
yield flushApzRepaints(testDriver);
|
||||
ok(elm.scrollTop > 0, "APZ animation in progress", "scrollTop is now " + elm.scrollTop);
|
||||
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
|
||||
|
||||
var frameReconstructionTriggered = 0;
|
||||
// Register the listener that triggers the frame reconstruction
|
||||
elm.onscroll = function() {
|
||||
// Do the reconstruction
|
||||
elm.parentNode.classList.add('contentBefore');
|
||||
frameReconstructionTriggered++;
|
||||
// schedule a thing to undo the changes above
|
||||
setTimeout(function() {
|
||||
elm.parentNode.classList.remove('contentBefore');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// and do a few more frames of the animation, this should trigger the listener
|
||||
// and the frame reconstruction
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
yield flushApzRepaints(testDriver);
|
||||
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
|
||||
ok(frameReconstructionTriggered > 0, "Frame reconstruction triggered", "reconstruction triggered " + frameReconstructionTriggered + " times");
|
||||
|
||||
// and now run to completion
|
||||
for (var i = 0; i < 100; i++) {
|
||||
utils.advanceTimeAndRefresh(16);
|
||||
}
|
||||
utils.restoreNormalRefresh();
|
||||
yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
|
||||
yield flushApzRepaints(testDriver);
|
||||
|
||||
is(elm.scrollTop, 200, "Element should have scrolled by 200px");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
|
||||
waitUntilApzStable()
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
35
gfx/layers/apz/test/mochitest/test_group_mouseevents.html
Normal file
35
gfx/layers/apz/test/mochitest/test_group_mouseevents.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Various mouse tests that spawn in new windows</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
var subtests = [
|
||||
// Sanity test to synthesize a mouse click
|
||||
{'file': 'helper_click.html?dtc=false'},
|
||||
// Same as above, but with a dispatch-to-content region that exercises the
|
||||
// main-thread notification codepaths for mouse events
|
||||
{'file': 'helper_click.html?dtc=true'},
|
||||
// Sanity test for click but with some mouse movement between the down and up
|
||||
{'file': 'helper_drag_click.html'},
|
||||
// Test for dragging on a fake-scrollbar element that scrolls the page
|
||||
{'file': 'helper_drag_scroll.html'}
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
runSubtestsSeriallyInFreshWindows(subtests)
|
||||
.then(SimpleTest.finish);
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
31
gfx/layers/apz/test/mochitest/test_group_pointerevents.html
Normal file
31
gfx/layers/apz/test/mochitest/test_group_pointerevents.html
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1285070
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1285070</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
var subtests = [
|
||||
{'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
|
||||
{'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
runSubtestsSeriallyInFreshWindows(subtests)
|
||||
.then(SimpleTest.finish);
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
104
gfx/layers/apz/test/mochitest/test_group_touchevents.html
Normal file
104
gfx/layers/apz/test/mochitest/test_group_touchevents.html
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Various touch tests that spawn in new windows</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
var basic_pan_prefs = [
|
||||
// Dropping the touch slop to 0 makes the tests easier to write because
|
||||
// we can just do a one-pixel drag to get over the pan threshold rather
|
||||
// than having to hard-code some larger value.
|
||||
["apz.touch_start_tolerance", "0.0"],
|
||||
// The touchstart from the drag can turn into a long-tap if the touch-move
|
||||
// events get held up. Try to prevent that by making long-taps require
|
||||
// a 10 second hold. Note that we also cannot enable chaos mode on this
|
||||
// test for this reason, since chaos mode can cause the long-press timer
|
||||
// to fire sooner than the pref dictates.
|
||||
["ui.click_hold_context_menus.delay", 10000],
|
||||
// The subtests in this test do touch-drags to pan the page, but we don't
|
||||
// want those pans to turn into fling animations, so we increase the
|
||||
// fling min velocity requirement absurdly high.
|
||||
["apz.fling_min_velocity_threshold", "10000"],
|
||||
// The helper_div_pan's div gets a displayport on scroll, but if the
|
||||
// test takes too long the displayport can expire before the new scroll
|
||||
// position is synced back to the main thread. So we disable displayport
|
||||
// expiry for these tests.
|
||||
["apz.displayport_expiry_ms", 0],
|
||||
];
|
||||
|
||||
var touch_action_prefs = basic_pan_prefs.slice(); // make a copy
|
||||
touch_action_prefs.push(["layout.css.touch_action.enabled", true]);
|
||||
|
||||
var isWindows = (getPlatform() == "windows");
|
||||
|
||||
var subtests = [
|
||||
// Simple tests to exercise basic panning behaviour
|
||||
{'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
|
||||
{'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
|
||||
{'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},
|
||||
|
||||
// Simple test to exercise touch-tapping behaviour
|
||||
{'file': 'helper_tap.html'},
|
||||
// Tapping, but with a full-zoom applied
|
||||
{'file': 'helper_tap_fullzoom.html'},
|
||||
|
||||
// For the following two tests, disable displayport suppression to make sure it
|
||||
// doesn't interfere with the test by scheduling paints non-deterministically.
|
||||
{'file': 'helper_scrollto_tap.html?true', 'prefs': [["apz.paint_skipping.enabled", true]], 'dp_suppression': false},
|
||||
{'file': 'helper_scrollto_tap.html?false', 'prefs': [["apz.paint_skipping.enabled", false]], 'dp_suppression': false},
|
||||
|
||||
// Taps on media elements to make sure the touchend event is delivered
|
||||
// properly. We increase the long-tap timeout to ensure it doesn't get trip
|
||||
// during the tap.
|
||||
// Also this test (on Windows) cannot satisfy the OS requirement of providing
|
||||
// an injected touch event every 100ms, because it waits for a paint between
|
||||
// the touchstart and the touchend, so we have to use the "fake injection"
|
||||
// code instead.
|
||||
{'file': 'helper_bug1162771.html', 'prefs': [["ui.click_hold_context_menus.delay", 10000],
|
||||
["apz.test.fails_with_native_injection", isWindows]]},
|
||||
|
||||
// As with the previous test, this test cannot inject touch events every 100ms
|
||||
// because it waits for a long-tap, so we have to use the "fake injection" code
|
||||
// instead.
|
||||
{'file': 'helper_long_tap.html', 'prefs': [["apz.test.fails_with_native_injection", isWindows]]},
|
||||
|
||||
// For the following test, we want to make sure APZ doesn't wait for a content
|
||||
// response that is never going to arrive. To detect this we set the content response
|
||||
// timeout to a day, so that the entire test times out and fails if APZ does
|
||||
// end up waiting.
|
||||
{'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
|
||||
["apz.test.fails_with_native_injection", isWindows]]},
|
||||
|
||||
// Simple test to exercise touch-action CSS property
|
||||
{'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
|
||||
// More complex touch-action tests, with overlapping regions and such
|
||||
{'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
|
||||
// Tests that touch-action CSS properties are handled in APZ without waiting
|
||||
// on the main-thread, when possible
|
||||
{'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
|
||||
if (getPlatform() == "android") {
|
||||
// This has a lot of subtests, and Android emulators are slow.
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
runSubtestsSeriallyInFreshWindows(subtests)
|
||||
.then(SimpleTest.finish);
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
41
gfx/layers/apz/test/mochitest/test_group_wheelevents.html
Normal file
41
gfx/layers/apz/test/mochitest/test_group_wheelevents.html
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Various wheel-scrolling tests that spawn in new windows</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
var prefs = [
|
||||
// turn off smooth scrolling so that we don't have to wait for
|
||||
// APZ animations to finish before sampling the scroll offset
|
||||
['general.smoothScroll', false],
|
||||
// ensure that any mouse movement will trigger a new wheel transaction,
|
||||
// because in this test we move the mouse a bunch and want to recalculate
|
||||
// the target APZC after each such movement.
|
||||
['mousewheel.transaction.ignoremovedelay', 0],
|
||||
['mousewheel.transaction.timeout', 0]
|
||||
]
|
||||
|
||||
var subtests = [
|
||||
{'file': 'helper_scroll_on_position_fixed.html', 'prefs': prefs},
|
||||
{'file': 'helper_bug1271432.html', 'prefs': prefs},
|
||||
{'file': 'helper_scroll_inactive_perspective.html', 'prefs': prefs},
|
||||
{'file': 'helper_scroll_inactive_zindex.html', 'prefs': prefs}
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
runSubtestsSeriallyInFreshWindows(subtests)
|
||||
.then(SimpleTest.finish);
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
44
gfx/layers/apz/test/mochitest/test_group_zoom.html
Normal file
44
gfx/layers/apz/test/mochitest/test_group_zoom.html
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Various zoom-related tests that spawn in new windows</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
var prefs = [
|
||||
// We need the APZ paint logging information
|
||||
["apz.test.logging_enabled", true],
|
||||
// Dropping the touch slop to 0 makes the tests easier to write because
|
||||
// we can just do a one-pixel drag to get over the pan threshold rather
|
||||
// than having to hard-code some larger value.
|
||||
["apz.touch_start_tolerance", "0.0"],
|
||||
// The subtests in this test do touch-drags to pan the page, but we don't
|
||||
// want those pans to turn into fling animations, so we increase the
|
||||
// fling-stop threshold velocity to absurdly high.
|
||||
["apz.fling_stopped_threshold", "10000"],
|
||||
// The helper_bug1280013's div gets a displayport on scroll, but if the
|
||||
// test takes too long the displayport can expire before we read the value
|
||||
// out of the test. So we disable displayport expiry for these tests.
|
||||
["apz.displayport_expiry_ms", 0],
|
||||
];
|
||||
|
||||
var subtests = [
|
||||
{'file': 'helper_bug1280013.html', 'prefs': prefs},
|
||||
];
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.onload = function() {
|
||||
runSubtestsSeriallyInFreshWindows(subtests)
|
||||
.then(SimpleTest.finish);
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
719
gfx/layers/apz/test/mochitest/test_interrupted_reflow.html
Normal file
719
gfx/layers/apz/test/mochitest/test_interrupted_reflow.html
Normal file
|
|
@ -0,0 +1,719 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1292781
|
||||
-->
|
||||
<head>
|
||||
<title>Test for bug 1292781</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
.outer {
|
||||
height: 400px;
|
||||
width: 415px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.inner {
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
}
|
||||
.inner div:nth-child(even) {
|
||||
background-color: lightblue;
|
||||
}
|
||||
.inner div:nth-child(odd) {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.outer.contentBefore::before {
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292781">Mozilla Bug 1292781</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p>The frame reconstruction should not leave this scrollframe in a bad state</p>
|
||||
<div class="outer">
|
||||
<div class="inner">
|
||||
this is the top of the scrollframe.
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
this is near the top of the scrollframe.
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
this is near the bottom of the scrollframe.
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
<div>this is a box</div>
|
||||
this is the bottom of the scrollframe.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script type="text/javascript">
|
||||
|
||||
// Returns a list of async scroll offsets that the |inner| element had, one for
|
||||
// each paint.
|
||||
function getAsyncScrollOffsets(aPaintsToIgnore) {
|
||||
var offsets = [];
|
||||
var compositorTestData = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData();
|
||||
var buckets = compositorTestData.paints.slice(aPaintsToIgnore);
|
||||
ok(buckets.length >= 3, "Expected at least three paints in the compositor test data");
|
||||
var childIsLayerized = false;
|
||||
for (var i = 0; i < buckets.length; ++i) {
|
||||
var apzcTree = buildApzcTree(convertScrollFrameData(buckets[i].scrollFrames));
|
||||
var rcd = findRcdNode(apzcTree);
|
||||
if (rcd == null) {
|
||||
continue;
|
||||
}
|
||||
if (rcd.children.length > 0) {
|
||||
// The child may not be layerized in the first few paints, but once it is
|
||||
// layerized, it should stay layerized.
|
||||
childIsLayerized = true;
|
||||
}
|
||||
if (!childIsLayerized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ok(rcd.children.length == 1, "Root content APZC has exactly one child");
|
||||
var scroll = rcd.children[0].asyncScrollOffset;
|
||||
var pieces = scroll.replace(/[()\s]+/g, '').split(',');
|
||||
is(pieces.length, 2, "expected string of form (x,y)");
|
||||
offsets.push({ x: parseInt(pieces[0]),
|
||||
y: parseInt(pieces[1]) });
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
|
||||
// The APZ test data accumulates whenever a test turns it on. We just want
|
||||
// the data for this test, so we check how many frames are already recorded
|
||||
// and discard those later.
|
||||
var framesToSkip = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData().paints.length;
|
||||
|
||||
var elm = document.getElementsByClassName('inner')[0];
|
||||
// Set a zero-margin displayport to ensure that the element is async-scrollable
|
||||
// otherwise on Fennec it is not
|
||||
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, 0);
|
||||
|
||||
var maxScroll = elm.scrollTopMax;
|
||||
elm.scrollTop = maxScroll;
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// Take control of the refresh driver
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
|
||||
// Force the next reflow to get interrupted
|
||||
utils.forceReflowInterrupt();
|
||||
|
||||
// Make a change that triggers frame reconstruction, and then tick the refresh
|
||||
// driver so that layout processes the pending restyles and then runs an
|
||||
// interruptible reflow. That reflow *will* be interrupted (because of the flag
|
||||
// we set above), and we should end up with a transient 0,0 scroll offset
|
||||
// being sent to the compositor.
|
||||
elm.parentNode.classList.add('contentBefore');
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
// On android, and maybe non-e10s platforms generally, we need to manually
|
||||
// kick the paint to send the layer transaction to the compositor.
|
||||
yield waitForAllPaints(function() { setTimeout(testDriver, 0) });
|
||||
|
||||
// Read the main-thread scroll offset; although this is temporarily 0,0 that
|
||||
// temporary value is never exposed to content - instead reading this value
|
||||
// will finish doing the interrupted reflow from above and then report the
|
||||
// correct scroll offset.
|
||||
is(elm.scrollTop, maxScroll, "Main-thread scroll position was restored");
|
||||
|
||||
// .. and now flush everything to make sure the state gets pushed over to the
|
||||
// compositor and APZ as well.
|
||||
utils.restoreNormalRefresh();
|
||||
yield waitForApzFlushedRepaints(testDriver);
|
||||
|
||||
// Now we pull the compositor data and check it. What we expect to see is that
|
||||
// the scroll position goes to maxScroll, then drops to 0 and then goes back
|
||||
// to maxScroll. This test is specifically testing that last bit - that it
|
||||
// properly gets restored from 0 to maxScroll.
|
||||
// The one hitch is that on Android this page is loaded with some amount of
|
||||
// zoom, and the async scroll is in ParentLayerPixel coordinates, so it will
|
||||
// not match maxScroll exactly. Since we can't reliably compute what that
|
||||
// ParentLayer scroll will be, we just make sure the async scroll is nonzero
|
||||
// and use the first value we encounter to verify that it got restored properly.
|
||||
// The other alternative is to spawn this test into a new window with 1.0 zoom
|
||||
// but I'm tired of doing that for pretty much every test.
|
||||
var state = 0;
|
||||
var asyncScrollOffsets = getAsyncScrollOffsets(framesToSkip);
|
||||
dump("Got scroll offsets: " + JSON.stringify(asyncScrollOffsets) + "\n");
|
||||
var maxScrollParentLayerPixels = maxScroll;
|
||||
while (asyncScrollOffsets.length > 0) {
|
||||
let offset = asyncScrollOffsets.shift();
|
||||
switch (state) {
|
||||
// 0 is the initial state, the scroll offset might be zero but should
|
||||
// become non-zero from when we set scrollTop to scrollTopMax
|
||||
case 0:
|
||||
if (offset.y == 0) {
|
||||
break;
|
||||
}
|
||||
if (getPlatform() == "android") {
|
||||
ok(offset.y > 0, "Async scroll y of scrollframe is " + offset.y);
|
||||
maxScrollParentLayerPixels = offset.y;
|
||||
} else {
|
||||
is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe is " + offset.y);
|
||||
}
|
||||
state = 1;
|
||||
break;
|
||||
|
||||
// state 1 starts out at maxScrollParentLayerPixels, should drop to 0
|
||||
// because of the interrupted reflow putting the scroll into a transient
|
||||
// zero state
|
||||
case 1:
|
||||
if (offset.y == maxScrollParentLayerPixels) {
|
||||
break;
|
||||
}
|
||||
is(offset.y, 0, "Async scroll position was temporarily 0");
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
// state 2 starts out the transient 0 scroll offset, and we expect the
|
||||
// scroll position to get restored back to maxScrollParentLayerPixels
|
||||
case 2:
|
||||
if (offset.y == 0) {
|
||||
break;
|
||||
}
|
||||
is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe restored to " + offset.y);
|
||||
state = 3;
|
||||
break;
|
||||
|
||||
// Terminal state. The scroll position should stay at maxScrollParentLayerPixels
|
||||
case 3:
|
||||
is(offset.y, maxScrollParentLayerPixels, "Scroll position maintained");
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(state, 3, "The scroll position did drop to 0 and then get restored properly");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
pushPrefs([["apz.test.logging_enabled", true],
|
||||
["apz.displayport_expiry_ms", 0]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
214
gfx/layers/apz/test/mochitest/test_layerization.html
Normal file
214
gfx/layers/apz/test/mochitest/test_layerization.html
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
|
||||
-->
|
||||
<head>
|
||||
<title>Test for layerization</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
|
||||
<style>
|
||||
#container {
|
||||
display: flex;
|
||||
overflow: scroll;
|
||||
height: 500px;
|
||||
}
|
||||
.outer-frame {
|
||||
height: 500px;
|
||||
overflow: scroll;
|
||||
flex-basis: 100%;
|
||||
background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
|
||||
}
|
||||
#container-content {
|
||||
height: 200%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a>
|
||||
<p id="display"></p>
|
||||
<div id="container">
|
||||
<div id="outer1" class="outer-frame">
|
||||
<div id="inner1" class="inner-frame">
|
||||
<div class="inner-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="outer2" class="outer-frame">
|
||||
<div id="inner2" class="inner-frame">
|
||||
<div class="inner-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe>
|
||||
<iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe>
|
||||
<!-- The container-content div ensures 'container' is scrollable, so the
|
||||
optimization that layerizes the primary async-scrollable frame on page
|
||||
load layerizes it rather than its child subframes. -->
|
||||
<div id="container-content"></div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
// Scroll the mouse wheel over |element|.
|
||||
function scrollWheelOver(element, waitForScroll, testDriver) {
|
||||
moveMouseAndScrollWheelOver(element, 10, 10, testDriver, waitForScroll);
|
||||
}
|
||||
|
||||
const DISPLAYPORT_EXPIRY = 100;
|
||||
|
||||
// This helper function produces another helper function, which, when invoked,
|
||||
// invokes the provided testDriver argument in a setTimeout 0. This is really
|
||||
// just useful in cases when there are no paints pending, because then
|
||||
// waitForAllPaints will invoke its callback synchronously. If we did
|
||||
// waitForAllPaints(testDriver) that might cause reentrancy into the testDriver
|
||||
// which is bad. This function works around that.
|
||||
function asyncWrapper(testDriver) {
|
||||
return function() {
|
||||
setTimeout(testDriver, 0);
|
||||
};
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
// Initially, nothing should be layerized.
|
||||
ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
|
||||
ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
|
||||
ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
|
||||
ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
|
||||
ok(!isLayerized('outer3'), "initially 'outer3' should not be layerized");
|
||||
ok(!isLayerized('inner3'), "initially 'inner3' should not be layerized");
|
||||
ok(!isLayerized('outer4'), "initially 'outer4' should not be layerized");
|
||||
ok(!isLayerized('inner4'), "initially 'inner4' should not be layerized");
|
||||
|
||||
// Scrolling over outer1 should layerize outer1, but not inner1.
|
||||
yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
|
||||
ok(isLayerized('outer1'), "scrolling 'outer1' should cause it to be layerized");
|
||||
ok(!isLayerized('inner1'), "scrolling 'outer1' should not cause 'inner1' to be layerized");
|
||||
|
||||
// Scrolling over inner2 should layerize both outer2 and inner2.
|
||||
yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
|
||||
ok(isLayerized('inner2'), "scrolling 'inner2' should cause it to be layerized");
|
||||
ok(isLayerized('outer2'), "scrolling 'inner2' should also cause 'outer2' to be layerized");
|
||||
|
||||
// The second half of the test repeats the same checks as the first half,
|
||||
// but with an iframe as the outer scrollable frame.
|
||||
|
||||
// Scrolling over outer3 should layerize outer3, but not inner3.
|
||||
yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement, true, testDriver);
|
||||
ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
|
||||
ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");
|
||||
|
||||
// Scrolling over outer4 should layerize both outer4 and inner4.
|
||||
yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'), true, testDriver);
|
||||
ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
|
||||
ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
|
||||
|
||||
// Now we enable displayport expiry, and verify that things are still
|
||||
// layerized as they were before.
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, testDriver);
|
||||
ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
|
||||
ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
|
||||
ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
|
||||
ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
|
||||
ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
|
||||
ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
|
||||
ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
|
||||
ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");
|
||||
|
||||
// Now we trigger a scroll on some of the things still layerized, so that
|
||||
// the displayport expiry gets triggered.
|
||||
|
||||
// Expire displayport with scrolling on outer1
|
||||
yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
|
||||
ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");
|
||||
|
||||
// Expire displayport with scrolling on inner2
|
||||
yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
// Once the expiry elapses, it will trigger expiry on outer2, so we check
|
||||
// both, one at a time.
|
||||
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
|
||||
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");
|
||||
|
||||
// Scroll on inner3. inner3 isn't layerized, and this will cause it to
|
||||
// get layerized, but it will also trigger displayport expiration for inner3
|
||||
// which will eventually trigger displayport expiration on inner3 and outer3.
|
||||
// Note that the displayport expiration might actually happen before the wheel
|
||||
// input is processed in the compositor (see bug 1246480 comment 3), and so
|
||||
// we make sure not to wait for a scroll event here, since it may never fire.
|
||||
// However, if we do get a scroll event while waiting for the expiry, we need
|
||||
// to restart the expiry timer because the displayport expiry got reset. There's
|
||||
// no good way that I can think of to deterministically avoid doing this.
|
||||
let inner3 = document.getElementById('outer3').contentDocument.getElementById('inner3');
|
||||
yield scrollWheelOver(inner3, false, testDriver);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
var timerId = setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
var timeoutResetter = function() {
|
||||
ok(true, "Got a scroll event; resetting timer...");
|
||||
clearTimeout(timerId);
|
||||
setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
// by not updating timerId we ensure that this listener resets the timeout
|
||||
// at most once.
|
||||
};
|
||||
inner3.addEventListener('scroll', timeoutResetter, false);
|
||||
yield; // wait for the setTimeout to elapse
|
||||
inner3.removeEventListener('scroll', timeoutResetter, false);
|
||||
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
|
||||
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");
|
||||
|
||||
// Scroll outer4 and wait for the expiry. It should NOT get expired because
|
||||
// inner4 is still layerized
|
||||
yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement, true, testDriver);
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
// Wait for the expiry to elapse
|
||||
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
|
||||
yield waitForAllPaints(asyncWrapper(testDriver));
|
||||
ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
|
||||
ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
|
||||
SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)
|
||||
|
||||
// Disable smooth scrolling, because it results in long-running scroll
|
||||
// animations that can result in a 'scroll' event triggered by an earlier
|
||||
// wheel event as corresponding to a later wheel event.
|
||||
// Also enable APZ test logging, since we use that data to determine whether
|
||||
// a scroll frame was layerized.
|
||||
pushPrefs([["general.smoothScroll", false],
|
||||
["apz.displayport_expiry_ms", 0],
|
||||
["apz.test.logging_enabled", true]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,541 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test scrolling flattened inactive frames</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
p {
|
||||
width:200px;
|
||||
height:200px;
|
||||
border:solid 1px black;
|
||||
overflow:auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="iframe-body" style="overflow: auto; height: 1000px">
|
||||
<hr>
|
||||
<hr>
|
||||
<hr>
|
||||
<p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p id="subframe">
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p><p>
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<script clss="testbody" type="text/javascript;version=1.7">
|
||||
function ScrollTops() {
|
||||
this.outerScrollTop = document.getElementById('iframe-body').scrollTop;
|
||||
this.innerScrollTop = document.getElementById('subframe').scrollTop;
|
||||
}
|
||||
|
||||
var DefaultEvent = {
|
||||
deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 0, deltaY: 1,
|
||||
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
|
||||
};
|
||||
|
||||
function test() {
|
||||
var subframe = document.getElementById('subframe');
|
||||
var oldpos = new ScrollTops();
|
||||
sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
|
||||
var newpos = new ScrollTops();
|
||||
ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
|
||||
ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
|
||||
doOuterScroll(subframe, newpos);
|
||||
});
|
||||
}
|
||||
|
||||
function doOuterScroll(subframe, oldpos) {
|
||||
var outer = document.getElementById('iframe-body');
|
||||
sendWheelAndPaint(outer, 20, 5, DefaultEvent, function () {
|
||||
var newpos = new ScrollTops();
|
||||
ok(oldpos.outerScrollTop != newpos.outerScrollTop, "viewport should have scrolled");
|
||||
ok(oldpos.innerScrollTop == newpos.innerScrollTop, "subframe should not have scrolled");
|
||||
doInnerScrollAgain(subframe, newpos);
|
||||
});
|
||||
}
|
||||
|
||||
function doInnerScrollAgain(subframe, oldpos) {
|
||||
sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
|
||||
var newpos = new ScrollTops();
|
||||
ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
|
||||
ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.testInChaosMode();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
pushPrefs([['general.smoothScroll', false],
|
||||
['mousewheel.transaction.timeout', 0],
|
||||
['mousewheel.transaction.ignoremovedelay', 0]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(test);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test scrolling flattened inactive frames</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container" style="height: 300px; width: 600px; overflow: auto; background: yellow">
|
||||
<div id="outer" style="height: 400px; width: 500px; overflow: auto; background: black">
|
||||
<div id="inner" style="mix-blend-mode: screen; height: 800px; overflow: auto; background: purple">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
function test() {
|
||||
var container = document.getElementById('container');
|
||||
var outer = document.getElementById('outer');
|
||||
var inner = document.getElementById('inner');
|
||||
var outerScrollTop = outer.scrollTop;
|
||||
var containerScrollTop = container.scrollTop;
|
||||
var event = {
|
||||
deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 0,
|
||||
deltaY: 10,
|
||||
lineOrPageDeltaX: 0,
|
||||
lineOrPageDeltaY: 10,
|
||||
};
|
||||
sendWheelAndPaint(inner, 20, 30, event, function () {
|
||||
ok(container.scrollTop == containerScrollTop, "container scrollframe should not have scrolled");
|
||||
ok(outer.scrollTop > outerScrollTop, "nested scrollframe should have scrolled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.testInChaosMode();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
pushPrefs([['general.smoothScroll', false],
|
||||
['mousewheel.transaction.timeout', 1000000]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(test);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test scrolling subframe scrollbars</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
p {
|
||||
width:200px;
|
||||
height:200px;
|
||||
border:solid 1px black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="subframe">
|
||||
1 <br>
|
||||
2 <br>
|
||||
3 <br>
|
||||
4 <br>
|
||||
5 <br>
|
||||
6 <br>
|
||||
7 <br>
|
||||
8 <br>
|
||||
9 <br>
|
||||
10 <br>
|
||||
11 <br>
|
||||
12 <br>
|
||||
13 <br>
|
||||
14 <br>
|
||||
15 <br>
|
||||
16 <br>
|
||||
17 <br>
|
||||
18 <br>
|
||||
19 <br>
|
||||
20 <br>
|
||||
21 <br>
|
||||
22 <br>
|
||||
23 <br>
|
||||
24 <br>
|
||||
25 <br>
|
||||
26 <br>
|
||||
27 <br>
|
||||
28 <br>
|
||||
29 <br>
|
||||
30 <br>
|
||||
31 <br>
|
||||
32 <br>
|
||||
33 <br>
|
||||
34 <br>
|
||||
35 <br>
|
||||
36 <br>
|
||||
37 <br>
|
||||
38 <br>
|
||||
39 <br>
|
||||
40 <br>
|
||||
</p>
|
||||
<script clss="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var DefaultEvent = {
|
||||
deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 0, deltaY: 1,
|
||||
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
|
||||
};
|
||||
|
||||
var ScrollbarWidth = 0;
|
||||
|
||||
function test() {
|
||||
var subframe = document.getElementById('subframe');
|
||||
var oldClientWidth = subframe.clientWidth;
|
||||
|
||||
subframe.style.overflow = 'auto';
|
||||
subframe.getBoundingClientRect();
|
||||
|
||||
waitForAllPaintsFlushed(function () {
|
||||
ScrollbarWidth = oldClientWidth - subframe.clientWidth;
|
||||
if (!ScrollbarWidth) {
|
||||
// Probably we have overlay scrollbars - abort the test.
|
||||
ok(true, "overlay scrollbars - skipping test");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
ok(subframe.scrollHeight > subframe.clientHeight, "subframe should have scrollable content");
|
||||
testScrolling(subframe);
|
||||
});
|
||||
}
|
||||
|
||||
function testScrolling(subframe) {
|
||||
// Send a wheel event roughly to where we think the trackbar is. We pick a
|
||||
// point at the bottom, in the middle of the trackbar, where the slider is
|
||||
// unlikely to be (since it starts at the top).
|
||||
var posX = subframe.clientWidth + (ScrollbarWidth / 2);
|
||||
var posY = subframe.clientHeight - 20;
|
||||
|
||||
var oldScrollTop = subframe.scrollTop;
|
||||
|
||||
sendWheelAndPaint(subframe, posX, posY, DefaultEvent, function () {
|
||||
ok(subframe.scrollTop > oldScrollTop, "subframe should have scrolled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
pushPrefs([['general.smoothScroll', false],
|
||||
['mousewheel.transaction.timeout', 0],
|
||||
['mousewheel.transaction.ignoremovedelay', 0]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(test);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
77
gfx/layers/apz/test/mochitest/test_smoothness.html
Normal file
77
gfx/layers/apz/test/mochitest/test_smoothness.html
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Test Frame Uniformity While Scrolling</title>
|
||||
<script type="text/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
|
||||
<style>
|
||||
#content {
|
||||
height: 5000px;
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var scrollEvents = 100;
|
||||
var i = 0;
|
||||
var testPref = "gfx.vsync.collect-scroll-transforms";
|
||||
// Scroll points
|
||||
var x = 100;
|
||||
var y = 150;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var utils = _getDOMWindowUtils(window);
|
||||
|
||||
function sendScrollEvent(aRafTimestamp) {
|
||||
var scrollDiv = document.getElementById("content");
|
||||
|
||||
if (i < scrollEvents) {
|
||||
i++;
|
||||
// Scroll diff
|
||||
var dx = 0;
|
||||
var dy = -10; // Negative to scroll down
|
||||
synthesizeNativeWheelAndWaitForWheelEvent(scrollDiv, x, y, dx, dy);
|
||||
window.requestAnimationFrame(sendScrollEvent);
|
||||
} else {
|
||||
// Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5
|
||||
// w/o silk + e10s + apz, I get up to 7. Lower is better.
|
||||
// Windows, I get ~3. Values are not valid w/o hardware vsync
|
||||
var uniformities = _getDOMWindowUtils().getFrameUniformityTestData();
|
||||
for (var j = 0; j < uniformities.layerUniformities.length; j++) {
|
||||
var layerResult = uniformities.layerUniformities[j];
|
||||
var layerAddr = layerResult.layerAddress;
|
||||
var uniformity = layerResult.frameUniformity;
|
||||
var msg = "Layer: " + layerAddr.toString(16) + " Uniformity: " + uniformity;
|
||||
SimpleTest.ok((uniformity >= 0) && (uniformity < 4.0), msg);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
window.requestAnimationFrame(sendScrollEvent);
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var apzEnabled = SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled");
|
||||
if (!apzEnabled) {
|
||||
SimpleTest.ok(true, "APZ not enabled, skipping test");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set" : [
|
||||
[testPref, true]
|
||||
]
|
||||
}, startTest);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1203140
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1203140</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203140">Mozilla Bug 1203140</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="overflow-y:scroll; height: 400px">
|
||||
<p>The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.</p>
|
||||
<div id="box" style="width: 200px; height: 200px; background-color: blue"></div>
|
||||
<div style="height: 1000px; width: 10px">Div to make 'content' scrollable</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes
|
||||
|
||||
function takeSnapshots(e) {
|
||||
// Grab some snapshots, and make sure some of them are different (i.e. check
|
||||
// the page is scrolling in the compositor, concurrently with this wheel
|
||||
// listener running).
|
||||
// Note that we want this function to take less time than the content response
|
||||
// timeout, otherwise the scrolling will start even if we haven't returned,
|
||||
// and that would invalidate purpose of the test.
|
||||
var start = Date.now();
|
||||
var lastSnapshot = null;
|
||||
var success = false;
|
||||
|
||||
// Get the position of the 'content' div relative to the screen
|
||||
var rect = rectRelativeToScreen(document.getElementById('content'));
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(16);
|
||||
var snapshot = getSnapshot(rect);
|
||||
//dump("Took snapshot " + snapshot + "\n"); // this might help with debugging
|
||||
|
||||
if (lastSnapshot && lastSnapshot != snapshot) {
|
||||
ok(true, "Found some different pixels in snapshot " + i + " compared to previous");
|
||||
success = true;
|
||||
}
|
||||
lastSnapshot = snapshot;
|
||||
}
|
||||
ok(success, "Found some snapshots that were different");
|
||||
ok((Date.now() - start) < kResponseTimeoutMs, "Snapshotting ran quickly enough");
|
||||
|
||||
// Until now, no scroll events will have been dispatched to content. That's
|
||||
// because scroll events are dispatched on the main thread, which we've been
|
||||
// hogging with the code above. At this point we restore the normal refresh
|
||||
// behaviour and let the main thread go back to C++ code, so the scroll events
|
||||
// fire and we unwind from the main test continuation.
|
||||
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
var box = document.getElementById('box');
|
||||
|
||||
// Ensure the div is layerized by scrolling it
|
||||
yield moveMouseAndScrollWheelOver(box, 10, 10, testDriver);
|
||||
|
||||
box.addEventListener('touchstart', function(e) {
|
||||
ok(false, "This should never be run");
|
||||
}, false);
|
||||
box.addEventListener('wheel', takeSnapshots, { capture: false, passive: true });
|
||||
|
||||
// Let the event regions and layerization propagate to the APZ
|
||||
yield waitForAllPaints(function() {
|
||||
flushApzRepaints(testDriver);
|
||||
});
|
||||
|
||||
// Take over control of the refresh driver and compositor
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
|
||||
// Trigger an APZ scroll using a wheel event. If APZ is waiting for a
|
||||
// content response, it will wait for takeSnapshots to finish running before
|
||||
// it starts scrolling, which will cause the checks in takeSnapshots to fail.
|
||||
yield synthesizeNativeMouseMoveAndWaitForMoveEvent(box, 10, 10, testDriver);
|
||||
yield synthesizeNativeWheelAndWaitForScrollEvent(box, 10, 10, 0, -50, testDriver);
|
||||
}
|
||||
|
||||
if (isApzEnabled()) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Disable touch events, so that APZ knows not to wait for touch listeners.
|
||||
// Also explicitly set the content response timeout, so we know how long it
|
||||
// is (see comment in takeSnapshots).
|
||||
// Finally, enable smooth scrolling, so that the wheel-scroll we do as part
|
||||
// of the test triggers an APZ animation rather than doing an instant scroll.
|
||||
// Note that this pref doesn't work for the synthesized wheel events on OS X,
|
||||
// those are hard-coded to be instant scrolls.
|
||||
pushPrefs([["dom.w3c_touch_events.enabled", 0],
|
||||
["apz.content_response_timeout", kResponseTimeoutMs],
|
||||
["general.smoothscroll", true]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
106
gfx/layers/apz/test/mochitest/test_wheel_scroll.html
Normal file
106
gfx/layers/apz/test/mochitest/test_wheel_scroll.html
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1013412</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#content {
|
||||
height: 800px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#scroller {
|
||||
height: 2000px;
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
|
||||
#scrollbox {
|
||||
margin-top: 200px;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
border-radius: 250px;
|
||||
box-shadow: inset 0 0 0 60px #555;
|
||||
background: #777;
|
||||
}
|
||||
|
||||
#circle {
|
||||
position: relative;
|
||||
left: 240px;
|
||||
top: 20px;
|
||||
border: 10px solid white;
|
||||
border-radius: 10px;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
transform-origin: 10px 230px;
|
||||
will-change: transform;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
|
||||
<div id="scroller">
|
||||
<div id="scrollbox">
|
||||
<div id="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
var rotation = 0;
|
||||
var rotationAdjusted = false;
|
||||
|
||||
var incrementForMode = function (mode) {
|
||||
switch (mode) {
|
||||
case WheelEvent.DOM_DELTA_PIXEL: return 1;
|
||||
case WheelEvent.DOM_DELTA_LINE: return 15;
|
||||
case WheelEvent.DOM_DELTA_PAGE: return 400;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
document.getElementById("scrollbox").addEventListener("wheel", function (e) {
|
||||
rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
|
||||
document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
|
||||
rotationAdjusted = true;
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
function* test(testDriver) {
|
||||
var content = document.getElementById('content');
|
||||
for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
|
||||
yield synthesizeNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5, testDriver);
|
||||
}
|
||||
var scrollbox = document.getElementById('scrollbox');
|
||||
is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
|
||||
is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
|
||||
is(rotationAdjusted, true, "The rotation should have been adjusted");
|
||||
}
|
||||
|
||||
SimpleTest.testInChaosMode();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// If we allow smooth scrolling the "smooth" scrolling may cause the page to
|
||||
// glide past the scrollbox (which is supposed to stop the scrolling) and so
|
||||
// we might end up at the bottom of the page.
|
||||
pushPrefs([["general.smoothScroll", false]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
137
gfx/layers/apz/test/mochitest/test_wheel_transactions.html
Normal file
137
gfx/layers/apz/test/mochitest/test_wheel_transactions.html
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1175585</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
#outer-frame {
|
||||
height: 500px;
|
||||
overflow: scroll;
|
||||
background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
|
||||
}
|
||||
#inner-frame {
|
||||
margin-top: 25%;
|
||||
height: 200%;
|
||||
width: 75%;
|
||||
overflow: scroll;
|
||||
}
|
||||
#inner-content {
|
||||
height: 200%;
|
||||
width: 200%;
|
||||
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175585">APZ wheel transactions test</a>
|
||||
<p id="display"></p>
|
||||
<div id="outer-frame">
|
||||
<div id="inner-frame">
|
||||
<div id="inner-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
function scrollWheelOver(element, deltaY, testDriver) {
|
||||
synthesizeNativeWheelAndWaitForScrollEvent(element, 10, 10, 0, deltaY, testDriver);
|
||||
}
|
||||
|
||||
function* test(testDriver) {
|
||||
var outer = document.getElementById('outer-frame');
|
||||
var inner = document.getElementById('inner-frame');
|
||||
var innerContent = document.getElementById('inner-content');
|
||||
|
||||
// Register a wheel event listener that records the target of
|
||||
// the last wheel event, so that we can make assertions about it.
|
||||
var lastWheelTarget;
|
||||
var wheelTargetRecorder = function(e) { lastWheelTarget = e.target; };
|
||||
window.addEventListener("wheel", wheelTargetRecorder);
|
||||
|
||||
// Scroll |outer| to the bottom.
|
||||
while (outer.scrollTop < outer.scrollTopMax) {
|
||||
yield scrollWheelOver(outer, -10, testDriver);
|
||||
}
|
||||
|
||||
// Verify that this has brought |inner| under the wheel.
|
||||
is(lastWheelTarget, innerContent, "'inner-content' should have been brought under the wheel");
|
||||
window.removeEventListener("wheel", wheelTargetRecorder);
|
||||
|
||||
// Immediately after, scroll it back up a bit.
|
||||
yield scrollWheelOver(outer, 10, testDriver);
|
||||
|
||||
// Check that it was |outer| that scrolled back, and |inner| didn't
|
||||
// scroll at all, as all the above scrolls should be in the same
|
||||
// transaction.
|
||||
ok(outer.scrollTop < outer.scrollTopMax, "'outer' should have scrolled back a bit");
|
||||
is(inner.scrollTop, 0, "'inner' should not have scrolled");
|
||||
|
||||
// The next part of the test is related to the transaction timeout.
|
||||
// Turn it down a bit so waiting for the timeout to elapse doesn't
|
||||
// slow down the test harness too much.
|
||||
var timeout = 5;
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["mousewheel.transaction.timeout", timeout]]}, testDriver);
|
||||
SimpleTest.requestFlakyTimeout("we are testing code that measures actual elapsed time between two events");
|
||||
|
||||
// Scroll up a bit more. It's still |outer| scrolling because
|
||||
// |inner| is still scrolled all the way to the top.
|
||||
yield scrollWheelOver(outer, 10, testDriver);
|
||||
|
||||
// Wait for the transaction timeout to elapse.
|
||||
// timeout * 5 is used to make it less likely that the timeout is less than
|
||||
// the system timestamp resolution
|
||||
yield window.setTimeout(testDriver, timeout * 5);
|
||||
|
||||
// Now scroll down. The transaction having timed out, the event
|
||||
// should pick up a new target, and that should be |inner|.
|
||||
yield scrollWheelOver(outer, -10, testDriver);
|
||||
ok(inner.scrollTop > 0, "'inner' should have been scrolled");
|
||||
|
||||
// Finally, test scroll handoff after a timeout.
|
||||
|
||||
// Continue scrolling |inner| down to the bottom.
|
||||
var prevScrollTop = inner.scrollTop;
|
||||
while (inner.scrollTop < inner.scrollTopMax) {
|
||||
yield scrollWheelOver(outer, -10, testDriver);
|
||||
// Avoid a failure getting us into an infinite loop.
|
||||
ok(inner.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
|
||||
prevScrollTop = inner.scrollTop;
|
||||
}
|
||||
|
||||
// Wait for the transaction timeout to elapse.
|
||||
// timeout * 5 is used to make it less likely that the timeout is less than
|
||||
// the system timestamp resolution
|
||||
yield window.setTimeout(testDriver, timeout * 5);
|
||||
|
||||
// Continued downward scrolling should scroll |outer| to the bottom.
|
||||
prevScrollTop = outer.scrollTop;
|
||||
while (outer.scrollTop < outer.scrollTopMax) {
|
||||
yield scrollWheelOver(outer, -10, testDriver);
|
||||
// Avoid a failure getting us into an infinite loop.
|
||||
ok(outer.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
|
||||
prevScrollTop = outer.scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Disable smooth scrolling because it makes the test flaky (we don't have a good
|
||||
// way of detecting when the scrolling is finished).
|
||||
pushPrefs([["general.smoothScroll", false]])
|
||||
.then(waitUntilApzStable)
|
||||
.then(runContinuation(test))
|
||||
.then(SimpleTest.finish);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
9
gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
Normal file
9
gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body onload="scrollTo(450,0); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 10px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
Normal file
10
gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body onload="scrollTo(-450,0); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 10px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
Normal file
14
gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="-449" reftest-async-scroll-y="0"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<!-- Doing scrollTo(-1,0) is to activate the right arrow in the scrollbar
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(-1,0); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 10px; background: white"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
13
gfx/layers/apz/test/reftest/async-scrollbar-1-h.html
Normal file
13
gfx/layers/apz/test/reftest/async-scrollbar-1-h.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="449" reftest-async-scroll-y="0"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<!-- Doing scrollTo(1,0) is to activate the left arrow in the scrollbar
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(1,0); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 10px; background: white"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
9
gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
Normal file
9
gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 10px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
Normal file
10
gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body onload="scrollTo(0,10000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 10px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
Normal file
14
gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 10px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
13
gfx/layers/apz/test/reftest/async-scrollbar-1-v.html
Normal file
13
gfx/layers/apz/test/reftest/async-scrollbar-1-v.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="0" reftest-async-scroll-y="9999"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<!-- Doing scrollTo(0,1) is to activate the up arrow in the scrollbar
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(0,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 10px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body onload="scrollTo(450,8000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<body onload="scrollTo(-450,8000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
Normal file
14
gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="-440" reftest-async-scroll-y="7999"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style> html { direction: rtl; } </style>
|
||||
</head>
|
||||
<!-- Doing scrollTo(-10,1) is to activate the right/up arrows in the scrollbars
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(-10,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
13
gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html
Normal file
13
gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="449" reftest-async-scroll-y="7999"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html
Normal file
14
gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="224" reftest-async-scroll-y="4999"
|
||||
reftest-async-zoom="2.0"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 4500px; height: 10000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 9000px; height: 20000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
14
gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html
Normal file
14
gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait"
|
||||
reftest-async-scroll
|
||||
reftest-async-scroll-x="899" reftest-async-scroll-y="19999"
|
||||
reftest-async-zoom="0.5"><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<!-- Doing scrollTo(1,1) is to activate the left/up arrows in the scrollbars
|
||||
for non-overlay scrollbar environments -->
|
||||
<body onload="scrollTo(1,1); document.documentElement.classList.remove('reftest-wait')">
|
||||
<div style="width: 18000px; height: 40000px; background: white;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<script>
|
||||
function run() {
|
||||
document.body.classList.toggle('noscroll');
|
||||
document.getElementById('spacer').style.height = '100%';
|
||||
// Scroll to the very end, including any fractional pixels
|
||||
document.body.scrollTop = document.body.scrollTopMax + 1;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.noscroll {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<body onload="run()">
|
||||
<div id="spacer" style="height: 5000px">
|
||||
This is the top of the page.
|
||||
</div>
|
||||
This is the bottom of the page.
|
||||
</body>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<html class="reftest-wait">
|
||||
<!--
|
||||
For bug 1266833; syncing the scroll offset to APZ properly when the scroll
|
||||
position is clamped to a smaller value during a frame reconstruction.
|
||||
-->
|
||||
<script>
|
||||
function run() {
|
||||
document.body.scrollTop = document.body.scrollTopMax;
|
||||
|
||||
// Let the scroll position propagate to APZ before we do the frame
|
||||
// reconstruction. Ideally we would wait for flushApzRepaints here but
|
||||
// we don't have access to DOMWindowUtils in a reftest, so we just wait
|
||||
// 100ms to approximate it. With bug 1266833 fixed, this test should
|
||||
// never fail regardless of what this timeout value is.
|
||||
setTimeout(frameReconstruction, 100);
|
||||
}
|
||||
|
||||
function frameReconstruction() {
|
||||
document.body.classList.toggle('noscroll');
|
||||
document.documentElement.classList.toggle('reconstruct-body');
|
||||
document.getElementById('spacer').style.height = '100%';
|
||||
document.documentElement.classList.remove('reftest-wait');
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.noscroll {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Toggling this on and off triggers a frame reconstruction on the <body> */
|
||||
html.reconstruct-body::before {
|
||||
top: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
</style>
|
||||
<body onload="setTimeout(run, 0)">
|
||||
<div id="spacer" style="height: 5000px">
|
||||
This is the top of the page.
|
||||
</div>
|
||||
This is the bottom of the page.
|
||||
</body>
|
||||
10
gfx/layers/apz/test/reftest/initial-scale-1-ref.html
Normal file
10
gfx/layers/apz/test/reftest/initial-scale-1-ref.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
|
||||
with something a little more sane.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
gfx/layers/apz/test/reftest/initial-scale-1.html
Normal file
10
gfx/layers/apz/test/reftest/initial-scale-1.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta name="viewport" content="initial-scale=0; width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
This tests that an initial-scale of 0 (i.e. garbage) is overridden<br/>
|
||||
with something a little more sane.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
20
gfx/layers/apz/test/reftest/reftest-stylo.list
Normal file
20
gfx/layers/apz/test/reftest/reftest-stylo.list
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
|
||||
# The following tests test the async positioning of the scrollbars.
|
||||
# Basic root-frame scrollbar with async scrolling
|
||||
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v.html async-scrollbar-1-v.html
|
||||
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-h.html async-scrollbar-1-h.html
|
||||
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-vh.html async-scrollbar-1-vh.html
|
||||
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl.html
|
||||
skip-if(!asyncPan) fuzzy-if(Android,13,8) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl.html
|
||||
skip-if(!asyncPan) fuzzy-if(Android,8,10) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl.html
|
||||
|
||||
# Different async zoom levels. Since the scrollthumb gets async-scaled in the
|
||||
# compositor, the border-radius ends of the scrollthumb are going to be a little
|
||||
# off, hence the fuzzy-if clauses.
|
||||
skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1.html
|
||||
skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2.html
|
||||
|
||||
# Meta-viewport tag support
|
||||
skip-if(!asyncZoom) == initial-scale-1.html initial-scale-1.html
|
||||
|
||||
skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping.html
|
||||
19
gfx/layers/apz/test/reftest/reftest.list
Normal file
19
gfx/layers/apz/test/reftest/reftest.list
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# The following tests test the async positioning of the scrollbars.
|
||||
# Basic root-frame scrollbar with async scrolling
|
||||
fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html
|
||||
fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
|
||||
fuzzy-if(Android,3,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
|
||||
fuzzy-if(Android,1,2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
|
||||
fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
|
||||
fuzzy-if(Android,3,7) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
|
||||
|
||||
# Different async zoom levels. Since the scrollthumb gets async-scaled in the
|
||||
# compositor, the border-radius ends of the scrollthumb are going to be a little
|
||||
# off, hence the fuzzy-if clauses.
|
||||
fuzzy-if(Android,54,18) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
|
||||
fuzzy-if(Android,45,21) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
|
||||
|
||||
# Meta-viewport tag support
|
||||
skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html
|
||||
|
||||
skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruction-scroll-clamping-ref.html
|
||||
Loading…
Add table
Add a link
Reference in a new issue