import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1262361 - Use gfxCriticalError to collect useful info when device reset. r=milan (a565047753)
- Bug 1265282 - Annotate reports instead of crashing when setting an incompatible compositor (D3D11). r=dvander (ed04e9e8eb)
- Bug 1235407 - Part 3: Force a reset when OpenSharedHandle fails. r=milan (a2f5c656eb)
- Bug 1264142 - Add ImageLayerComposite::GetFullyRenderedRegion() r=mattwoodrow (3a5f479820)
- Bug 1224199 - Remove some unused code in TiledLayerBufferComposite - r=nical (b3c60126ce)
- Bug 1262328 - Add Add ClientLayer::HandleMemoryPressure() r=nical (31bb89613d)
- Bug 1224833 - Check explicitly if image has TextureClient r=nical (34044dc195)
- Bug 1240867 - Add missing include. r=nical (abd9a8876e)
- Bug 1240800: When we've reallocated our buffer client side and fail to track the proper invalid region always upload the bounds of the visible region. r=mattwoodrow (7a7cc1928c)
- Bug 1253386 - Double buffer in CanvasClient2D - r=nical (a0fe10f1a6)
- Bug 1266380 - fix offset of untransformed surface when 3d transforming in BasicLayerManager. r=jrmuizel (3d3931346f)
- Bug 1257288 - Fix a bug in APZCTM logging code. r=kats (3c73c99077)
- Bug 1265424 - Record if the target was confirmed via a timeout or not. r=botond (e25c0c016d)
- Bug 1265424 - Ensure that HasReceivedAllContentNotifications doesn't start returning true if the target APZC was confirmed by timeout. r=botond (6c4e57cb8f)
- Bug 1229039 - If a PanGesture input block gets interrupted, just start a new block instead of not sending the rest of the events through the APZ. r=mstange (534b6691f7)
- Bug 1056356 - Remove the hand-rolled mechanism used to get nsRefPtr<const OverscrollHandoffChaiin> to work. r=kats (9d692fa32f)
- Bug 1253617 - Fix non-unified build bustage in OverscrollHandoffStat.cpp r=BenWa (38a53f7521)
- Bug 1262432 - Remove assertion that may be legitimately false sometimes. r=tnikkel (36bbb36476)
- Bug 1169802 - Temporary workaround for a deeper bug, to prevent an assertion from firing. r=botond (b1edd69e49)
- bits of Bug 1223296 (182a1ee34e)
- move around (bug 1014691) and align some more tests (19db30010c)
- Bug 944164 - Update jorendb to current function names, and much else: (b96a636c60)
- Bug 1244222 - Tests. r=bz (9cfbe54859)
- Bug 888969 - Make XPCJSID instanceof comparisons work correctly when [[GetPrototypeOf]] on the [[Prototype]] chain of the instance being tested throws an exception. r=bz (ff450de2ef)
- Bug 1256306 - Bump the Windows stack limit. r=bholley (5f4f0c3b1c)
- Bug 1257234 - Detect main thread's stack size at runtime, on Windows. r=ted (108608ce73)
- bug 1264651 - remove dom.max_child_script_run_time pref r=billm (443bc36d84)
- Bug 1266630 - Make fallible the orphan node table used during memory reporting. r=mccr8. (b7c6da934c)
- Bug 1259699 - Adjust Windows stack limits to account for large PGO stack frames. r=bholley (87ff9dbef9)
- bug 1250486 - get rid of the static ctor for XPCShellImpl.cpp r=bz (748def73ea)
- Bug 1231309 - guard sz with assert. r=bobbyholley (34080a1022)
- Bug 1255817 part 5. Remove the now-unused xpc::SystemErrorReporter. r=bholley (c014f7fa7f)
- Bug 1257888 - Link chromium mutex-based atomics implementation to webrtc signaling tests. r=froydnj (fadb9764b2)
- Bug 1253123 - Remove ipc_sync_message (r=jld) a=kwierso (7cc8a8fe0e)
- Bug 1253123 - Remove ipc_channel_proxy (r=jld) (4000ea80f3)
- Bug 1253123 - Remove message_router (r=jld) a=kwierso (8b41859a17)
- Bug 1235633 - IPC OOM mitigation by eliminating buffer copying (r=jld) (ecd6c12bec)
- Bug 1263314: Remove NonThreadSafe. r=jld (06a6331dee)
- Bug 1261567 - Include compat dir in libevent include path. r=billm (b496d2ad1f)
- Bug 1258312 - Add crash annotation to EnumSerializer r=jld (7face45d63)
- Bug 1262463 - part 1 - turn NS_RUNTIMEABORTs in protocols into FatalErrors; r=jld (731b35dc68)
- Bug 1262463 - part 2 - don't pass the other process pid into FatalError; r=jld (6f54c58494)
- Bug 1262463 - part 3 - out-of-line NS_RUNTIMEABORT calls in IPDL-generated code; r=jld (0728f61a5f)
- Bug 1259428 - part 1 - don't call Log methods of generated method classes; r=jld (938c6ff705)
- Bug 1259428 - part 2 - remove dodgy static_cast downcasts from logging statements; r=jld (4fa0a5228d)
- Bug 1259428 - part 3 - remove Log() methods from generated message subclasses; r=jld (964a0c2487)
- Bug 1259428 - part 4 - remove prtime.h from generated protocol headers; r=jld (8548b37b91)
- Bug 1259428 - part 5 - convert Message subclasses to constructor functions; r=jld (63f195ffce)
- Bug 1259428 - part 6 - remove unneeded MessageDecl methods; r=jld (fc8cd72bd7)
- Bug 1262458 - rename {msg,reply}Class-related things to reflect new functional reality; r=jld (8122887df4)
- Bug 1258663 - Crash annotate system call failures in the IPC transport. r=gabor (5f483a17a5)
- Bug 1258317 - Part 1: Annotate the crash report with process information on failure to transfer an IPC transport to another process. r=krizsa (107947c7c3)
- Bug 1258317 - cross compilation fixup. (534c214182)
- Bug 1258317 - Part 2: Remove unused privilege in mozilla::ipc::TransferHandleTorProcess(). r=krizsa (82fc8ca4cc)
- Bug 1252246 - Try to use PTHREAD_PROCESS_SHARED for CrossProcessMutex on more Unices. r=glandium f=kats (195742a508)
- now we can add crashreporter (2cbb6f2d1a)
- Bug 1251226 - Avoid passing a std::wstring through the variadic method; r=bobowen (80b2dd8034)
- Bug 1260736 - Let the client to filter out its interested messages to lower the number of times entering the monitor in PeekMessages(). r=dvander (3aea1ed77a)
- Bug 1264662 - Record IPC message capacity instead of size. r=billm (fbfff4e5b8)
- Bug 1261099 - Avoid two Message copies in MaybeUndeferIncall. r=billm (ebb7fe2b47)
This commit is contained in:
2024-07-23 11:36:09 +08:00
parent be4fef94e8
commit 05670874b5
152 changed files with 2391 additions and 3369 deletions
-70
View File
@@ -496,76 +496,6 @@ bool ScriptErrorEvent::sHandlingScriptError = false;
// soon.
namespace xpc {
void
SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
JS::Rooted<JS::Value> exception(cx);
::JS_GetPendingException(cx, &exception);
// Note: we must do this before running any more code on cx.
::JS_ClearPendingException(cx);
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
nsCOMPtr<nsIGlobalObject> globalObject;
// The eventual plan is for error reporting to happen in the AutoJSAPI
// destructor using the global with which the AutoJSAPI was initialized. We
// can't _quite_ do that yet, so we take a sloppy stab at those semantics. If
// we have an nsIScriptContext, we'll get the right answer modulo
// non-current-inners.
//
// Otherwise, we just use the privileged junk scope. This has the effect of
// causing us to report the error as "chrome javascript" rather than "content
// javascript", and not invoking any error reporters. This is exactly what we
// want here.
if (nsIScriptContext* scx = GetScriptContextFromJSContext(cx)) {
nsCOMPtr<nsPIDOMWindow> outer = do_QueryInterface(scx->GetGlobalObject());
if (outer) {
globalObject = static_cast<nsGlobalWindow*>(outer->GetCurrentInnerWindow());
}
}
// We run addons in a separate privileged compartment, but they still expect
// to trigger the onerror handler of their associated DOMWindow.
//
// Note that the way we do this right now is sloppy. Error reporters can
// theoretically be triggered at arbitrary times (not just immediately before
// an AutoJSAPI comes off the stack), so we don't really have a way of knowing
// that the global of the current compartment is the correct global with which
// to report the error. But in practice this is probably fine for the time
// being, and will get cleaned up soon when we fix bug 981187.
if (!globalObject && JS::CurrentGlobalOrNull(cx)) {
globalObject = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(cx));
}
if (!globalObject) {
globalObject = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
if (globalObject) {
RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
bool isChrome = nsContentUtils::IsSystemPrincipal(globalObject->PrincipalOrNull());
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(globalObject);
xpcReport->Init(report, message, isChrome, win ? win->WindowID() : 0);
// If we can't dispatch an event to a window, report it to the console
// directly. This includes the case where the error was an OOM, because
// triggering a scripted event handler is likely to generate further OOMs.
if (!win || JSREPORT_IS_WARNING(xpcReport->mFlags) ||
report->errorNumber == JSMSG_OUT_OF_MEMORY)
{
JS::Rooted<JSObject*> stack(cx,
xpc::FindExceptionStackForConsoleReport(win, exception));
xpcReport->LogToConsoleWithStack(stack);
return;
}
// Otherwise, we need to asynchronously invoke onerror before we can decide
// whether or not to report the error to the console.
DispatchScriptErrorEvent(win, JS_GetRuntime(cx), xpcReport, exception);
}
}
void
DispatchScriptErrorEvent(nsPIDOMWindow *win, JSRuntime *rt, xpc::ErrorReport *xpcReport,
JS::Handle<JS::Value> exception)
-14
View File
@@ -1,14 +0,0 @@
# The following tests test the async positioning of the scrollbars.
# Basic root-frame scrollbar with async scrolling
chaos-mode skip-if(!asyncPan) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html
chaos-mode skip-if(!asyncPan) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
chaos-mode skip-if(!asyncPan) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
chaos-mode skip-if(!asyncPan) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
chaos-mode skip-if(!asyncPan) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
chaos-mode skip-if(!asyncPan) == 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.
chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
+1 -1
View File
@@ -1770,7 +1770,7 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
MOZ_ASSERT(result);
}
APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
result, aNode, *aOutHitResult);
result, resultNode, *aOutHitResult);
return result;
}
+31 -10
View File
@@ -25,7 +25,8 @@ static uint64_t sBlockCounter = InputBlockState::NO_BLOCK_ID + 1;
InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed)
: mTargetApzc(aTargetApzc)
, mTargetConfirmed(aTargetConfirmed)
, mTargetConfirmed(aTargetConfirmed ? TargetConfirmationState::eConfirmed
: TargetConfirmationState::eUnconfirmed)
, mBlockId(sBlockCounter++)
, mTransformToApzc(aTargetApzc->GetTransformToThis())
{
@@ -35,12 +36,23 @@ InputBlockState::InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetAp
}
bool
InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
InputBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
if (mTargetConfirmed) {
MOZ_ASSERT(aState == TargetConfirmationState::eConfirmed
|| aState == TargetConfirmationState::eTimedOut);
if (mTargetConfirmed == TargetConfirmationState::eTimedOut &&
aState == TargetConfirmationState::eConfirmed) {
// The main thread finally responded. We had already timed out the
// confirmation, but we want to update the state internally so that we
// can record the time for telemetry purposes.
mTargetConfirmed = TargetConfirmationState::eTimedOutAndMainThreadResponded;
}
if (mTargetConfirmed != TargetConfirmationState::eUnconfirmed) {
return false;
}
mTargetConfirmed = true;
mTargetConfirmed = aState;
TBS_LOG("%p got confirmed target APZC %p\n", this, mTargetApzc.get());
if (mTargetApzc == aTargetApzc) {
@@ -85,7 +97,14 @@ InputBlockState::GetBlockId() const
bool
InputBlockState::IsTargetConfirmed() const
{
return mTargetConfirmed;
return mTargetConfirmed != TargetConfirmationState::eUnconfirmed;
}
bool
InputBlockState::HasReceivedRealConfirmedTarget() const
{
return mTargetConfirmed == TargetConfirmationState::eConfirmed ||
mTargetConfirmed == TargetConfirmationState::eTimedOutAndMainThreadResponded;
}
bool
@@ -192,7 +211,7 @@ CancelableBlockState::IsDefaultPrevented() const
bool
CancelableBlockState::HasReceivedAllContentNotifications() const
{
return IsTargetConfirmed() && mContentResponded;
return HasReceivedRealConfirmedTarget() && mContentResponded;
}
bool
@@ -359,7 +378,8 @@ WheelBlockState::SetContentResponse(bool aPreventDefault)
}
bool
WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
// The APZC that we find via APZCCallbackHelpers may not be the same APZC
// ESM or OverscrollHandoff would have computed. Make sure we get the right
@@ -370,7 +390,7 @@ WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aT
apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
}
InputBlockState::SetConfirmedTargetApzc(apzc);
InputBlockState::SetConfirmedTargetApzc(apzc, aState);
return true;
}
@@ -603,7 +623,8 @@ PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>&
}
bool
PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc)
PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState)
{
// The APZC that we find via APZCCallbackHelpers may not be the same APZC
// ESM or OverscrollHandoff would have computed. Make sure we get the right
@@ -618,7 +639,7 @@ PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController
}
}
InputBlockState::SetConfirmedTargetApzc(apzc);
InputBlockState::SetConfirmedTargetApzc(apzc, aState);
return true;
}
+15 -4
View File
@@ -39,17 +39,26 @@ class InputBlockState
public:
static const uint64_t NO_BLOCK_ID = 0;
enum class TargetConfirmationState {
eUnconfirmed,
eTimedOut,
eTimedOutAndMainThreadResponded,
eConfirmed
};
explicit InputBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
bool aTargetConfirmed);
virtual ~InputBlockState()
{}
virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc);
virtual bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState);
const RefPtr<AsyncPanZoomController>& GetTargetApzc() const;
const RefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
uint64_t GetBlockId() const;
bool IsTargetConfirmed() const;
bool HasReceivedRealConfirmedTarget() const;
void SetScrolledApzc(AsyncPanZoomController* aApzc);
AsyncPanZoomController* GetScrolledApzc() const;
@@ -65,7 +74,7 @@ private:
private:
RefPtr<AsyncPanZoomController> mTargetApzc;
bool mTargetConfirmed;
TargetConfirmationState mTargetConfirmed;
const uint64_t mBlockId;
// The APZC that was actually scrolled by events in this input block.
@@ -227,7 +236,8 @@ public:
void HandleEvents() override;
bool MustStayActive() override;
const char* Type() override;
bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState) override;
void AddEvent(const ScrollWheelInput& aEvent);
@@ -348,7 +358,8 @@ public:
void HandleEvents() override;
bool MustStayActive() override;
const char* Type() override;
bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
bool SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc,
TargetConfirmationState aState) override;
void AddEvent(const PanGestureInput& aEvent);
+22 -14
View File
@@ -118,7 +118,8 @@ InputQueue::ReceiveTouchInput(const RefPtr<AsyncPanZoomController>& aTarget,
// from a fast fling to a pinch state (i.e. second finger goes down while
// the first finger is moving).
block->SetDuringFastFling();
block->SetConfirmedTargetApzc(aTarget);
block->SetConfirmedTargetApzc(aTarget,
InputBlockState::TargetConfirmationState::eConfirmed);
if (gfxPrefs::TouchActionEnabled()) {
block->SetAllowedTouchBehaviors(currentBehaviors);
}
@@ -334,22 +335,25 @@ InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget
block = mInputBlockQueue.LastElement()->AsPanGestureBlock();
}
PanGestureInput event = aEvent;
nsEventStatus result = nsEventStatus_eConsumeDoDefault;
if (!block || block->WasInterrupted()) {
if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
// Only PANGESTURE_START events are allowed to start a new pan gesture block.
INPQ_LOG("pangesture block %p was interrupted %d\n", block,
block ? block->WasInterrupted() : 0);
return nsEventStatus_eConsumeDoDefault;
if (event.mType != PanGestureInput::PANGESTURE_START) {
// Only PANGESTURE_START events are allowed to start a new pan gesture
// block, but we really want to start a new block here, so we magically
// turn this input into a PANGESTURE_START.
INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n",
event.mType);
event.mType = PanGestureInput::PANGESTURE_START;
}
block = new PanGestureBlockState(aTarget, aTargetConfirmed, aEvent);
block = new PanGestureBlockState(aTarget, aTargetConfirmed, event);
INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n",
block, block->GetBlockId(), aTarget.get());
if (aTargetConfirmed &&
aEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
!CanScrollTargetHorizontally(aEvent, block)) {
event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
!CanScrollTargetHorizontally(event, block)) {
// This event may trigger a swipe gesture, depending on what our caller
// wants to do it. We need to suspend handling of this block until we get
// a content response which will tell us whether to proceed or abort the
@@ -379,8 +383,8 @@ InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget
// null) should take priority. This is equivalent to just always using the
// target (confirmed or not) from the block, which is what
// MaybeHandleCurrentBlock() does.
if (!MaybeHandleCurrentBlock(block, aEvent)) {
block->AddEvent(aEvent.AsPanGestureInput());
if (!MaybeHandleCurrentBlock(block, event)) {
block->AddEvent(event.AsPanGestureInput());
}
return result;
@@ -583,7 +587,9 @@ InputQueue::MainThreadTimeout(const uint64_t& aInputBlockId) {
// target apzc in the case where the main thread doesn't get back to us
// fast enough.
success = mInputBlockQueue[i]->TimeoutContentResponse();
success |= mInputBlockQueue[i]->SetConfirmedTargetApzc(mInputBlockQueue[i]->GetTargetApzc());
success |= mInputBlockQueue[i]->SetConfirmedTargetApzc(
mInputBlockQueue[i]->GetTargetApzc(),
InputBlockState::TargetConfirmationState::eTimedOut);
break;
}
}
@@ -621,7 +627,8 @@ InputQueue::SetConfirmedTargetApzc(uint64_t aInputBlockId, const RefPtr<AsyncPan
for (size_t i = 0; i < mInputBlockQueue.Length(); i++) {
CancelableBlockState* block = mInputBlockQueue[i].get();
if (block->GetBlockId() == aInputBlockId) {
success = block->SetConfirmedTargetApzc(aTargetApzc);
success = block->SetConfirmedTargetApzc(aTargetApzc,
InputBlockState::TargetConfirmationState::eConfirmed);
block->RecordContentResponseTime();
break;
}
@@ -644,7 +651,8 @@ InputQueue::ConfirmDragBlock(uint64_t aInputBlockId, const RefPtr<AsyncPanZoomCo
DragBlockState* block = mInputBlockQueue[i]->AsDragBlock();
if (block && block->GetBlockId() == aInputBlockId) {
block->SetDragMetrics(aDragMetrics);
success = block->SetConfirmedTargetApzc(aTargetApzc);
success = block->SetConfirmedTargetApzc(aTargetApzc,
InputBlockState::TargetConfirmationState::eConfirmed);
block->RecordContentResponseTime();
break;
}
+4 -34
View File
@@ -16,43 +16,13 @@
#include "Units.h" // for ScreenPoint
namespace mozilla {
class InputData;
namespace layers {
class AsyncPanZoomController;
/**
* A variant of NS_INLINE_DECL_THREADSAFE_REFCOUNTING which makes the refcount
* variable |mutable|, and the AddRef and Release methods |const|, to allow
* using an |RefPtr<const T>|, and thereby enforcing better const-correctness.
* This is currently here because OverscrollHandoffChain is the only thing
* currently using it. As a follow-up, we can move this to xpcom/glue, write
* a corresponding version for non-threadsafe refcounting, and perhaps
* transition other clients of NS_INLINE_DECL_[THREADSAFE_]REFCOUNTING to the
* mutable versions.
*/
#define NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(_class) \
public: \
NS_METHOD_(MozExternalRefCountType) AddRef(void) const { \
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(_class) \
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
nsrefcnt count = ++mRefCnt; \
NS_LOG_ADDREF(const_cast<_class*>(this), count, #_class, sizeof(*this)); \
return (nsrefcnt) count; \
} \
NS_METHOD_(MozExternalRefCountType) Release(void) const { \
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
nsrefcnt count = --mRefCnt; \
NS_LOG_RELEASE(const_cast<_class*>(this), count, #_class); \
if (count == 0) { \
delete (this); \
return 0; \
} \
return count; \
} \
protected: \
mutable ::mozilla::ThreadSafeAutoRefCnt mRefCnt; \
public:
/**
* This class represents the chain of APZCs along which overscroll is handed off.
* It is created by APZCTreeManager by starting from an initial APZC which is
@@ -71,7 +41,7 @@ public:
// Mutable so that we can pass around the class by
// RefPtr<const OverscrollHandoffChain> and thus enforce that, once built,
// the chain is not modified.
NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING(OverscrollHandoffChain)
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OverscrollHandoffChain)
/*
* Methods for building the handoff chain.
@@ -1,140 +0,0 @@
// Utilities for synthesizing of native events.
function getPlatform() {
if (navigator.platform.indexOf("Win") == 0) {
return "windows";
}
if (navigator.platform.indexOf("Mac") == 0) {
return "mac";
}
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();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
// to device pixels relative to aElement's containing window.
function coordinatesRelativeToWindow(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)
};
}
// 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 |window|'s top-left. 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 = coordinatesRelativeToWindow(aX, aY, aElement);
if (aDeltaX && aDeltaY) {
throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
}
var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
_getDOMWindowUtils().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 the window. See synthesizeNativeWheel for
// details on the parameters.
function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
window.addEventListener("wheel", function wheelWaiter(e) {
window.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 the window.
// See synthesizeNativeWheel for details on the parameters.
function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
var useCapture = true; // scroll events don't always bubble
window.addEventListener("scroll", function scrollWaiter(e) {
window.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 = coordinatesRelativeToWindow(aX, aY, aElement);
_getDOMWindowUtils().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 = coordinatesRelativeToWindow(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
return true;
}
function synthesizeNativeDrag(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);
}
-118
View File
@@ -1,118 +0,0 @@
// 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 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;
}
// ----------------------------------------------------------------
// Utilities for reconstructing the structure of the APZC tree from
// 'parentScrollId' entries in the APZ test data.
// ----------------------------------------------------------------
// Create a node with scroll id 'id' in the APZC tree.
function makeNode(id) {
return {scrollId: id, children: []};
}
// Find a node with scroll id 'id' in the APZC tree rooted at 'root'.
function findNode(root, id) {
if (root.scrollId == id) {
return root;
}
for (var i = 0; i < root.children.length; ++i) {
var subtreeResult = findNode(root.children[i], id);
if (subtreeResult != null) {
return subtreeResult;
}
}
return null;
}
// Add a child -> parent link to the APZC tree rooted at 'root'.
function addLink(root, child, parent) {
var parentNode = findNode(root, parent);
if (parentNode == null) {
parentNode = makeNode(parent);
root.children.push(parentNode);
}
parentNode.children.push(makeNode(child));
}
// Add a root node to the APZC tree. It will become a direct
// child of 'root'.
function addRoot(root, id) {
root.children.push(makeNode(id));
}
// 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 = makeNode(-1);
for (var scrollId in paint) {
if ("isRootForLayersId" in paint[scrollId]) {
addRoot(root, scrollId);
} else if ("parentScrollId" in paint[scrollId]) {
addLink(root, scrollId, paint[scrollId]["parentScrollId"]);
}
}
return root;
}
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();
}
}
-38
View File
@@ -1,38 +0,0 @@
<!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">
function scrollPage() {
SpecialPowers._addMessageListener("APZ:TransformEnd", function() {
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll);
});
const TOUCH_SLOP = 1;
synthesizeNativeDrag(document.body, 10, 100, 0, -(50 + TOUCH_SLOP));
dump("Finished native drag, waiting for transform-end observer...\n");
}
function checkScroll() {
window.opener.is(window.scrollY, 50, "check that the window scrolled");
window.opener.testDone();
}
window.onload = function() {
setTimeout(scrollPage, 0);
}
</script>
</head>
<body>
<div style="height: 5000px; background-color: lightgreen;">
This div makes the page scrollable.
</div>
</body>
</html>
-147
View File
@@ -1,147 +0,0 @@
<!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 root APZC
// and a child APZC for the scrollable <div>.
SimpleTest.is(apzcTree.children.length, 1, "expected a single root APZC");
var rootApzc = apzcTree.children[0];
SimpleTest.is(rootApzc.children.length, 1, "expected a single child APZC");
var childScrollId = rootApzc.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];
// This content-side data should have a displayport for our scrollable <div>.
SimpleTest.ok(childScrollId in correspondingContentPaint,
"expected scroll frame data for scroll id " + childScrollId);
SimpleTest.ok("displayport" in correspondingContentPaint[childScrollId],
"expected a displayport for scroll id " + childScrollId);
var childDisplayport = correspondingContentPaint[childScrollId]["displayport"];
var dpFields = childDisplayport.replace(/[()\s]+/g, '').split(',');
SimpleTest.is(dpFields.length, 4, "expected displayport string of form (x,y,w,h)");
var dpWidth = dpFields[2];
var dpHeight = dpFields[3];
SimpleTest.ok(dpWidth >= 50 && dpHeight >= 50,
"expected a displayport at least as large as the scrollable element");
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 style="height: 50px; width: 50px; overflow: scroll">
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>
-44
View File
@@ -1,44 +0,0 @@
<!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">
function scrollOuter() {
SpecialPowers._addMessageListener("APZ:TransformEnd", function() {
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll);
});
const TOUCH_SLOP = 1;
synthesizeNativeDrag(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;
window.opener.is(outerScroll, 50, "check that the div scrolled");
window.opener.testDone();
}
window.onload = function() {
setTimeout(scrollOuter, 0);
}
</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>
-14
View File
@@ -1,14 +0,0 @@
<!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
View File
@@ -1,14 +0,0 @@
<!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>
@@ -1,41 +0,0 @@
<!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">
function scrollOuter() {
var outer = document.getElementById('outer');
SpecialPowers._addMessageListener("APZ:TransformEnd", function() {
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll, outer.contentWindow);
});
const TOUCH_SLOP = 1;
synthesizeNativeDrag(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;
window.opener.is(outerScroll, 50, "check that the iframe scrolled");
window.opener.testDone();
}
window.onload = function() {
setTimeout(scrollOuter, 0);
}
</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>
@@ -1,15 +0,0 @@
body {
height: 500px;
}
.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);
}
-8
View File
@@ -1,8 +0,0 @@
[DEFAULT]
support-files = apz_test_utils.js
[test_bug982141.html]
skip-if = toolkit != 'gonk' # bug 991198
support-files = helper_bug982141.html
[test_bug1151663.html]
skip-if = toolkit != 'gonk' # bug 991198
support-files = helper_bug1151663.html
@@ -31,19 +31,56 @@ function nativeHorizontalWheelEventMsg() {
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 nativeMouseMoveEventMsg() {
switch (getPlatform()) {
case "windows": return 1; // MOUSEEVENTF_MOVE
case "mac": return 5; // NSMouseMoved
case "linux": return 3; // GDK_MOTION_NOTIFY
}
throw "Native wheel events not supported on platform " + getPlatform();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
// to device pixels relative to aElement's containing window.
function coordinatesRelativeToWindow(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
};
}
// 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 |window|'s top-left. aDeltaX and aDeltaY
// are pixel deltas, and aObserver can be left undefined if not needed.
// 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) {
aX += window.mozInnerScreenX;
aY += window.mozInnerScreenY;
var pt = coordinatesRelativeToWindow(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();
_getDOMWindowUtils().sendNativeMouseScrollEvent(aX, aY, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
return true;
}
@@ -63,13 +100,80 @@ function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDel
}
// Synthesizes a native mousewheel event and invokes the callback once the
// wheel event is dispatched to the window. See synthesizeNativeWheel for
// details on the parameters.
function synthesizeNativeWheelAndWaitForEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
window.addEventListener("wheel", function wheelWaiter(e) {
window.removeEventListener("wheel", wheelWaiter);
// 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 = coordinatesRelativeToWindow(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 = coordinatesRelativeToWindow(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
return true;
}
function synthesizeNativeDrag(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 = coordinatesRelativeToWindow(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
return true;
}
@@ -97,3 +97,26 @@ function flushApzRepaints(aCallback, aWindow = window) {
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);
});
});
}
@@ -55,15 +55,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
// 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(compositorTestData.paints[lastCompositorPaintSeqNo]);
var apzcTree = buildApzcTree(paint);
// The apzc tree for this page should consist of a single root APZC,
// and no child APZCs.
SimpleTest.is(apzcTree.children.length, 1, "expected a single root APZC");
var rootApzc = apzcTree.children[0];
SimpleTest.is(rootApzc.children.length, 0, "expected no child APZCs");
// 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();
}
@@ -5,6 +5,7 @@ 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">
@@ -58,12 +59,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
// 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 root APZC
// and a child APZC for the scrollable <div>.
SimpleTest.is(apzcTree.children.length, 1, "expected a single root APZC");
var rootApzc = apzcTree.children[0];
SimpleTest.is(rootApzc.children.length, 1, "expected a single child APZC");
var childScrollId = rootApzc.children[0].scrollId;
// 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,
@@ -80,17 +82,27 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
SimpleTest.is(dpFields.length, 4, "expected displayport string of form (x,y,w,h)");
var dpWidth = dpFields[2];
var dpHeight = dpFields[3];
SimpleTest.ok(dpWidth >= 50 && dpHeight >= 50,
"expected a displayport at least as large as the scrollable element");
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(dpWidth >= subframe.clientWidth && dpHeight >= subframe.clientHeight,
"expected a displayport at least as large as the scrollable element, got " + childDisplayport);
window.opener.finishTest();
}
</script>
</head>
<body style="overflow: hidden;"><!-- Make sure the root frame is not scrollable -->
<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 style="height: 50px; width: 50px; overflow: scroll">
<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>
@@ -0,0 +1,70 @@
<!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) {
window.opener.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() {
if (!window.TouchEvent) {
window.opener.ok(true, "Touch events are not supported on this platform, sorry!\n");
window.opener.testDone();
return;
}
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) {
window.opener.is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
window.opener.testDone();
}
window.onload = function() {
waitForAllPaints(function() {
flushApzRepaints(startTest);
});
}
</script>
</head>
<body style="height: 5000px">
<div style="height: 50px">spacer</div>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>
@@ -3,7 +3,7 @@ body {
}
.inner-frame {
margin-top: 25%;
margin-top: 50px; /* this should be at least 30px */
height: 200%;
width: 75%;
overflow: scroll;
@@ -0,0 +1,42 @@
<!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() {
if (!window.TouchEvent) {
window.opener.ok(true, "Touch events are not supported on this platform, sorry!\n");
window.opener.testDone();
return;
}
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) {
window.opener.is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
window.opener.testDone();
}
window.onload = function() {
waitForAllPaints(function() {
flushApzRepaints(clickButton);
});
}
</script>
</head>
<body>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>
+13 -3
View File
@@ -1,17 +1,22 @@
[test_bug982141.html]
skip-if = toolkit != 'gonk' # bug 991198
[DEFAULT]
support-files =
apz_test_utils.js
apz_test_native_event_utils.js
helper_bug982141.html
helper_bug1151663.html
helper_iframe1.html
helper_iframe2.html
helper_subframe_style.css
helper_basic_pan.html
helper_div_pan.html
helper_iframe_pan.html
helper_scrollto_tap.html
helper_tap.html
tags = apz
[test_bug982141.html]
[test_bug1151663.html]
[test_wheel_scroll.html]
skip-if = (os == 'android') || (os == 'b2g') # wheel events not supported
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_wheel_transactions.html]
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_bug1151667.html]
@@ -28,3 +33,8 @@ skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel ev
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_scroll_subframe_scrollbar.html]
skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
[test_frame_reconstruction.html]
[test_tap.html]
# Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
# On OS X we don't support touch events at all.
skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
@@ -8,7 +8,6 @@
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.testInChaosMode();
// this page just serially loads each one of the following test helper pages in
// a new window and waits for it to call testDone()
@@ -53,12 +52,19 @@ window.onload = function() {
["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.
["ui.click_hold_context_menus.delay", "10000"],
// 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-stop threshold velocity to absurdly high.
["apz.fling_stopped_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],
]
}, testDone);
};
@@ -17,6 +17,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
// inside an iframe which means we have no control over the root APZC.
var w = null;
window.onload = function() {
if (!SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled) {
ok(true, "APZ is not enabled, this test is not relevant, sorry!\n");
SimpleTest.finish();
return;
}
SpecialPowers.pushPrefEnv({"set": [["apz.test.logging_enabled", true]]}, function() {
w = window.open("helper_bug1151663.html", "_blank");
});
@@ -17,7 +17,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
// inside an iframe which means we have no control over the root APZC.
var w = null;
window.onload = function() {
w = window.open("helper_bug982141.html", "_blank");
if (!SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled) {
ok(true, "APZ is not enabled, this test is not relevant, sorry!\n");
SimpleTest.finish();
return;
}
SpecialPowers.pushPrefEnv({"set": [["apz.test.logging_enabled", true]]}, function() {
w = window.open("helper_bug982141.html", "_blank");
});
};
function finishTest() {
@@ -0,0 +1,231 @@
<!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* runTest() {
var elm = document.getElementsByClassName('inner')[0];
elm.scrollTop = 0;
yield flushApzRepaints(driveTest);
// 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(driveTest, 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(driveTest);
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(driveTest);
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(driveTest, 0); });
yield flushApzRepaints(driveTest);
is(elm.scrollTop, 200, "Element should have scrolled by 200px");
}
var gTestContinuation = null;
function driveTest() {
if (!gTestContinuation) {
gTestContinuation = runTest();
}
var ret = gTestContinuation.next();
if (ret.done) {
SimpleTest.finish();
}
}
SimpleTest.waitForExplicitFinish();
var apzEnabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
if (!apzEnabled) {
ok(true, "APZ not enabled, skipping test");
SimpleTest.finish();
} else {
SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
SimpleTest.waitForFocus(driveTest, window);
}
</script>
</body>
</html>
@@ -54,7 +54,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
<script type="application/javascript;version=1.7">
// Scroll the mouse wheel over |element|.
function scrollWheelOver(element) {
function scrollWheelOver(element, waitForScroll = true) {
var x = 10;
var y = 10;
// Move the mouse to the desired wheel location.
@@ -65,13 +65,19 @@ function scrollWheelOver(element) {
// wheel event, otherwise there is a chance they might get reordered, and
// we have the transaction problem again.
synthesizeNativeMouseMoveAndWaitForMoveEvent(element, x, y, function() {
synthesizeNativeWheelAndWaitForScrollEvent(element, x, y, 0, -10, driveTest);
if (waitForScroll) {
synthesizeNativeWheelAndWaitForScrollEvent(element, x, y, 0, -10, driveTest);
} else {
synthesizeNativeWheelAndWaitForWheelEvent(element, x, y, 0, -10, driveTest);
}
});
}
var gTestContinuation = null;
var utils;
const DISPLAYPORT_EXPIRY = 100;
// Return whether the element with id |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.
@@ -91,6 +97,14 @@ function isLayerized(elementId) {
return false;
}
// Helper function to pass to waitForAllPaints rather than passing driveTest
// directly. If there are no paints pending waitForAllPaints will invoke the
// callback synchronously, and if we did waitForAllPaints(driveTest) that might
// cause reentrancy into driveTest which is bad.
function callDriveTestAsync() {
setTimeout(driveTest, 0);
}
function* runTest() {
utils = SpecialPowers.getDOMWindowUtils(window);
@@ -126,6 +140,74 @@ function* runTest() {
yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'));
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]]}, driveTest);
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'));
yield waitForAllPaints(function() {
flushApzRepaints(driveTest);
});
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
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'));
yield waitForAllPaints(function() {
flushApzRepaints(driveTest);
});
// Once the expiry elapses, it will trigger expiry on outer2, so we check
// both, one at a time.
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
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.
yield scrollWheelOver(document.getElementById('outer3').contentDocument.getElementById('inner3'), false);
yield waitForAllPaints(function() {
flushApzRepaints(driveTest);
});
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
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);
yield waitForAllPaints(function() {
flushApzRepaints(driveTest);
});
// Wait for the expiry to elapse
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync);
ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
}
function driveTest() {
@@ -153,7 +235,8 @@ function startTest() {
}
SimpleTest.waitForExplicitFinish();
SimpleTest.testInChaosMode();
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
@@ -161,6 +244,7 @@ SimpleTest.testInChaosMode();
// Also enable APZ test logging, since we use that data to determine whether
// a scroll frame was layerized.
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
["apz.displayport_expiry_ms", 0],
["apz.test.logging_enabled", true]]},
function() {
SimpleTest.waitForFocus(startTest, window);
@@ -536,6 +536,7 @@ window.onload = function() {
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
</script>
</body>
@@ -5,6 +5,7 @@
<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>
@@ -35,15 +36,22 @@ function test() {
});
}
function startTest() {
waitForAllPaints(function() {
flushApzRepaints(test);
});
}
window.onload = function() {
SpecialPowers.pushPrefEnv({
'set': [['general.smoothScroll', false],
['mousewheel.transaction.timeout', 1000000]],
}, function () {
SimpleTest.waitForFocus(test);
SimpleTest.waitForFocus(startTest);
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
</script>
</body>
@@ -5,6 +5,7 @@
<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 {
@@ -103,13 +104,19 @@ function testScrolling(subframe) {
});
}
function startTest() {
waitForAllPaints(function() {
flushApzRepaints(test);
});
}
window.onload = function() {
SpecialPowers.pushPrefEnv({
'set': [['general.smoothScroll', false],
['mousewheel.transaction.timeout', 0],
['mousewheel.transaction.ignoremovedelay', 0]]
}, function () {
SimpleTest.waitForFocus(test);
SimpleTest.waitForFocus(startTest);
});
}
@@ -32,7 +32,7 @@
// Scroll diff
var dx = 0;
var dy = -10; // Negative to scroll down
synthesizeNativeWheelAndWaitForEvent(scrollDiv, x, y, dx, dy);
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
@@ -61,12 +61,6 @@
SimpleTest.finish();
}
var hwVsyncEnabled = SpecialPowers.getBoolPref("gfx.vsync.hw-vsync.enabled");
if (!hwVsyncEnabled) {
SimpleTest.ok(true, "Hardware vsync not enabled, skipping test");
SimpleTest.finish();
}
SpecialPowers.pushPrefEnv({
"set" : [
[testPref, true]
@@ -0,0 +1,84 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Sanity panning test</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// this page just serially loads each one of the following test helper pages in
// a new window and waits for it to call testDone()
var tests = [
{'file': 'helper_tap.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}
];
var testIndex = -1;
var w = null;
function testDone() {
var test = tests[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
// testDone() after that's been processed
SpecialPowers.popPrefEnv(function() {
w.close();
w = null;
testDone();
});
return;
}
w.close();
}
testIndex++;
if (testIndex >= tests.length) {
SimpleTest.finish();
return;
}
test = tests[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);
}
if (!!test.prefs) {
// Got some prefs for this subtest, push them
SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
w = window.open(test.file, "_blank");
});
} else {
w = window.open(test.file, "_blank");
}
}
window.onload = function() {
if (!SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled) {
ok(true, "APZ is not enabled, this test is not relevant, sorry!\n");
SimpleTest.finish();
return;
}
testDone();
};
</script>
</head>
<body>
</body>
</html>
@@ -78,7 +78,7 @@ document.getElementById("scrollbox").addEventListener("wheel", function (e) {
function* runTest() {
var content = document.getElementById('content');
for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
yield synthesizeNativeWheelAndWaitForEvent(content, 100, 150, 0, -5, continueTest);
yield synthesizeNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5, continueTest);
}
var scrollbox = document.getElementById('scrollbox');
is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
@@ -106,6 +106,7 @@ function startTest() {
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}, continueTest);
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(startTest, window);
@@ -114,4 +115,3 @@ SimpleTest.waitForFocus(startTest, window);
</body>
</html>
@@ -7,7 +7,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
<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 {
@@ -131,13 +133,19 @@ function driveTest() {
}
function startTest() {
// Disable smooth scrolling because it makes the test flaky (we don't have a good
// way of detecting when the scrolling is finished).
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}, driveTest);
waitForAllPaints(function() {
flushApzRepaints(driveTest);
});
}
// Disable smooth scrolling because it makes the test flaky (we don't have a good
// way of detecting when the scrolling is finished).
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]},
function() {
SimpleTest.waitForFocus(startTest, window);
});
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(startTest, window);
</script>
</pre>
@@ -2,7 +2,7 @@
<html class="reftest-wait"><head>
<meta name="viewport" content="width=device-width">
</head>
<body onload="scrollTo(450,10000); document.documentElement.classList.remove('reftest-wait')">
<body onload="scrollTo(450,8000); document.documentElement.classList.remove('reftest-wait')">
<div style="width: 9000px; height: 20000px; background: white;"></div>
</body>
</html>
@@ -3,7 +3,7 @@
<meta name="viewport" content="width=device-width">
<style> html { direction: rtl; } </style>
</head>
<body onload="scrollTo(-450,10000); document.documentElement.classList.remove('reftest-wait')">
<body onload="scrollTo(-450,8000); document.documentElement.classList.remove('reftest-wait')">
<div style="width: 9000px; height: 20000px; background: white;"></div>
</body>
</html>
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html class="reftest-wait"
reftest-async-scroll
reftest-async-scroll-x="-440" reftest-async-scroll-y="9999"><head>
reftest-async-scroll-x="-440" reftest-async-scroll-y="7999"><head>
<meta name="viewport" content="width=device-width">
<style> html { direction: rtl; } </style>
</head>
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html class="reftest-wait"
reftest-async-scroll
reftest-async-scroll-x="449" reftest-async-scroll-y="9999"><head>
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
@@ -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>
@@ -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>
+17
View File
@@ -0,0 +1,17 @@
# 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-ref.html
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
skip-if(!asyncPan) fuzzy-if(Android,6,8) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
skip-if(!asyncPan) fuzzy-if(Android,13,8) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
skip-if(!asyncPan) fuzzy-if(Android,8,10) == 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.
skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
# Meta-viewport tag support
skip-if(!asyncZoom) == initial-scale-1.html initial-scale-1-ref.html
-57
View File
@@ -1,57 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Sanity panning test</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.testInChaosMode();
// this page just serially loads each one of the following test helper pages in
// a new window and waits for it to call testDone()
var tests = [
'helper_basic_pan.html',
'helper_div_pan.html',
'helper_iframe_pan.html',
];
var testIndex = -1;
var w = null;
function testDone() {
if (w) {
w.close();
}
testIndex++;
if (testIndex < tests.length) {
w = window.open(tests[testIndex], "_blank");
} else {
SimpleTest.finish();
}
}
window.onload = function() {
SpecialPowers.pushPrefEnv(
{ "set":
[
// 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 B2G emulator is hella slow, and needs more than 300ms to run the
// main-thread code that deals with layerizing subframes and running
// touch listeners. In my local runs this needs to be at least 1000.
["apz.content_response_timeout", "5000"]
]
}, testDone);
};
</script>
</head>
<body>
</body>
</html>
-63
View File
@@ -1,63 +0,0 @@
<!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="text/javascript" src="/tests/SimpleTest/EventUtils.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>
#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();
SimpleTest.waitForFocus(startTest, window);
</script>
</pre>
</body>
</html>
-35
View File
@@ -1,35 +0,0 @@
<!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>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
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() {
SpecialPowers.pushPrefEnv({"set": [["apz.test.logging_enabled", true]]}, 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>
-168
View File
@@ -1,168 +0,0 @@
<!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="text/javascript" src="/tests/SimpleTest/EventUtils.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) {
var x = 10;
var y = 10;
// Move the mouse to the desired wheel location.
// Not doing so can result in the wheel events from two consecutive
// scrollWheelOver() 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.
synthesizeNativeMouseMoveAndWaitForMoveEvent(element, x, y, function() {
synthesizeNativeWheelAndWaitForScrollEvent(element, x, y, 0, -10, driveTest);
});
}
var gTestContinuation = null;
var utils;
// Return whether the element with id |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 = utils.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* runTest() {
utils = SpecialPowers.getDOMWindowUtils(window);
// 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'));
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'));
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);
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'));
ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
}
function driveTest() {
if (!gTestContinuation) {
gTestContinuation = runTest();
}
var ret = gTestContinuation.next();
if (ret.done) {
SimpleTest.finish();
}
}
function startTest() {
// This test requires APZ - if it's not enabled, skip it.
var apzEnabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
if (!apzEnabled) {
ok(true, "APZ not enabled, skipping test");
SimpleTest.finish();
return;
}
driveTest();
}
SimpleTest.waitForExplicitFinish();
SimpleTest.testInChaosMode();
// 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.
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
["apz.test.logging_enabled", true]]},
function() {
SimpleTest.waitForFocus(startTest, window);
});
</script>
</pre>
</body>
</html>
@@ -1,50 +0,0 @@
<!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>
<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();
});
}
window.onload = function() {
SpecialPowers.pushPrefEnv({
'set': [['general.smoothScroll', false],
['mousewheel.transaction.timeout', 1000000]],
}, function () {
SimpleTest.waitForFocus(test);
});
}
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>
-117
View File
@@ -1,117 +0,0 @@
<!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="text/javascript" src="/tests/SimpleTest/EventUtils.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* runTest() {
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, continueTest);
}
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");
}
var gTestContinuation = null;
function continueTest() {
if (!gTestContinuation) {
gTestContinuation = runTest();
}
var ret = gTestContinuation.next();
if (ret.done) {
SimpleTest.finish();
} else {
is(ret.value, true, "Wheel event successfully synthesized");
}
}
function startTest() {
// 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.
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}, continueTest);
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(startTest, window);
</script>
</pre>
</body>
</html>
@@ -1,142 +0,0 @@
<!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="apz_test_native_event_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) {
synthesizeNativeWheelAndWaitForScrollEvent(element, 10, 10, 0, deltaY, driveTest);
}
function* runTest() {
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);
}
// 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);
// 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]]}, driveTest);
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);
// Wait for the transaction timeout to elapse.
yield window.setTimeout(driveTest, timeout);
// 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);
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);
// 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.
yield window.setTimeout(driveTest, timeout);
// Continued downward scrolling should scroll |outer| to the bottom.
prevScrollTop = outer.scrollTop;
while (outer.scrollTop < outer.scrollTopMax) {
yield scrollWheelOver(outer, -10);
// Avoid a failure getting us into an infinite loop.
ok(outer.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
prevScrollTop = outer.scrollTop;
}
}
var gTestContinuation = null;
function driveTest() {
if (!gTestContinuation) {
gTestContinuation = runTest();
}
var ret = gTestContinuation.next();
if (ret.done) {
SimpleTest.finish();
}
}
function startTest() {
// Disable smooth scrolling because it makes the test flaky (we don't have a good
// way of detecting when the scrolling is finished).
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}, driveTest);
}
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(startTest, window);
</script>
</pre>
</body>
</html>
@@ -258,7 +258,6 @@ APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics)
// adjusts the display port margins, so do it before we set those.
ScrollFrame(content, aMetrics);
MOZ_ASSERT(nsLayoutUtils::HasDisplayPort(content));
SetDisplayPortMargins(shell, content, aMetrics);
SetPaintRequestTime(content, aMetrics.GetPaintRequestTime());
}
@@ -90,7 +90,10 @@ ActiveElementManager::TriggerElementActivation()
if (!mCanBePan) {
SetActive(mTarget);
} else {
CancelTask(); // this is only needed because of bug 1169802. Fixing that
// bug properly should make this unnecessary.
MOZ_ASSERT(mSetActiveTask == nullptr);
mSetActiveTask = NewRunnableMethod(
this, &ActiveElementManager::SetActiveTask, mTarget);
MessageLoop::current()->PostDelayedTask(
@@ -28,6 +28,7 @@ class APZEventState;
// tree.
class ChromeProcessController : public mozilla::layers::GeckoContentController
{
protected:
typedef mozilla::layers::FrameMetrics FrameMetrics;
typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
+3 -2
View File
@@ -864,7 +864,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
paintLayerContext.Apply2DTransform();
const nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
// If needsGroup is true, we'll clip to the visible region after we've popped the group
if (needsClipToVisibleRegion && !needsGroup) {
gfxUtils::ClipToRegion(aTarget, visibleRegion);
@@ -897,7 +897,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
return;
}
const IntRect& bounds = visibleRegion.GetBounds();
IntRect bounds = visibleRegion.GetBounds();
RefPtr<DrawTarget> untransformedDT =
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
SurfaceFormat::B8G8R8A8);
@@ -934,6 +934,7 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
ToRect(aTarget->GetClipExtents()));
xformBounds.RoundOut();
effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
RefPtr<DrawTarget> xformDT =
+18 -16
View File
@@ -71,14 +71,14 @@ void
CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
{
AutoRemoveTexture autoRemove(this);
if (mBuffer &&
(mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) {
autoRemove.mTexture = mBuffer;
mBuffer = nullptr;
if (mBackBuffer &&
(mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) {
autoRemove.mTexture = mBackBuffer;
mBackBuffer = nullptr;
}
bool bufferCreated = false;
if (!mBuffer) {
if (!mBackBuffer) {
bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
gfxContentType contentType = isOpaque
? gfxContentType::COLOR
@@ -90,46 +90,48 @@ CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
}
mBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
if (!mBuffer) {
mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
if (!mBackBuffer) {
NS_WARNING("Failed to allocate the TextureClient");
return;
}
MOZ_ASSERT(mBuffer->CanExposeDrawTarget());
MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
bufferCreated = true;
}
bool updated = false;
{
TextureClientAutoLock autoLock(mBuffer, OpenMode::OPEN_WRITE_ONLY);
TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
if (!autoLock.Succeeded()) {
mBuffer = nullptr;
mBackBuffer = nullptr;
return;
}
RefPtr<DrawTarget> target = mBuffer->BorrowDrawTarget();
RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
if (target) {
aLayer->UpdateTarget(target);
updated = true;
}
}
if (bufferCreated && !AddTextureClient(mBuffer)) {
mBuffer = nullptr;
if (bufferCreated && !AddTextureClient(mBackBuffer)) {
mBackBuffer = nullptr;
return;
}
if (updated) {
AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
t->mTextureClient = mBuffer;
t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBuffer->GetSize());
t->mTextureClient = mBackBuffer;
t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
t->mFrameID = mFrameID;
t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID();
GetForwarder()->UseTextures(this, textures);
mBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
}
mBackBuffer.swap(mFrontBuffer);
}
already_AddRefed<TextureClient>
+4 -3
View File
@@ -99,7 +99,7 @@ public:
virtual void Clear() override
{
mBuffer = nullptr;
mBackBuffer = mFrontBuffer = nullptr;
}
virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
@@ -112,7 +112,7 @@ public:
virtual void OnDetach() override
{
mBuffer = nullptr;
mBackBuffer = mFrontBuffer = nullptr;
}
private:
@@ -122,7 +122,8 @@ private:
TextureFlags aFlags,
ClientCanvasLayer* aLayer);
RefPtr<TextureClient> mBuffer;
RefPtr<TextureClient> mBackBuffer;
RefPtr<TextureClient> mFrontBuffer;
};
// Used for GL canvases where we don't need to do any readback, i.e., with a
+7
View File
@@ -62,6 +62,13 @@ public:
}
}
virtual void HandleMemoryPressure() override
{
if (mCanvasClient) {
mCanvasClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = CanvasLayerAttributes(mFilter, mBounds);
+7
View File
@@ -60,6 +60,13 @@ protected:
DestroyBackBuffer();
}
virtual void HandleMemoryPressure() override
{
if (mImageClient) {
mImageClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = ImageLayerAttributes(mFilter, mScaleToSize, mScaleMode);
+14
View File
@@ -762,6 +762,10 @@ ClientLayerManager::ClearCachedResources(Layer* aSubtree)
void
ClientLayerManager::HandleMemoryPressure()
{
if (mRoot) {
HandleMemoryPressureLayer(mRoot);
}
for (size_t i = 0; i < mTexturePools.Length(); i++) {
mTexturePools[i]->ShrinkToMinimumSize();
}
@@ -777,6 +781,16 @@ ClientLayerManager::ClearLayer(Layer* aLayer)
}
}
void
ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
{
ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
HandleMemoryPressureLayer(child);
}
}
void
ClientLayerManager::GetBackendName(nsAString& aName)
{
+6
View File
@@ -293,6 +293,8 @@ private:
void ClearLayer(Layer* aLayer);
void HandleMemoryPressureLayer(Layer* aLayer);
bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags);
@@ -380,6 +382,10 @@ public:
virtual void ClearCachedResources() { }
// Shrink memory usage.
// Called when "memory-pressure" is observed.
virtual void HandleMemoryPressure() { }
virtual void RenderLayer() = 0;
virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
+8 -1
View File
@@ -76,7 +76,14 @@ public:
mValidRegion.SetEmpty();
DestroyBackBuffer();
}
virtual void HandleMemoryPressure() override
{
if (mContentClient) {
mContentClient->HandleMemoryPressure();
}
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
{
aAttrs = PaintedLayerAttributes(GetValidRegion());
@@ -73,6 +73,13 @@ public:
virtual void ClearCachedResources() override;
virtual void HandleMemoryPressure() override
{
if (mContentClient) {
mContentClient->HandleMemoryPressure();
}
}
/**
* Helper method to find the nearest ancestor layers which
* scroll and have a displayport. The parameters are out-params
+9 -1
View File
@@ -243,7 +243,15 @@ void
CompositableClient::ClearCachedResources()
{
if (mTextureClientRecycler) {
mTextureClientRecycler = nullptr;
mTextureClientRecycler->ShrinkToMinimumSize();
}
}
void
CompositableClient::HandleMemoryPressure()
{
if (mTextureClientRecycler) {
mTextureClientRecycler->ShrinkToMinimumSize();
}
}
+6
View File
@@ -195,6 +195,12 @@ public:
*/
virtual void ClearCachedResources();
/**
* Shrink memory usage.
* Called when "memory-pressure" is observed.
*/
virtual void HandleMemoryPressure();
/**
* Should be called when deataching a TextureClient from a Compositable, because
* some platforms need to do some extra book keeping when this happens (for
+1 -1
View File
@@ -357,7 +357,7 @@ ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
// changes and some changed buffer content isn't reflected in the
// draw or invalidate region (on purpose!). When this happens, we
// need to read back the entire buffer too.
updatedRegion = aVisibleRegion;
updatedRegion = aVisibleRegion.GetBounds();
mIsNewBuffer = false;
} else {
updatedRegion = aRegionToDraw;
+8 -4
View File
@@ -4,7 +4,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageClient.h"
#include <stdint.h> // for uint32_t
#include "ClientLayerManager.h" // for ClientLayer
#include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc
#include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc
#include "GLImages.h" // for SurfaceTextureImage::Data, etc
@@ -12,6 +15,7 @@
#include "gfxPlatform.h" // for gfxPlatform
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/BaseSize.h" // for BaseSize
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
@@ -29,8 +33,7 @@
#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
#include "nsISupportsImpl.h" // for Image::Release, etc
#include "nsRect.h" // for mozilla::gfx::IntRect
#include "mozilla/gfx/2D.h"
#include "ClientLayerManager.h"
#ifdef MOZ_WIDGET_GONK
#include "GrallocImages.h"
#endif
@@ -174,11 +177,12 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag
#endif
RefPtr<TextureClient> texture = image->GetTextureClient(this);
const bool hasTextureClient = !!texture;
for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
if (mBuffers[i].mImageSerial == image->GetSerial()) {
if (texture) {
MOZ_ASSERT(texture == mBuffers[i].mTextureClient);
if (hasTextureClient) {
MOZ_ASSERT(image->GetTextureClient(this) == mBuffers[i].mTextureClient);
} else {
texture = mBuffers[i].mTextureClient;
}
@@ -144,6 +144,15 @@ TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
aTextureFlags, aAllocFlags);
}
void
TextureClientRecycleAllocator::ShrinkToMinimumSize()
{
MutexAutoLock lock(mLock);
while (!mPooledClients.empty()) {
mPooledClients.pop();
}
}
void
TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
{
@@ -49,6 +49,8 @@ public:
TextureFlags aTextureFlags,
TextureAllocationFlags flags = ALLOC_DEFAULT);
void ShrinkToMinimumSize();
protected:
virtual already_AddRefed<TextureClient>
Allocate(gfx::SurfaceFormat aFormat,
@@ -168,6 +168,23 @@ ImageLayerComposite::IsOpaque()
return false;
}
nsIntRegion
ImageLayerComposite::GetFullyRenderedRegion()
{
if (!mImageHost ||
!mImageHost->IsAttached()) {
return GetShadowVisibleRegion().ToUnknownRegion();
}
if (mScaleMode == ScaleMode::STRETCH) {
nsIntRegion shadowVisibleRegion;
shadowVisibleRegion.And(GetShadowVisibleRegion().ToUnknownRegion(), nsIntRegion(gfx::IntRect(0, 0, mScaleToSize.width, mScaleToSize.height)));
return shadowVisibleRegion;
}
return GetShadowVisibleRegion().ToUnknownRegion();
}
CompositableHost*
ImageLayerComposite::GetCompositableHost()
{
@@ -61,6 +61,8 @@ public:
virtual bool IsOpaque() override;
virtual nsIntRegion GetFullyRenderedRegion() override;
protected:
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+1 -1
View File
@@ -553,7 +553,7 @@ public:
* While progressive drawing is in progress this region will be
* a subset of the shadow visible region.
*/
nsIntRegion GetFullyRenderedRegion();
virtual nsIntRegion GetFullyRenderedRegion();
protected:
gfx::Matrix4x4 mShadowTransform;
@@ -36,12 +36,6 @@ TiledLayerBufferComposite::~TiledLayerBufferComposite()
Clear();
}
/* static */ void
TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
{
textureHost->CompositorRecycle();
}
void
TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
{
-4
View File
@@ -146,10 +146,6 @@ public:
void SetCompositor(Compositor* aCompositor);
// Recycle callback for TextureHost.
// Used when TiledContentClient is present in client side.
static void RecycleCallback(TextureHost* textureHost, void* aClosure);
protected:
CSSToParentLayerScale2D mFrameResolution;
+4 -1
View File
@@ -642,7 +642,9 @@ static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor)
{
CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11()
: nullptr;
MOZ_DIAGNOSTIC_ASSERT(!!compositor);
if (!compositor) {
gfxCriticalNote << "[D3D11] Attempt to set an incompatible compositor";
}
return compositor;
}
@@ -670,6 +672,7 @@ DXGITextureHostD3D11::Lock()
}
if (!mTextureSource) {
if (!mTexture && !OpenSharedHandle()) {
gfxWindowsPlatform::GetPlatform()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
return false;
}
+1 -1
View File
@@ -672,7 +672,7 @@ CompositorD3D9::FailedToResetDevice() {
if (mFailedResetAttempts > 10) {
mFailedResetAttempts = 0;
gfxWindowsPlatform::GetPlatform()->D3D9DeviceReset();
gfxWarning() << "[D3D9] Unable to get a working D3D9 Compositor";
gfxCriticalNote << "[D3D9] Unable to get a working D3D9 Compositor";
}
}
+1 -1
View File
@@ -905,7 +905,7 @@ TextureHostD3D9::UpdatedInternal(const nsIntRegion* aRegion)
}
if (!mTextureSource->UpdateFromTexture(mTexture, regionToUpdate)) {
gfxWarning() << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed";
gfxCriticalNote << "[D3D9] DataTextureSourceD3D9::UpdateFromTexture failed";
}
}
+17
View File
@@ -0,0 +1,17 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
include('/ipc/chromium/chromium-config.mozbuild')
Library('chromium_atomics')
# This test is copied from ../moz.build for atomicops_internals_mutex.cc
ost = CONFIG['OS_TEST']
if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost:
SOURCES += [
'../src/base/atomicops_internals_mutex.cc',
'../src/base/lock_impl_posix.cc',
]
+5 -5
View File
@@ -9,6 +9,7 @@ include(libevent_path_prefix + '/libeventcommon.mozbuild')
UNIFIED_SOURCES += [
'src/base/at_exit.cc',
'src/base/buffer.cc',
'src/base/command_line.cc',
'src/base/file_path.cc',
'src/base/file_util.cc',
@@ -17,7 +18,6 @@ UNIFIED_SOURCES += [
'src/base/logging.cc',
'src/base/message_loop.cc',
'src/base/message_pump_default.cc',
'src/base/non_thread_safe.cc',
'src/base/pickle.cc',
'src/base/rand_util.cc',
'src/base/revocable_store.cc',
@@ -34,11 +34,7 @@ UNIFIED_SOURCES += [
'src/chrome/common/child_thread.cc',
'src/chrome/common/chrome_switches.cc',
'src/chrome/common/ipc_channel.cc',
'src/chrome/common/ipc_channel_proxy.cc',
'src/chrome/common/ipc_message.cc',
'src/chrome/common/ipc_sync_channel.cc',
'src/chrome/common/ipc_sync_message.cc',
'src/chrome/common/message_router.cc',
'src/chrome/common/notification_service.cc',
]
@@ -165,3 +161,7 @@ CXXFLAGS += CONFIG['TK_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
DIRS += [
'atomics',
]
+125
View File
@@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "buffer.h"
Buffer::Buffer()
: mBuffer(nullptr),
mSize(0),
mReserved(0)
{
}
Buffer::~Buffer()
{
if (mBuffer) {
free(mBuffer);
}
}
bool
Buffer::empty() const
{
return mSize == 0;
}
size_t
Buffer::size() const
{
return mSize;
}
const char*
Buffer::data() const
{
return mBuffer;
}
void
Buffer::clear()
{
free(mBuffer);
mBuffer = nullptr;
mSize = 0;
mReserved = 0;
}
void
Buffer::try_realloc(size_t newlength)
{
char* buffer = (char*)realloc(mBuffer, newlength);
if (buffer) {
mBuffer = buffer;
mReserved = newlength;
return;
}
// If we're growing the buffer, crash. If we're shrinking, then we continue to
// use the old (larger) buffer.
MOZ_RELEASE_ASSERT(newlength <= mReserved);
}
void
Buffer::append(const char* bytes, size_t length)
{
if (mSize + length > mReserved) {
try_realloc(mSize + length);
}
memcpy(mBuffer + mSize, bytes, length);
mSize += length;
}
void
Buffer::assign(const char* bytes, size_t length)
{
if (bytes >= mBuffer && bytes < mBuffer + mReserved) {
MOZ_RELEASE_ASSERT(bytes + length <= mBuffer + mSize);
memmove(mBuffer, bytes, length);
mSize = length;
try_realloc(length);
} else {
try_realloc(length);
mSize = length;
memcpy(mBuffer, bytes, length);
}
}
void
Buffer::erase(size_t start, size_t count)
{
mSize -= count;
memmove(mBuffer + start, mBuffer + start + count, mSize - start);
try_realloc(mSize);
}
void
Buffer::reserve(size_t size)
{
if (mReserved < size) {
try_realloc(size);
}
}
char*
Buffer::trade_bytes(size_t count)
{
char* result = mBuffer;
mSize = mReserved = mSize - count;
mBuffer = mReserved ? (char*)malloc(mReserved) : nullptr;
MOZ_RELEASE_ASSERT(!mReserved || mBuffer);
if (mSize) {
memcpy(mBuffer, result + count, mSize);
}
// Try to resize the buffer down, but ignore failure. This can cause extra
// copies, but so be it.
char* resized = (char*)realloc(result, count);
if (resized) {
return resized;
}
return result;
}
+44
View File
@@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CHROME_BASE_BUFFER_H_
#define CHROME_BASE_BUFFER_H_
// Buffer is a simple std::string-like class for buffering up IPC messages. Its
// main distinguishing characteristic is the trade_bytes function.
class Buffer {
public:
Buffer();
~Buffer();
bool empty() const;
const char* data() const;
size_t size() const;
void clear();
void append(const char* bytes, size_t length);
void assign(const char* bytes, size_t length);
void erase(size_t start, size_t count);
void reserve(size_t size);
// This function should be used by a caller who wants to extract the first
// |count| bytes from the buffer. Rather than copying the bytes out, this
// function returns the entire buffer. The bytes in range [count, size()) are
// copied out to a new buffer which becomes the current buffer. The
// presumption is that |count| is very large and approximately equal to size()
// so not much needs to be copied.
char* trade_bytes(size_t count);
private:
void try_realloc(size_t newlength);
char* mBuffer;
size_t mSize;
size_t mReserved;
};
#endif // CHROME_BASE_BUFFER_H_
-24
View File
@@ -1,24 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/non_thread_safe.h"
// These checks are only done in debug builds.
#ifndef NDEBUG
#include "base/logging.h"
NonThreadSafe::NonThreadSafe()
: valid_thread_id_(PlatformThread::CurrentId()) {
}
bool NonThreadSafe::CalledOnValidThread() const {
return valid_thread_id_ == PlatformThread::CurrentId();
}
NonThreadSafe::~NonThreadSafe() {
DCHECK(CalledOnValidThread());
}
#endif // NDEBUG
-52
View File
@@ -1,52 +0,0 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_NON_THREAD_SAFE_H__
#define BASE_NON_THREAD_SAFE_H__
#include "base/platform_thread.h"
// A helper class used to help verify that methods of a class are
// called from the same thread. One can inherit from this class and use
// CalledOnValidThread() to verify.
//
// This is intended to be used with classes that appear to be thread safe, but
// aren't. For example, a service or a singleton like the preferences system.
//
// Example:
// class MyClass : public NonThreadSafe {
// public:
// void Foo() {
// DCHECK(CalledOnValidThread());
// ... (do stuff) ...
// }
// }
//
// In Release mode, CalledOnValidThread will always return true.
//
#ifndef NDEBUG
class NonThreadSafe {
public:
NonThreadSafe();
~NonThreadSafe();
bool CalledOnValidThread() const;
private:
PlatformThreadId valid_thread_id_;
};
#else
// Do nothing in release mode.
class NonThreadSafe {
public:
NonThreadSafe() {}
~NonThreadSafe() {}
bool CalledOnValidThread() const {
return true;
}
};
#endif // NDEBUG
#endif // BASE_NON_THREAD_SAFE_H__
+22 -2
View File
@@ -122,10 +122,10 @@ Pickle::Pickle(int header_size)
header_->payload_size = 0;
}
Pickle::Pickle(const char* data, int data_len)
Pickle::Pickle(const char* data, int data_len, Ownership ownership)
: header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
header_size_(0),
capacity_(kCapacityReadOnly),
capacity_(ownership == BORROWS ? kCapacityReadOnly : data_len),
variable_buffer_offset_(0) {
if (data_len >= static_cast<int>(sizeof(Header)))
header_size_ = data_len - header_->payload_size;
@@ -670,3 +670,23 @@ const char* Pickle::FindNext(uint32_t header_size,
return start + header_size + hdr->payload_size;
}
// static
uint32_t Pickle::GetLength(uint32_t header_size,
const char* start,
const char* end) {
DCHECK(header_size == AlignInt(header_size));
DCHECK(header_size <= static_cast<memberAlignmentType>(kPayloadUnit));
if (end < start)
return 0;
size_t length = static_cast<size_t>(end - start);
if (length < sizeof(Header))
return 0;
const Header* hdr = reinterpret_cast<const Header*>(start);
if (length < header_size)
return 0;
return header_size + hdr->payload_size;
}
+23 -9
View File
@@ -32,6 +32,11 @@
//
class Pickle {
public:
enum Ownership {
BORROWS,
OWNS,
};
~Pickle();
// Initialize a Pickle object using the default header size.
@@ -42,11 +47,13 @@ class Pickle {
// will be rounded up to ensure that the header size is 32bit-aligned.
explicit Pickle(int header_size);
// Initializes a Pickle from a const block of data. The data is not copied;
// instead the data is merely referenced by this Pickle. Only const methods
// should be used on the Pickle when initialized this way. The header
// padding size is deduced from the data length.
Pickle(const char* data, int data_len);
// Initializes a Pickle from a const block of data. If ownership == BORROWS,
// the data is not copied; instead the data is merely referenced by this
// Pickle. Only const methods should be used on the Pickle when initialized
// this way. The header padding size is deduced from the data length. If
// ownership == OWNS, then again no copying takes place. However, the buffer
// is writable and will be freed when this Pickle is destroyed.
Pickle(const char* data, int data_len, Ownership ownership = BORROWS);
// Initializes a Pickle as a deep copy of another Pickle.
Pickle(const Pickle& other);
@@ -62,6 +69,11 @@ class Pickle {
int size() const { return static_cast<int>(header_size_ +
header_->payload_size); }
// Return the full size of the memory allocated for this Pickle's data.
uint32_t capacity() const {
return capacity_;
}
// Returns the data for this Pickle.
const void* data() const { return header_; }
@@ -234,10 +246,6 @@ class Pickle {
return header_ ? payload() + payload_size() : nullptr;
}
uint32_t capacity() const {
return capacity_;
}
// Resizes the buffer for use when writing the specified amount of data. The
// location that the data should be written at is returned, or NULL if there
// was an error. Call EndWrite with the returned offset and the given length
@@ -282,6 +290,12 @@ class Pickle {
const char* range_start,
const char* range_end);
// If the given range contains at least header_size bytes, return the length
// of the pickled data including the header.
static uint32_t GetLength(uint32_t header_size,
const char* range_start,
const char* range_end);
// The allocation granularity of the payload.
static const int kPayloadUnit;
+8 -2
View File
@@ -6,12 +6,12 @@
#ifndef BASE_TASK_H_
#define BASE_TASK_H_
#include "base/non_thread_safe.h"
#include "base/revocable_store.h"
#include "base/tracked.h"
#include "base/tuple.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Tuple.h"
#include "nsISupportsImpl.h"
// Helper functions so that we can call a function a pass it arguments that come
// from a Tuple.
@@ -128,7 +128,7 @@ class ScopedTaskFactory : public RevocableStore {
return new TaskWrapper(this);
}
class TaskWrapper : public TaskType, public NonThreadSafe {
class TaskWrapper : public TaskType {
public:
explicit TaskWrapper(RevocableStore* store) : revocable_(store) { }
@@ -137,9 +137,15 @@ class ScopedTaskFactory : public RevocableStore {
TaskType::Run();
}
~TaskWrapper() {
NS_ASSERT_OWNINGTHREAD(TaskWrapper);
}
private:
Revocable revocable_;
NS_DECL_OWNINGTHREAD
DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper);
};
@@ -154,13 +154,13 @@ ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
}
void ChildProcessHost::ListenerHook::OnMessageReceived(
const IPC::Message& msg) {
IPC::Message&& msg) {
bool msg_is_ok = true;
bool handled = false;
if (!handled) {
host_->OnMessageReceived(msg);
host_->OnMessageReceived(mozilla::Move(msg));
}
if (!msg_is_ok)
@@ -75,7 +75,7 @@ class ChildProcessHost :
void InstanceCreated();
// IPC::Channel::Listener implementation:
virtual void OnMessageReceived(const IPC::Message& msg) { }
virtual void OnMessageReceived(IPC::Message&& msg) { }
virtual void OnChannelConnected(int32_t peer_pid) { }
virtual void OnChannelError() { }
@@ -102,7 +102,7 @@ class ChildProcessHost :
class ListenerHook : public IPC::Channel::Listener {
public:
explicit ListenerHook(ChildProcessHost* host);
virtual void OnMessageReceived(const IPC::Message& msg);
virtual void OnMessageReceived(IPC::Message&& msg);
virtual void OnChannelConnected(int32_t peer_pid);
virtual void OnChannelError();
virtual void GetQueuedMessages(std::queue<IPC::Message>& queue);

Some files were not shown because too many files have changed in this diff Show More