Bug 1127066 - Extract SendSetTargetAPZCNotification and its helpers into APZCCallbackHelper. r=kats

This commit is contained in:
Botond Ballo 2015-02-06 18:11:19 -05:00
parent 79d664e733
commit ccad381bc1
4 changed files with 218 additions and 184 deletions

View File

@ -849,6 +849,22 @@ TabChild::Create(nsIContentChild* aManager,
return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
}
class TabChildSetTargetAPZCCallback : public SetTargetAPZCCallback {
public:
explicit TabChildSetTargetAPZCCallback(TabChild* aTabChild)
: mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
{}
void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const MOZ_OVERRIDE {
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
static_cast<TabChild*>(tabChild.get())->SendSetTargetAPZC(aInputBlockId, aTargets);
}
}
private:
nsWeakPtr mTabChild;
};
TabChild::TabChild(nsIContentChild* aManager,
const TabId& aTabId,
@ -874,6 +890,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mEndTouchIsClick(false)
, mIgnoreKeyPressEvent(false)
, mActiveElementManager(new ActiveElementManager())
, mSetTargetAPZCCallback(new TabChildSetTargetAPZCCallback(this))
, mHasValidInnerSize(false)
, mDestroyed(false)
, mUniqueId(aTabId)
@ -2322,15 +2339,8 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
{
if (IsAsyncPanZoomEnabled()) {
nsCOMPtr<nsIDocument> document(GetDocument());
if (nsIPresShell* shell = document->GetShell()) {
if (nsIFrame* rootFrame = shell->GetRootFrame()) {
nsTArray<ScrollableLayerGuid> targets;
bool waitForRefresh =
PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId, rootFrame, aEvent.refPoint, &targets);
SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
}
}
APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document, aEvent, aGuid,
aInputBlockId, mSetTargetAPZCCallback);
}
WidgetWheelEvent event(aEvent);
@ -2485,160 +2495,6 @@ TabChild::CancelTapTracking()
mTapHoldTimer = nullptr;
}
static nsIScrollableFrame*
GetScrollableAncestorFrame(nsIFrame* aTarget)
{
if (!aTarget) {
return nullptr;
}
uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
| nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
}
static dom::Element*
GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
{
if (!aScrollableFrame) {
return nullptr;
}
nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
if (!scrolledFrame) {
return nullptr;
}
// |scrolledFrame| should at this point be the root content frame of the
// nearest ancestor scrollable frame. The element corresponding to this
// frame should be the one with the displayport set on it, so find that
// element and return it.
nsIContent* content = scrolledFrame->GetContent();
MOZ_ASSERT(content->IsElement()); // roc says this must be true
return content->AsElement();
}
class DisplayportSetListener : public nsAPostRefreshObserver {
public:
DisplayportSetListener(TabChild* aTabChild,
nsIPresShell* aPresShell,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets)
: mTabChild(aTabChild)
, mPresShell(aPresShell)
, mInputBlockId(aInputBlockId)
, mTargets(aTargets)
{
}
virtual ~DisplayportSetListener()
{
}
void DidRefresh() MOZ_OVERRIDE {
if (!mTabChild) {
MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
return;
}
TABC_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
mTabChild->SendSetTargetAPZC(mInputBlockId, mTargets);
if (!mPresShell->RemovePostRefreshObserver(this)) {
MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
// Graceful handling, just in case...
mTabChild = nullptr;
mPresShell = nullptr;
return;
}
delete this;
}
private:
nsRefPtr<TabChild> mTabChild;
nsRefPtr<nsIPresShell> mPresShell;
uint64_t mInputBlockId;
nsTArray<ScrollableLayerGuid> mTargets;
};
bool
TabChild::PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
nsIFrame* aRootFrame,
const LayoutDeviceIntPoint& aRefPoint,
nsTArray<ScrollableLayerGuid>* aTargets)
{
ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
nsPoint point =
nsLayoutUtils::GetEventCoordinatesRelativeTo(WebWidget(), aRefPoint, aRootFrame);
nsIFrame* target =
nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
nsAutoString dpElementDesc;
if (dpElement) {
dpElement->Describe(dpElementDesc);
}
TABC_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
aInputBlockId, dpElement.get(),
NS_LossyConvertUTF16toASCII(dpElementDesc).get());
bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
dpElement, &(guid.mPresShellId), &(guid.mScrollId));
aTargets->AppendElement(guid);
if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
return false;
}
TABC_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
}
void
TabChild::SendSetTargetAPZCNotification(nsIPresShell* aShell,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets,
bool aWaitForRefresh)
{
bool waitForRefresh = aWaitForRefresh;
if (waitForRefresh) {
TABC_LOG("At least one target got a new displayport, need to wait for refresh\n");
waitForRefresh = aShell->AddPostRefreshObserver(
new DisplayportSetListener(this, aShell, aInputBlockId, aTargets));
}
if (!waitForRefresh) {
TABC_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
SendSetTargetAPZC(aInputBlockId, aTargets);
} else {
TABC_LOG("Successfully registered post-refresh observer\n");
}
}
void
TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId)
{
nsCOMPtr<nsIDocument> document(GetDocument());
nsIPresShell* shell = document->GetShell();
if (!shell) {
return;
}
nsIFrame* rootFrame = shell->GetRootFrame();
if (!rootFrame) {
return;
}
bool waitForRefresh = false;
nsTArray<ScrollableLayerGuid> targets;
for (size_t i = 0; i < aEvent.touches.Length(); i++) {
waitForRefresh |= PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId,
rootFrame, aEvent.touches[i]->mRefPoint, &targets);
}
SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
}
float
TabChild::GetPresShellResolution() const
{
@ -2664,7 +2520,9 @@ TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
mWidget->GetDefaultScale(), GetPresShellResolution());
if (localEvent.message == NS_TOUCH_START && IsAsyncPanZoomEnabled()) {
SendSetTargetAPZCNotification(localEvent, aGuid, aInputBlockId);
nsCOMPtr<nsIDocument> document = GetDocument();
APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document,
localEvent, aGuid, aInputBlockId, mSetTargetAPZCCallback);
}
// Dispatch event to content (potentially a long-running operation)

View File

@ -46,6 +46,7 @@ class RenderFrameChild;
namespace layers {
class ActiveElementManager;
struct SetTargetAPZCCallback;
}
namespace widget {
@ -252,6 +253,7 @@ class TabChild MOZ_FINAL : public TabChildBase,
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
typedef mozilla::layers::ActiveElementManager ActiveElementManager;
typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
public:
/**
@ -597,26 +599,6 @@ private:
void SendPendingTouchPreventedResponse(bool aPreventDefault,
const ScrollableLayerGuid& aGuid);
// Adds the scrollable layer target to the target list, and returns whether
// or not the caller should wait for a refresh to send a target
// notification.
bool PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId,
nsIFrame* aRootFrame,
const LayoutDeviceIntPoint& aRefPoint,
nsTArray<ScrollableLayerGuid>* aTargets);
// Sends a SetTarget notification for APZC, given one or more previous
// calls to PrepareForAPZCSetTargetNotification().
void SendSetTargetAPZCNotification(nsIPresShell* aShell,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets,
bool aWaitForRefresh);
void SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
const mozilla::layers::ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
// Get the pres shell resolution of the document in this tab.
float GetPresShellResolution() const;
@ -664,6 +646,7 @@ private:
bool mIgnoreKeyPressEvent;
nsRefPtr<ActiveElementManager> mActiveElementManager;
nsRefPtr<SetTargetAPZCCallback> mSetTargetAPZCCallback;
bool mHasValidInnerSize;
bool mDestroyed;
// Position of tab, relative to parent widget (typically the window)

View File

@ -434,5 +434,174 @@ APZCCallbackHelper::FireSingleTapEvent(const LayoutDevicePoint& aPoint,
DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aWidget);
}
static nsIScrollableFrame*
GetScrollableAncestorFrame(nsIFrame* aTarget)
{
if (!aTarget) {
return nullptr;
}
uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
| nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
}
static dom::Element*
GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
{
if (!aScrollableFrame) {
return nullptr;
}
nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
if (!scrolledFrame) {
return nullptr;
}
// |scrolledFrame| should at this point be the root content frame of the
// nearest ancestor scrollable frame. The element corresponding to this
// frame should be the one with the displayport set on it, so find that
// element and return it.
nsIContent* content = scrolledFrame->GetContent();
MOZ_ASSERT(content->IsElement()); // roc says this must be true
return content->AsElement();
}
// Determine the scrollable target frame for the given point and add it to
// the target list. If the frame doesn't have a displayport, set one.
// Return whether or not a displayport was set.
static bool
PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
const ScrollableLayerGuid& aGuid,
nsIFrame* aRootFrame,
const LayoutDeviceIntPoint& aRefPoint,
nsTArray<ScrollableLayerGuid>* aTargets)
{
ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
nsPoint point =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
nsIFrame* target =
nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
nsAutoString dpElementDesc;
if (dpElement) {
dpElement->Describe(dpElementDesc);
}
APZCCH_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
aInputBlockId, dpElement.get(),
NS_LossyConvertUTF16toASCII(dpElementDesc).get());
bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
dpElement, &(guid.mPresShellId), &(guid.mScrollId));
aTargets->AppendElement(guid);
if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
return false;
}
APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
}
class DisplayportSetListener : public nsAPostRefreshObserver {
public:
DisplayportSetListener(const nsRefPtr<SetTargetAPZCCallback>& aCallback,
nsIPresShell* aPresShell,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets)
: mCallback(aCallback)
, mPresShell(aPresShell)
, mInputBlockId(aInputBlockId)
, mTargets(aTargets)
{
}
virtual ~DisplayportSetListener()
{
}
void DidRefresh() MOZ_OVERRIDE {
if (!mCallback) {
MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
return;
}
APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
mCallback->Run(mInputBlockId, mTargets);
if (!mPresShell->RemovePostRefreshObserver(this)) {
MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
// Graceful handling, just in case...
mCallback = nullptr;
mPresShell = nullptr;
return;
}
delete this;
}
private:
nsRefPtr<SetTargetAPZCCallback> mCallback;
nsRefPtr<nsIPresShell> mPresShell;
uint64_t mInputBlockId;
nsTArray<ScrollableLayerGuid> mTargets;
};
// Sends a SetTarget notification for APZC, given one or more previous
// calls to PrepareForAPZCSetTargetNotification().
static void
SendSetTargetAPZCNotificationHelper(nsIPresShell* aShell,
const uint64_t& aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets,
bool aWaitForRefresh,
const nsRefPtr<SetTargetAPZCCallback>& aCallback)
{
bool waitForRefresh = aWaitForRefresh;
if (waitForRefresh) {
APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n");
waitForRefresh = aShell->AddPostRefreshObserver(
new DisplayportSetListener(aCallback, aShell, aInputBlockId, aTargets));
}
if (!waitForRefresh) {
APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
aCallback->Run(aInputBlockId, aTargets);
} else {
APZCCH_LOG("Successfully registered post-refresh observer\n");
}
}
void
APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
nsIDocument* aDocument,
const WidgetGUIEvent& aEvent,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
const nsRefPtr<SetTargetAPZCCallback>& aCallback)
{
if (nsIPresShell* shell = aDocument->GetShell()) {
if (nsIFrame* rootFrame = shell->GetRootFrame()) {
bool waitForRefresh = false;
nsTArray<ScrollableLayerGuid> targets;
if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
rootFrame, touchEvent->touches[i]->mRefPoint, &targets);
}
} else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
rootFrame, wheelEvent->refPoint, &targets);
}
// TODO: Do other types of events need to be handled?
if (!targets.IsEmpty()) {
SendSetTargetAPZCNotificationHelper(shell, aInputBlockId, targets,
waitForRefresh, aCallback);
}
}
}
}
}
}

View File

@ -18,6 +18,17 @@ template<class T> struct already_AddRefed;
namespace mozilla {
namespace layers {
/* A base class for callbacks to be passed to APZCCallbackHelper::SendSetTargetAPZCNotification.
* If we had something like std::function, we could just use
* std::function<void(uint64_t, const nsTArray<ScrollableLayerGuid>&)>. */
struct SetTargetAPZCCallback {
public:
NS_INLINE_DECL_REFCOUNTING(SetTargetAPZCCallback)
virtual void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const = 0;
protected:
virtual ~SetTargetAPZCCallback() {}
};
/* This class contains some helper methods that facilitate implementing the
GeckoContentController callback interface required by the AsyncPanZoomController.
Since different platforms need to implement this interface in similar-but-
@ -118,6 +129,19 @@ public:
* via the given widget. */
static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
nsIWidget* aWidget);
/* Perform hit-testing on the touch points of |aEvent| to determine
* which scrollable frames they target. If any of these frames don't have
* a displayport, set one. Finally, invoke the provided callback with
* the guids of the target frames. If any displayports needed to be set,
* the callback is invoked after the next refresh, otherwise it's invoked
* right away. */
static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
nsIDocument* aDocument,
const WidgetGUIEvent& aEvent,
const ScrollableLayerGuid& aGuid,
uint64_t aInputBlockId,
const nsRefPtr<SetTargetAPZCCallback>& aCallback);
};
}