Bug 1544218 - part 1: Make mozilla::layers::GetPresShell() in APZCCallbackHelper.cpp return mozilla::PresShell directly r=kats
And this patch makes it return raw pointer. The safeness of the pointer
should be managed by `MOZ_CAN_RUN_SCRIPT` since incrementing refcount in
hot path is bad for performance. So, it should be followed by bug 1543315.
Differential Revision: https://phabricator.services.mozilla.com/D27471

/* -*- 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"APZCCallbackHelper.h"#include"TouchActionHelper.h"#include"gfxPlatform.h" // For gfxPlatform::UseTiling#include"gfxPrefs.h"#include"LayersLogging.h" // For Stringify#include"mozilla/dom/Element.h"#include"mozilla/dom/MouseEventBinding.h"#include"mozilla/dom/TabParent.h"#include"mozilla/IntegerPrintfMacros.h"#include"mozilla/layers/LayerTransactionChild.h"#include"mozilla/layers/ShadowLayers.h"#include"mozilla/layers/WebRenderLayerManager.h"#include"mozilla/layers/WebRenderBridgeChild.h"#include"mozilla/PresShell.h"#include"mozilla/TouchEvents.h"#include"nsContainerFrame.h"#include"nsContentUtils.h"#include"nsIContent.h"#include"nsIDOMWindow.h"#include"nsIDOMWindowUtils.h"#include"mozilla/dom/Document.h"#include"nsIInterfaceRequestorUtils.h"#include"nsIScrollableFrame.h"#include"nsLayoutUtils.h"#include"nsPrintfCString.h"#include"nsRefreshDriver.h"#include"nsString.h"#include"nsView.h"#include"Layers.h"// #define APZCCH_LOGGING 1#ifdef APZCCH_LOGGING# define APZCCH_LOG(...) printf_stderr("APZCCH: " __VA_ARGS__)#else# define APZCCH_LOG(...)#endifnamespacemozilla{namespacelayers{usingdom::TabParent;uint64_tAPZCCallbackHelper::sLastTargetAPZCNotificationInputBlock=uint64_t(-1);ScreenMarginAPZCCallbackHelper::AdjustDisplayPortForScrollDelta(constRepaintRequest&aRequest,constCSSPoint&aActualScrollOffset){// Correct the display-port by the difference between the requested scroll// offset and the resulting scroll offset after setting the requested value.ScreenPointshift=(aRequest.GetScrollOffset()-aActualScrollOffset)*aRequest.DisplayportPixelsPerCSSPixel();ScreenMarginmargins=aRequest.GetDisplayPortMargins();margins.left-=shift.x;margins.right+=shift.x;margins.top-=shift.y;margins.bottom+=shift.y;returnmargins;}staticScreenMarginRecenterDisplayPort(constScreenMargin&aDisplayPort){ScreenMarginmargins=aDisplayPort;margins.right=margins.left=margins.LeftRight()/2;margins.top=margins.bottom=margins.TopBottom()/2;returnmargins;}staticPresShell*GetPresShell(constnsIContent*aContent){if(dom::Document*doc=aContent->GetComposedDoc()){returndoc->GetPresShell();}returnnullptr;}staticCSSPointScrollFrameTo(nsIScrollableFrame*aFrame,constRepaintRequest&aRequest,bool&aSuccessOut){aSuccessOut=false;CSSPointtargetScrollPosition=aRequest.IsRootContent()?aRequest.GetLayoutViewport().TopLeft():aRequest.GetScrollOffset();if(!aFrame){returntargetScrollPosition;}CSSPointgeckoScrollPosition=CSSPoint::FromAppUnits(aFrame->GetScrollPosition());// If the repaint request was triggered due to a previous main-thread scroll// offset update sent to the APZ, then we don't need to do another scroll here// and we can just return.if(!aRequest.GetScrollOffsetUpdated()){returngeckoScrollPosition;}// If this frame is overflow:hidden, then the expectation is that it was// sized in a way that respects its scrollable boundaries. For the root// frame, this means that it cannot be scrolled in such a way that it moves// the layout viewport. For a non-root frame, this means that it cannot be// scrolled at all.//// In either case, |targetScrollPosition| should be the same as// |geckoScrollPosition| here.//// However, this is slightly racy. We query the overflow property of the// scroll frame at the time the repaint request arrives at the main thread// (i.e., right now), but APZ made the decision of whether or not to allow// scrolling based on the information it had at the time it processed the// scroll event. The overflow property could have changed at some time// between the two events and so APZ may have computed a scrollable region// that is larger than what is actually allowed.//// Currently, we allow the scroll position to change even though the frame is// overflow:hidden (that is, we take |targetScrollPosition|). If this turns// out to be problematic, an alternative solution would be to ignore the// scroll position change (that is, use |geckoScrollPosition|).if(aFrame->GetScrollStyles().mVertical==StyleOverflow::Hidden&&targetScrollPosition.y!=geckoScrollPosition.y){NS_WARNING(nsPrintfCString("APZCCH: targetScrollPosition.y (%f) != geckoScrollPosition.y (%f)",targetScrollPosition.y,geckoScrollPosition.y).get());}if(aFrame->GetScrollStyles().mHorizontal==StyleOverflow::Hidden&&targetScrollPosition.x!=geckoScrollPosition.x){NS_WARNING(nsPrintfCString("APZCCH: targetScrollPosition.x (%f) != geckoScrollPosition.x (%f)",targetScrollPosition.x,geckoScrollPosition.x).get());}// If the scrollable frame is currently in the middle of an async or smooth// scroll then we don't want to interrupt it (see bug 961280).// Also if the scrollable frame got a scroll request from a higher priority// origin since the last layers update, then we don't want to push our scroll// request because we'll clobber that one, which is bad.boolscrollInProgress=APZCCallbackHelper::IsScrollInProgress(aFrame);if(!scrollInProgress){aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition,nsGkAtoms::apz);geckoScrollPosition=CSSPoint::FromAppUnits(aFrame->GetScrollPosition());aSuccessOut=true;}// Return the final scroll position after setting it so that anything that// relies on it can have an accurate value. Note that even if we set it above// re-querying it is a good idea because it may have gotten clamped or// rounded.returngeckoScrollPosition;}/** * Scroll the scroll frame associated with |aContent| to the scroll position * requested in |aRequest|. * * Any difference between the requested and actual scroll positions is used to * update the callback-transform stored on the content, and return a new * display port. */staticScreenMarginScrollFrame(nsIContent*aContent,constRepaintRequest&aRequest){// Scroll the window to the desired spotnsIScrollableFrame*sf=nsLayoutUtils::FindScrollableFrameFor(aRequest.GetScrollId());if(sf){sf->ResetScrollInfoIfGeneration(aRequest.GetScrollGeneration());sf->SetScrollableByAPZ(!aRequest.IsScrollInfoLayer());if(sf->IsRootScrollFrameOfDocument()){if(!APZCCallbackHelper::IsScrollInProgress(sf)){if(RefPtr<PresShell>presShell=GetPresShell(aContent)){if(presShell->SetVisualViewportOffset(CSSPoint::ToAppUnits(aRequest.GetScrollOffset()),presShell->GetLayoutViewportOffset())){sf->MarkEverScrolled();}}}}}boolscrollUpdated=false;ScreenMargindisplayPortMargins=aRequest.GetDisplayPortMargins();CSSPointapzScrollOffset=aRequest.GetScrollOffset();CSSPointactualScrollOffset=ScrollFrameTo(sf,aRequest,scrollUpdated);if(scrollUpdated){if(aRequest.IsScrollInfoLayer()){// In cases where the APZ scroll offset is different from the content// scroll offset, we want to interpret the margins as relative to the APZ// scroll offset except when the frame is not scrollable by APZ.// Therefore, if the layer is a scroll info layer, we leave the margins// as-is and they will be interpreted as relative to the content scroll// offset.if(nsIFrame*frame=aContent->GetPrimaryFrame()){frame->SchedulePaint();}}else{// Correct the display port due to the difference between mScrollOffset// and the actual scroll offset.displayPortMargins=APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aRequest,actualScrollOffset);}}elseif(aRequest.IsRootContent()&&aRequest.GetScrollOffset()!=aRequest.GetLayoutViewport().TopLeft()){// APZ uses the visual viewport's offset to calculate where to place the// display port, so the display port is misplaced when a pinch zoom occurs.//// We need to force a display port adjustment in the following paint to// account for a difference between mScrollOffset and the actual scroll// offset in repaints requested by// AsyncPanZoomController::NotifyLayersUpdated.displayPortMargins=APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aRequest,actualScrollOffset);}else{// For whatever reason we couldn't update the scroll offset on the scroll// frame, which means the data APZ used for its displayport calculation is// stale. Fall back to a sane default behaviour. Note that we don't// tile-align the recentered displayport because tile-alignment depends on// the scroll position, and the scroll position here is out of our control.// See bug 966507 comment 21 for a more detailed explanation.displayPortMargins=RecenterDisplayPort(aRequest.GetDisplayPortMargins());}// APZ transforms inputs assuming we applied the exact scroll offset it// requested (|apzScrollOffset|). Since we may not have, record the difference// between what APZ asked for and what we actually applied, and apply it to// input events to compensate.// Note that if the main-thread had a change in its scroll position, we don't// want to record that difference here, because it can be large and throw off// input events by a large amount. It is also going to be transient, because// any main-thread scroll position change will be synced to APZ and we will// get another repaint request when APZ confirms. In the interval while this// is happening we can just leave the callback transform as it was.boolmainThreadScrollChanged=sf&&sf->CurrentScrollGeneration()!=aRequest.GetScrollGeneration()&&nsLayoutUtils::CanScrollOriginClobberApz(sf->LastScrollOrigin());if(aContent&&!mainThreadScrollChanged){CSSPointscrollDelta=apzScrollOffset-actualScrollOffset;aContent->SetProperty(nsGkAtoms::apzCallbackTransform,newCSSPoint(scrollDelta),nsINode::DeleteProperty<CSSPoint>);}returndisplayPortMargins;}staticvoidSetDisplayPortMargins(nsIPresShell*aPresShell,nsIContent*aContent,ScreenMarginaDisplayPortMargins,CSSSizeaDisplayPortBase){if(!aContent){return;}boolhadDisplayPort=nsLayoutUtils::HasDisplayPort(aContent);nsLayoutUtils::SetDisplayPortMargins(aContent,aPresShell,aDisplayPortMargins,0);if(!hadDisplayPort){nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(aContent->GetPrimaryFrame(),nsLayoutUtils::RepaintMode::Repaint);}nsRectbase(0,0,aDisplayPortBase.width*AppUnitsPerCSSPixel(),aDisplayPortBase.height*AppUnitsPerCSSPixel());nsLayoutUtils::SetDisplayPortBaseIfNotSet(aContent,base);}staticvoidSetPaintRequestTime(nsIContent*aContent,constTimeStamp&aPaintRequestTime){aContent->SetProperty(nsGkAtoms::paintRequestTime,newTimeStamp(aPaintRequestTime),nsINode::DeleteProperty<TimeStamp>);}voidAPZCCallbackHelper::NotifyLayerTransforms(constnsTArray<MatrixMessage>&aTransforms){MOZ_ASSERT(NS_IsMainThread());for(constMatrixMessage&msg:aTransforms){TabParent*parent=TabParent::GetTabParentFromLayersId(msg.GetLayersId());if(parent){parent->SetChildToParentConversionMatrix(ViewAs<LayoutDeviceToLayoutDeviceMatrix4x4>(msg.GetMatrix(),PixelCastJustification::ContentProcessIsLayerInUiProcess));}}}voidAPZCCallbackHelper::UpdateRootFrame(constRepaintRequest&aRequest){if(aRequest.GetScrollId()==ScrollableLayerGuid::NULL_SCROLL_ID){return;}nsIContent*content=nsLayoutUtils::FindContentFor(aRequest.GetScrollId());if(!content){return;}RefPtr<PresShell>presShell=GetPresShell(content);if(!presShell||aRequest.GetPresShellId()!=presShell->GetPresShellId()){return;}if(nsLayoutUtils::AllowZoomingForDocument(presShell->GetDocument())&&aRequest.GetScrollOffsetUpdated()){// If zooming is disabled then we don't really want to let APZ fiddle// with these things. In theory setting the resolution here should be a// no-op, but setting the visual viewport size is bad because it can cause a// stale value to be returned by window.innerWidth/innerHeight (see bug// 1187792).//// We also skip this codepath unless the metrics has a scroll offset update// type other eNone, because eNone just means that this repaint request// was triggered by APZ in response to a main-thread update. In this// scenario we don't want to update the main-thread resolution because// it can trigger unnecessary reflows.floatpresShellResolution=presShell->GetResolution();// If the pres shell resolution has changed on the content side side// the time this repaint request was fired, consider this request out of// date and drop it; setting a zoom based on the out-of-date resolution can// have the effect of getting us stuck with the stale resolution.if(!FuzzyEqualsMultiplicative(presShellResolution,aRequest.GetPresShellResolution())){return;}// The pres shell resolution is updated by the the async zoom since the// last paint.presShellResolution=aRequest.GetPresShellResolution()*aRequest.GetAsyncZoom().scale;presShell->SetResolutionAndScaleTo(presShellResolution,nsIPresShell::ChangeOrigin::eApz);}// Do this as late as possible since scrolling can flush layout. It also// adjusts the display port margins, so do it before we set those.ScreenMargindisplayPortMargins=ScrollFrame(content,aRequest);SetDisplayPortMargins(presShell,content,displayPortMargins,aRequest.CalculateCompositedSizeInCssPixels());SetPaintRequestTime(content,aRequest.GetPaintRequestTime());}voidAPZCCallbackHelper::UpdateSubFrame(constRepaintRequest&aRequest){if(aRequest.GetScrollId()==ScrollableLayerGuid::NULL_SCROLL_ID){return;}nsIContent*content=nsLayoutUtils::FindContentFor(aRequest.GetScrollId());if(!content){return;}// We don't currently support zooming for subframes, so nothing extra// needs to be done beyond the tasks common to this and UpdateRootFrame.ScreenMargindisplayPortMargins=ScrollFrame(content,aRequest);if(RefPtr<PresShell>presShell=GetPresShell(content)){SetDisplayPortMargins(presShell,content,displayPortMargins,aRequest.CalculateCompositedSizeInCssPixels());}SetPaintRequestTime(content,aRequest.GetPaintRequestTime());}boolAPZCCallbackHelper::GetOrCreateScrollIdentifiers(nsIContent*aContent,uint32_t*aPresShellIdOut,ScrollableLayerGuid::ViewID*aViewIdOut){if(!aContent){returnfalse;}*aViewIdOut=nsLayoutUtils::FindOrCreateIDFor(aContent);if(PresShell*presShell=GetPresShell(aContent)){*aPresShellIdOut=presShell->GetPresShellId();returntrue;}returnfalse;}voidAPZCCallbackHelper::InitializeRootDisplayport(nsIPresShell*aPresShell){// Create a view-id and set a zero-margin displayport for the root element// of the root document in the chrome process. This ensures that the scroll// frame for this element gets an APZC, which in turn ensures that all content// in the chrome processes is covered by an APZC.// The displayport is zero-margin because this element is generally not// actually scrollable (if it is, APZC will set proper margins when it's// scrolled).if(!aPresShell){return;}MOZ_ASSERT(aPresShell->GetDocument());nsIContent*content=aPresShell->GetDocument()->GetDocumentElement();if(!content){return;}uint32_tpresShellId;ScrollableLayerGuid::ViewIDviewId;if(APZCCallbackHelper::GetOrCreateScrollIdentifiers(content,&presShellId,&viewId)){nsPresContext*pc=aPresShell->GetPresContext();// This code is only correct for root content or toplevel documents.MOZ_ASSERT(!pc||pc->IsRootContentDocument()||!pc->GetParentPresContext());nsIFrame*frame=aPresShell->GetRootScrollFrame();if(!frame){frame=aPresShell->GetRootFrame();}nsRectbaseRect;if(frame){baseRect=nsRect(nsPoint(0,0),nsLayoutUtils::CalculateCompositionSizeForFrame(frame));}elseif(pc){baseRect=nsRect(nsPoint(0,0),pc->GetVisibleArea().Size());}nsLayoutUtils::SetDisplayPortBaseIfNotSet(content,baseRect);// Note that we also set the base rect that goes with these margins in// nsRootBoxFrame::BuildDisplayList.nsLayoutUtils::SetDisplayPortMargins(content,aPresShell,ScreenMargin(),0,nsLayoutUtils::RepaintMode::DoNotRepaint);nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(content->GetPrimaryFrame(),nsLayoutUtils::RepaintMode::DoNotRepaint);}}nsPresContext*APZCCallbackHelper::GetPresContextForContent(nsIContent*aContent){dom::Document*doc=aContent->GetComposedDoc();if(!doc){returnnullptr;}PresShell*presShell=doc->GetPresShell();if(!presShell){returnnullptr;}returnpresShell->GetPresContext();}nsIPresShell*APZCCallbackHelper::GetRootContentDocumentPresShellForContent(nsIContent*aContent){nsPresContext*context=GetPresContextForContent(aContent);if(!context){returnnullptr;}context=context->GetToplevelContentDocumentPresContext();if(!context){returnnullptr;}returncontext->PresShell();}staticnsIPresShell*GetRootDocumentPresShell(nsIContent*aContent){dom::Document*doc=aContent->GetComposedDoc();if(!doc){returnnullptr;}PresShell*presShell=doc->GetPresShell();if(!presShell){returnnullptr;}nsPresContext*context=presShell->GetPresContext();if(!context){returnnullptr;}context=context->GetRootPresContext();if(!context){returnnullptr;}returncontext->PresShell();}CSSPointAPZCCallbackHelper::ApplyCallbackTransform(constCSSPoint&aInput,constScrollableLayerGuid&aGuid){CSSPointinput=aInput;if(aGuid.mScrollId==ScrollableLayerGuid::NULL_SCROLL_ID){returninput;}nsCOMPtr<nsIContent>content=nsLayoutUtils::FindContentFor(aGuid.mScrollId);if(!content){returninput;}// First, scale inversely by the root content document's pres shell// resolution to cancel the scale-to-resolution transform that the// compositor adds to the layer with the pres shell resolution. The points// sent to Gecko by APZ don't have this transform unapplied (unlike other// compositor-side transforms) because APZ doesn't know about it.if(nsIPresShell*shell=GetRootDocumentPresShell(content)){input=input/shell->GetResolution();}// This represents any resolution on the Root Content Document (RCD)// that's not on the Root Document (RD). That is, on platforms where// RCD == RD, it's 1, and on platforms where RCD != RD, it's the RCD// resolution. 'input' has this resolution applied, but the scroll// delta retrieved below do not, so we need to apply them to the// delta before adding the delta to 'input'. (Technically, deltas// from scroll frames outside the RCD would already have this// resolution applied, but we don't have such scroll frames in// practice.)floatnonRootResolution=1.0f;if(nsIPresShell*shell=GetRootContentDocumentPresShellForContent(content)){nonRootResolution=shell->GetCumulativeNonRootScaleResolution();}// Now apply the callback-transform. This is only approximately correct,// see the comment on GetCumulativeApzCallbackTransform for details.CSSPointtransform=nsLayoutUtils::GetCumulativeApzCallbackTransform(content->GetPrimaryFrame());returninput+transform*nonRootResolution;}LayoutDeviceIntPointAPZCCallbackHelper::ApplyCallbackTransform(constLayoutDeviceIntPoint&aPoint,constScrollableLayerGuid&aGuid,constCSSToLayoutDeviceScale&aScale){LayoutDevicePointpoint=LayoutDevicePoint(aPoint.x,aPoint.y);point=ApplyCallbackTransform(point/aScale,aGuid)*aScale;returnLayoutDeviceIntPoint::Round(point);}voidAPZCCallbackHelper::ApplyCallbackTransform(WidgetEvent&aEvent,constScrollableLayerGuid&aGuid,constCSSToLayoutDeviceScale&aScale){if(aEvent.AsTouchEvent()){WidgetTouchEvent&event=*(aEvent.AsTouchEvent());for(size_ti=0;i<event.mTouches.Length();i++){event.mTouches[i]->mRefPoint=ApplyCallbackTransform(event.mTouches[i]->mRefPoint,aGuid,aScale);}}else{aEvent.mRefPoint=ApplyCallbackTransform(aEvent.mRefPoint,aGuid,aScale);}}nsEventStatusAPZCCallbackHelper::DispatchWidgetEvent(WidgetGUIEvent&aEvent){nsEventStatusstatus=nsEventStatus_eConsumeNoDefault;if(aEvent.mWidget){aEvent.mWidget->DispatchEvent(&aEvent,status);}returnstatus;}nsEventStatusAPZCCallbackHelper::DispatchSynthesizedMouseEvent(EventMessageaMsg,uint64_taTime,constLayoutDevicePoint&aRefPoint,ModifiersaModifiers,int32_taClickCount,nsIWidget*aWidget){MOZ_ASSERT(aMsg==eMouseMove||aMsg==eMouseDown||aMsg==eMouseUp||aMsg==eMouseLongTap);WidgetMouseEventevent(true,aMsg,aWidget,WidgetMouseEvent::eReal,WidgetMouseEvent::eNormal);event.mRefPoint=LayoutDeviceIntPoint::Truncate(aRefPoint.x,aRefPoint.y);event.mTime=aTime;event.button=WidgetMouseEvent::eLeftButton;event.inputSource=dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH;if(aMsg==eMouseLongTap){event.mFlags.mOnlyChromeDispatch=true;}event.mIgnoreRootScrollFrame=true;if(aMsg!=eMouseMove){event.mClickCount=aClickCount;}event.mModifiers=aModifiers;// Real touch events will generate corresponding pointer events. We set// convertToPointer to false to prevent the synthesized mouse events generate// pointer events again.event.convertToPointer=false;returnDispatchWidgetEvent(event);}boolAPZCCallbackHelper::DispatchMouseEvent(PresShell*aPresShell,constnsString&aType,constCSSPoint&aPoint,int32_taButton,int32_taClickCount,int32_taModifiers,boolaIgnoreRootScrollFrame,unsignedshortaInputSourceArg,uint32_taPointerId){NS_ENSURE_TRUE(aPresShell,true);booldefaultPrevented=false;nsContentUtils::SendMouseEvent(aPresShell,aType,aPoint.x,aPoint.y,aButton,nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED,aClickCount,aModifiers,aIgnoreRootScrollFrame,0,aInputSourceArg,aPointerId,false,&defaultPrevented,false,/* aIsWidgetEventSynthesized = */false);returndefaultPrevented;}voidAPZCCallbackHelper::FireSingleTapEvent(constLayoutDevicePoint&aPoint,ModifiersaModifiers,int32_taClickCount,nsIWidget*aWidget){if(aWidget->Destroyed()){return;}APZCCH_LOG("Dispatching single-tap component events to %s\n",Stringify(aPoint).c_str());inttime=0;DispatchSynthesizedMouseEvent(eMouseMove,time,aPoint,aModifiers,aClickCount,aWidget);DispatchSynthesizedMouseEvent(eMouseDown,time,aPoint,aModifiers,aClickCount,aWidget);DispatchSynthesizedMouseEvent(eMouseUp,time,aPoint,aModifiers,aClickCount,aWidget);}staticdom::Element*GetDisplayportElementFor(nsIScrollableFrame*aScrollableFrame){if(!aScrollableFrame){returnnullptr;}nsIFrame*scrolledFrame=aScrollableFrame->GetScrolledFrame();if(!scrolledFrame){returnnullptr;}// |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 truereturncontent->AsElement();}staticdom::Element*GetRootDocumentElementFor(nsIWidget*aWidget){// This returns the root element that ChromeProcessController sets the// displayport on during initialization.if(nsView*view=nsView::GetViewFor(aWidget)){if(nsIPresShell*shell=view->GetPresShell()){MOZ_ASSERT(shell->GetDocument());returnshell->GetDocument()->GetDocumentElement();}}returnnullptr;}staticnsIFrame*UpdateRootFrameForTouchTargetDocument(nsIFrame*aRootFrame){#if defined(MOZ_WIDGET_ANDROID)// Re-target so that the hit test is performed relative to the frame for the// Root Content Document instead of the Root Document which are different in// Android. See bug 1229752 comment 16 for an explanation of why this is// necessary.if(dom::Document*doc=aRootFrame->PresShell()->GetPrimaryContentDocument()){if(PresShell*presShell=doc->GetPresShell()){if(nsIFrame*frame=presShell->GetRootFrame()){returnframe;}}}#endifreturnaRootFrame;}namespace{usingFrameForPointOption=nsLayoutUtils::FrameForPointOption;// 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.staticboolPrepareForSetTargetAPZCNotification(nsIWidget*aWidget,constScrollableLayerGuid&aGuid,nsIFrame*aRootFrame,constLayoutDeviceIntPoint&aRefPoint,nsTArray<SLGuidAndRenderRoot>*aTargets){SLGuidAndRenderRootguid(aGuid.mLayersId,0,ScrollableLayerGuid::NULL_SCROLL_ID,wr::RenderRoot::Default);nsPointpoint=nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget,aRefPoint,aRootFrame);EnumSet<FrameForPointOption>options;if(nsLayoutUtils::AllowZoomingForDocument(aRootFrame->PresShell()->GetDocument())){// If zooming is enabled, we need IgnoreRootScrollFrame for correct// hit testing. Otherwise, don't use it because it interferes with// hit testing for some purposes such as scrollbar dragging (this will// need to be fixed before enabling zooming by default on desktop).options+=FrameForPointOption::IgnoreRootScrollFrame;}nsIFrame*target=nsLayoutUtils::GetFrameForPoint(aRootFrame,point,options);nsIScrollableFrame*scrollAncestor=target?nsLayoutUtils::GetAsyncScrollableAncestorFrame(target):aRootFrame->PresShell()->GetRootScrollFrameAsScrollable();// Assuming that if there's no scrollAncestor, there's already a displayPort.nsCOMPtr<dom::Element>dpElement=scrollAncestor?GetDisplayportElementFor(scrollAncestor):GetRootDocumentElementFor(aWidget);if(XRE_IsContentProcess()){guid.mRenderRoot=gfxUtils::GetContentRenderRoot();}else{guid.mRenderRoot=gfxUtils::RecursivelyGetRenderRootForElement(dpElement);}#ifdef APZCCH_LOGGINGnsAutoStringdpElementDesc;if(dpElement){dpElement->Describe(dpElementDesc);}APZCCH_LOG("For event at %s found scrollable element %p (%s)\n",Stringify(aRefPoint).c_str(),dpElement.get(),NS_LossyConvertUTF16toASCII(dpElementDesc).get());#endifboolguidIsValid=APZCCallbackHelper::GetOrCreateScrollIdentifiers(dpElement,&(guid.mScrollableLayerGuid.mPresShellId),&(guid.mScrollableLayerGuid.mScrollId));aTargets->AppendElement(guid);if(!guidIsValid||nsLayoutUtils::HasDisplayPort(dpElement)){returnfalse;}if(!scrollAncestor){// This can happen if the document element gets swapped out after// ChromeProcessController runs InitializeRootDisplayport. In this case// let's try to set a displayport again and bail out on this operation.APZCCH_LOG("Widget %p's document element %p didn't have a displayport\n",aWidget,dpElement.get());APZCCallbackHelper::InitializeRootDisplayport(aRootFrame->PresShell());returnfalse;}APZCCH_LOG("%p didn't have a displayport, so setting one...\n",dpElement.get());boolactivated=nsLayoutUtils::CalculateAndSetDisplayPortMargins(scrollAncestor,nsLayoutUtils::RepaintMode::Repaint);if(!activated){returnfalse;}nsIFrame*frame=do_QueryFrame(scrollAncestor);nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(frame,nsLayoutUtils::RepaintMode::Repaint);returntrue;}staticvoidSendLayersDependentApzcTargetConfirmation(nsIPresShell*aShell,uint64_taInputBlockId,constnsTArray<SLGuidAndRenderRoot>&aTargets){LayerManager*lm=aShell->GetLayerManager();if(!lm){return;}if(WebRenderLayerManager*wrlm=lm->AsWebRenderLayerManager()){if(WebRenderBridgeChild*wrbc=wrlm->WrBridge()){wrbc->SendSetConfirmedTargetAPZC(aInputBlockId,aTargets);}return;}ShadowLayerForwarder*lf=lm->AsShadowForwarder();if(!lf){return;}LayerTransactionChild*shadow=lf->GetShadowManager();if(!shadow){return;}shadow->SendSetConfirmedTargetAPZC(aInputBlockId,aTargets);}}// namespaceDisplayportSetListener::DisplayportSetListener(nsIWidget*aWidget,nsIPresShell*aPresShell,constuint64_t&aInputBlockId,constnsTArray<SLGuidAndRenderRoot>&aTargets):mWidget(aWidget),mPresShell(aPresShell),mInputBlockId(aInputBlockId),mTargets(aTargets){}DisplayportSetListener::~DisplayportSetListener(){}boolDisplayportSetListener::Register(){if(mPresShell->AddPostRefreshObserver(this)){APZCCH_LOG("Successfully registered post-refresh observer\n");returntrue;}// In case of failure just send the notification right awayAPZCCH_LOG("Sending target APZCs for input block %"PRIu64"\n",mInputBlockId);mWidget->SetConfirmedTargetAPZC(mInputBlockId,mTargets);returnfalse;}voidDisplayportSetListener::DidRefresh(){if(!mPresShell){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);SendLayersDependentApzcTargetConfirmation(mPresShell,mInputBlockId,std::move(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...mPresShell=nullptr;return;}deletethis;}UniquePtr<DisplayportSetListener>APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget*aWidget,dom::Document*aDocument,constWidgetGUIEvent&aEvent,constScrollableLayerGuid&aGuid,uint64_taInputBlockId){if(!aWidget||!aDocument){returnnullptr;}if(aInputBlockId==sLastTargetAPZCNotificationInputBlock){// We have already confirmed the target APZC for a previous event of this// input block. If we activated a scroll frame for this input block,// sending another target APZC confirmation would be harmful, as it might// race the original confirmation (which needs to go through a layers// transaction).APZCCH_LOG("Not resending target APZC confirmation for input block %"PRIu64"\n",aInputBlockId);returnnullptr;}sLastTargetAPZCNotificationInputBlock=aInputBlockId;if(PresShell*presShell=aDocument->GetPresShell()){if(nsIFrame*rootFrame=presShell->GetRootFrame()){rootFrame=UpdateRootFrameForTouchTargetDocument(rootFrame);boolwaitForRefresh=false;nsTArray<SLGuidAndRenderRoot>targets;if(constWidgetTouchEvent*touchEvent=aEvent.AsTouchEvent()){for(size_ti=0;i<touchEvent->mTouches.Length();i++){waitForRefresh|=PrepareForSetTargetAPZCNotification(aWidget,aGuid,rootFrame,touchEvent->mTouches[i]->mRefPoint,&targets);}}elseif(constWidgetWheelEvent*wheelEvent=aEvent.AsWheelEvent()){waitForRefresh=PrepareForSetTargetAPZCNotification(aWidget,aGuid,rootFrame,wheelEvent->mRefPoint,&targets);}elseif(constWidgetMouseEvent*mouseEvent=aEvent.AsMouseEvent()){waitForRefresh=PrepareForSetTargetAPZCNotification(aWidget,aGuid,rootFrame,mouseEvent->mRefPoint,&targets);}// TODO: Do other types of events need to be handled?if(!targets.IsEmpty()){if(waitForRefresh){APZCCH_LOG("At least one target got a new displayport, need to wait for ""refresh\n");returnMakeUnique<DisplayportSetListener>(aWidget,presShell,aInputBlockId,std::move(targets));}APZCCH_LOG("Sending target APZCs for input block %"PRIu64"\n",aInputBlockId);aWidget->SetConfirmedTargetAPZC(aInputBlockId,targets);}}}returnnullptr;}voidAPZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(nsIWidget*aWidget,dom::Document*aDocument,constWidgetTouchEvent&aEvent,uint64_taInputBlockId,constSetAllowedTouchBehaviorCallback&aCallback){if(!aWidget||!aDocument){return;}if(PresShell*presShell=aDocument->GetPresShell()){if(nsIFrame*rootFrame=presShell->GetRootFrame()){rootFrame=UpdateRootFrameForTouchTargetDocument(rootFrame);nsTArray<TouchBehaviorFlags>flags;for(uint32_ti=0;i<aEvent.mTouches.Length();i++){flags.AppendElement(TouchActionHelper::GetAllowedTouchBehavior(aWidget,rootFrame,aEvent.mTouches[i]->mRefPoint));}aCallback(aInputBlockId,std::move(flags));}}}voidAPZCCallbackHelper::NotifyMozMouseScrollEvent(constScrollableLayerGuid::ViewID&aScrollId,constnsString&aEvent){nsCOMPtr<nsIContent>targetContent=nsLayoutUtils::FindContentFor(aScrollId);if(!targetContent){return;}RefPtr<dom::Document>ownerDoc=targetContent->OwnerDoc();if(!ownerDoc){return;}nsContentUtils::DispatchTrustedEvent(ownerDoc,targetContent,aEvent,CanBubble::eYes,Cancelable::eYes);}voidAPZCCallbackHelper::NotifyFlushComplete(nsIPresShell*aShell){MOZ_ASSERT(NS_IsMainThread());// In some cases, flushing the APZ state to the main thread doesn't actually// trigger a flush and repaint (this is an intentional optimization - the// stuff visible to the user is still correct). However, reftests update their// snapshot based on invalidation events that are emitted during paints,// so we ensure that we kick off a paint when an APZ flush is done. Note that// only chrome/testing code can trigger this behaviour.if(aShell&&aShell->GetRootFrame()){aShell->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DEFAULT,false);}nsCOMPtr<nsIObserverService>observerService=mozilla::services::GetObserverService();MOZ_ASSERT(observerService);observerService->NotifyObservers(nullptr,"apz-repaints-flushed",nullptr);}/* static */boolAPZCCallbackHelper::IsScrollInProgress(nsIScrollableFrame*aFrame){returnaFrame->IsProcessingAsyncScroll()||nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())||aFrame->LastSmoothScrollOrigin();}/* static */voidAPZCCallbackHelper::NotifyAsyncScrollbarDragInitiated(uint64_taDragBlockId,constScrollableLayerGuid::ViewID&aScrollId,ScrollDirectionaDirection){MOZ_ASSERT(NS_IsMainThread());if(nsIScrollableFrame*scrollFrame=nsLayoutUtils::FindScrollableFrameFor(aScrollId)){scrollFrame->AsyncScrollbarDragInitiated(aDragBlockId,aDirection);}}/* static */voidAPZCCallbackHelper::NotifyAsyncScrollbarDragRejected(constScrollableLayerGuid::ViewID&aScrollId){MOZ_ASSERT(NS_IsMainThread());if(nsIScrollableFrame*scrollFrame=nsLayoutUtils::FindScrollableFrameFor(aScrollId)){scrollFrame->AsyncScrollbarDragRejected();}}/* static */voidAPZCCallbackHelper::NotifyAsyncAutoscrollRejected(constScrollableLayerGuid::ViewID&aScrollId){MOZ_ASSERT(NS_IsMainThread());nsCOMPtr<nsIObserverService>observerService=mozilla::services::GetObserverService();MOZ_ASSERT(observerService);nsAutoStringdata;data.AppendInt(aScrollId);observerService->NotifyObservers(nullptr,"autoscroll-rejected-by-apz",data.get());}/* static */voidAPZCCallbackHelper::CancelAutoscroll(constScrollableLayerGuid::ViewID&aScrollId){MOZ_ASSERT(NS_IsMainThread());nsCOMPtr<nsIObserverService>observerService=mozilla::services::GetObserverService();MOZ_ASSERT(observerService);nsAutoStringdata;data.AppendInt(aScrollId);observerService->NotifyObservers(nullptr,"apz:cancel-autoscroll",data.get());}/* static */voidAPZCCallbackHelper::NotifyPinchGesture(PinchGestureInput::PinchGestureTypeaType,LayoutDeviceCoordaSpanChange,ModifiersaModifiers,nsIWidget*aWidget){EventMessagemsg;switch(aType){casePinchGestureInput::PINCHGESTURE_START:msg=eMagnifyGestureStart;break;casePinchGestureInput::PINCHGESTURE_SCALE:msg=eMagnifyGestureUpdate;break;casePinchGestureInput::PINCHGESTURE_END:msg=eMagnifyGesture;break;}WidgetSimpleGestureEventevent(true,msg,aWidget);event.mDelta=aSpanChange;event.mModifiers=aModifiers;DispatchWidgetEvent(event);}}// namespace layers}// namespace mozilla