Bug 918288 - Ensure we set a displayport on elements that we get touch-start events for. r=botond

This commit is contained in:
Kartikaya Gupta 2014-11-21 21:36:25 -05:00
parent 911702f6c3
commit 0611166ee2
3 changed files with 119 additions and 34 deletions

View file

@ -2395,24 +2395,27 @@ TabChild::CancelTapTracking()
mTapHoldTimer = nullptr;
}
static dom::Element*
GetScrollableAncestor(nsIFrame* aTarget)
static nsIScrollableFrame*
GetScrollableAncestorFrame(nsIFrame* aTarget)
{
if (!aTarget) {
return nullptr;
}
uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
| nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
nsIScrollableFrame* scrollTarget = nsLayoutUtils::GetNearestScrollableFrame(
aTarget, flags);
if (!scrollTarget) {
return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
}
static dom::Element*
GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
{
if (!aScrollableFrame) {
return nullptr;
}
nsIFrame* scrolledFrame = scrollTarget->GetScrolledFrame();
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
@ -2422,6 +2425,49 @@ GetScrollableAncestor(nsIFrame* aTarget)
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;
}
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;
};
void
TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
const ScrollableLayerGuid& aGuid,
@ -2437,6 +2483,7 @@ TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
return;
}
bool waitForRefresh = false;
nsTArray<ScrollableLayerGuid> targets;
for (size_t i = 0; i < aEvent.touches.Length(); i++) {
ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
@ -2444,13 +2491,25 @@ TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
WebWidget(), aEvent.touches[i]->mRefPoint, rootFrame);
nsIFrame* target = nsLayoutUtils::GetFrameForPoint(rootFrame, touchPoint,
nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
APZCCallbackHelper::GetOrCreateScrollIdentifiers(
GetScrollableAncestor(target),
&(guid.mPresShellId),
&(guid.mScrollId));
nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
dpElement, &(guid.mPresShellId), &(guid.mScrollId));
targets.AppendElement(guid);
if (guidIsValid && !nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
waitForRefresh |= nsLayoutUtils::CalculateAndSetDisplayPortMargins(
scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
}
}
if (waitForRefresh) {
waitForRefresh = shell->AddPostRefreshObserver(
new DisplayportSetListener(this, shell, aInputBlockId, targets));
}
if (!waitForRefresh) {
SendSetTargetAPZC(aInputBlockId, targets);
}
}
bool

View file

@ -988,7 +988,7 @@ nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult)
return GetDisplayPortImpl(aContent, aResult, 1.0f);
}
void
bool
nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
nsIPresShell* aPresShell,
const ScreenMargin& aMargins,
@ -998,7 +998,7 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
DisplayPortMarginsPropertyData* currentData =
static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
if (currentData && currentData->mPriority > aPriority) {
return;
return false;
}
aContent->SetProperty(nsGkAtoms::DisplayPortMargins,
@ -1022,6 +1022,8 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
rootFrame->SchedulePaint();
}
}
return true;
}
void
@ -2737,20 +2739,22 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
return NS_OK;
}
// aScrollFrame and aScrollFrameAsScrollable must be non-nullptr
// aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrame
static FrameMetrics
CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
nsIScrollableFrame* aScrollFrameAsScrollable) {
CalculateFrameMetricsForDisplayPort(nsIScrollableFrame* aScrollFrame) {
nsIFrame* frame = do_QueryFrame(aScrollFrame);
MOZ_ASSERT(frame);
// Calculate the metrics necessary for calculating the displayport.
// This code has a lot in common with the code in ComputeFrameMetrics();
// we may want to refactor this at some point.
FrameMetrics metrics;
nsPresContext* presContext = aScrollFrame->PresContext();
nsPresContext* presContext = frame->PresContext();
nsIPresShell* presShell = presContext->PresShell();
CSSToLayoutDeviceScale deviceScale(float(nsPresContext::AppUnitsPerCSSPixel())
/ presContext->AppUnitsPerDevPixel());
float resolution = 1.0f;
if (aScrollFrame == presShell->GetRootScrollFrame()) {
if (frame == presShell->GetRootScrollFrame()) {
// Only the root scrollable frame for a given presShell should pick up
// the presShell's resolution. All the other frames are 1.0.
resolution = presShell->GetXResolution();
@ -2763,7 +2767,7 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
// and leaving mExtraResolution at 1.
LayoutDeviceToLayerScale cumulativeResolution(
presShell->GetCumulativeResolution().width
* nsLayoutUtils::GetTransformToAncestorScale(aScrollFrame).width);
* nsLayoutUtils::GetTransformToAncestorScale(frame).width);
LayerToParentLayerScale layerToParentLayerScale(1.0f);
metrics.mDevPixelsPerCSSPixel = deviceScale;
@ -2773,9 +2777,9 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
// Only the size of the composition bounds is relevant to the
// displayport calculation, not its origin.
nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollFrame);
nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(frame);
LayoutDeviceToParentLayerScale compBoundsScale(1.0f);
if (aScrollFrame == presShell->GetRootScrollFrame() && presContext->IsRootContentDocument()) {
if (frame == presShell->GetRootScrollFrame() && presContext->IsRootContentDocument()) {
if (presContext->GetParentPresContext()) {
gfxSize res = presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();
compBoundsScale = LayoutDeviceToParentLayerScale(res.width, res.height);
@ -2789,17 +2793,33 @@ CalculateFrameMetricsForDisplayPort(nsIFrame* aScrollFrame,
* compBoundsScale;
metrics.SetRootCompositionSize(
nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame, false, metrics));
nsLayoutUtils::CalculateRootCompositionSize(frame, false, metrics));
metrics.SetScrollOffset(CSSPoint::FromAppUnits(
aScrollFrameAsScrollable->GetScrollPosition()));
aScrollFrame->GetScrollPosition()));
metrics.mScrollableRect = CSSRect::FromAppUnits(
nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrameAsScrollable, nullptr));
nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrame, nullptr));
return metrics;
}
bool
nsLayoutUtils::CalculateAndSetDisplayPortMargins(nsIScrollableFrame* aScrollFrame,
RepaintMode aRepaintMode) {
nsIFrame* frame = do_QueryFrame(aScrollFrame);
MOZ_ASSERT(frame);
nsIContent* content = frame->GetContent();
MOZ_ASSERT(content);
FrameMetrics metrics = CalculateFrameMetricsForDisplayPort(aScrollFrame);
ScreenMargin displayportMargins = APZCTreeManager::CalculatePendingDisplayPort(
metrics, ParentLayerPoint(0.0f, 0.0f), 0.0);
nsIPresShell* presShell = frame->PresContext()->GetPresShell();
return nsLayoutUtils::SetDisplayPortMargins(
content, presShell, displayportMargins, 0, aRepaintMode);
}
bool
nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
nsIFrame* aScrollFrame,
@ -2830,13 +2850,7 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
// If we don't already have a displayport, calculate and set one.
if (!haveDisplayPort) {
FrameMetrics metrics = CalculateFrameMetricsForDisplayPort(aScrollFrame, scrollableFrame);
ScreenMargin displayportMargins = APZCTreeManager::CalculatePendingDisplayPort(
metrics, ParentLayerPoint(0.0f, 0.0f), 0.0);
nsIPresShell* presShell = aScrollFrame->PresContext()->GetPresShell();
nsLayoutUtils::SetDisplayPortMargins(
content, presShell, displayportMargins,
0, nsLayoutUtils::RepaintMode::DoNotRepaint);
CalculateAndSetDisplayPortMargins(scrollableFrame, nsLayoutUtils::RepaintMode::DoNotRepaint);
haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it");
}

View file

@ -181,8 +181,9 @@ public:
* @param aPriority a priority value to determine which margins take effect
* when multiple callers specify margins
* @param aRepaintMode whether to schedule a paint after setting the margins
* @return true if the new margins were applied.
*/
static void SetDisplayPortMargins(nsIContent* aContent,
static bool SetDisplayPortMargins(nsIContent* aContent,
nsIPresShell* aPresShell,
const ScreenMargin& aMargins,
uint32_t aPriority = 0,
@ -2429,6 +2430,17 @@ public:
}
}
/**
* Calculate a default set of displayport margins for the given scrollframe
* and set them on the scrollframe's content element. The margins are set with
* the default priority, which may clobber previously set margins. The repaint
* mode provided is passed through to the call to SetDisplayPortMargins.
* The |aScrollFrame| parameter must be non-null and queryable to an nsIFrame.
* @return true iff the call to SetDisplayPortMargins returned true.
*/
static bool CalculateAndSetDisplayPortMargins(nsIScrollableFrame* aScrollFrame,
RepaintMode aRepaintMode);
/**
* Get the display port for |aScrollFrame|'s content. If |aScrollFrame|
* WantsAsyncScroll() and we don't have a scrollable displayport yet (as