/* -*- 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"nsLayoutUtils.h"#include<algorithm>#include<limits>#include"ActiveLayerTracker.h"#include"DisplayItemClip.h"#include"gfx2DGlue.h"#include"gfxContext.h"#include"gfxDrawable.h"#include"gfxEnv.h"#include"gfxMatrix.h"#include"gfxPlatform.h"#include"gfxRect.h"#include"gfxTypes.h"#include"gfxUtils.h"#include"ImageContainer.h"#include"ImageOps.h"#include"ImageRegion.h"#include"imgIContainer.h"#include"imgIRequest.h"#include"Layers.h"#include"LayoutLogging.h"#include"MobileViewportManager.h"#include"mozilla/AccessibleCaretEventHub.h"#include"mozilla/ArrayUtils.h"#include"mozilla/BasicEvents.h"#include"mozilla/ClearOnShutdown.h"#include"mozilla/DisplayPortUtils.h"#include"mozilla/GeckoBindings.h"#include"mozilla/glean/GleanMetrics.h"#include"mozilla/dom/AnonymousContent.h"#include"mozilla/dom/BrowserChild.h"#include"mozilla/dom/CanvasUtils.h"#include"mozilla/dom/Document.h"#include"mozilla/dom/DocumentInlines.h"#include"mozilla/dom/DOMRect.h"#include"mozilla/dom/DOMStringList.h"#include"mozilla/dom/Element.h"#include"mozilla/dom/HTMLBodyElement.h"#include"mozilla/dom/HTMLCanvasElement.h"#include"mozilla/dom/HTMLImageElement.h"#include"mozilla/dom/HTMLMediaElementBinding.h"#include"mozilla/dom/HTMLVideoElement.h"#include"mozilla/dom/InspectorFontFace.h"#include"mozilla/dom/KeyframeEffect.h"#include"mozilla/dom/SVGViewportElement.h"#include"mozilla/dom/UIEvent.h"#include"mozilla/EffectCompositor.h"#include"mozilla/EffectSet.h"#include"mozilla/EventDispatcher.h"#include"mozilla/EventStateManager.h"#include"mozilla/FloatingPoint.h"#include"mozilla/gfx/2D.h"#include"mozilla/gfx/gfxVars.h"#include"mozilla/gfx/PathHelpers.h"#include"mozilla/layers/APZCCallbackHelper.h"#include"mozilla/layers/APZPublicUtils.h" // for apz::CalculatePendingDisplayPort#include"mozilla/layers/CompositorBridgeChild.h"#include"mozilla/layers/PAPZ.h"#include"mozilla/layers/StackingContextHelper.h"#include"mozilla/layers/WebRenderLayerManager.h"#include"mozilla/Likely.h"#include"mozilla/LookAndFeel.h"#include"mozilla/Maybe.h"#include"mozilla/MemoryReporting.h"#include"mozilla/PerfStats.h"#include"mozilla/Preferences.h"#include"mozilla/PresShell.h"#include"mozilla/ProfilerLabels.h"#include"mozilla/ProfilerMarkers.h"#include"mozilla/RestyleManager.h"#include"mozilla/ScopeExit.h"#include"mozilla/ScrollOrigin.h"#include"mozilla/ServoStyleSet.h"#include"mozilla/ServoStyleSetInlines.h"#include"mozilla/StaticPrefs_apz.h"#include"mozilla/StaticPrefs_dom.h"#include"mozilla/StaticPrefs_font.h"#include"mozilla/StaticPrefs_gfx.h"#include"mozilla/StaticPrefs_image.h"#include"mozilla/StaticPrefs_layers.h"#include"mozilla/StaticPrefs_layout.h"#include"mozilla/StyleAnimationValue.h"#include"mozilla/SVGImageContext.h"#include"mozilla/SVGIntegrationUtils.h"#include"mozilla/SVGTextFrame.h"#include"mozilla/SVGUtils.h"#include"mozilla/Telemetry.h"#include"mozilla/ToString.h"#include"mozilla/Unused.h"#include"mozilla/ViewportFrame.h"#include"mozilla/ViewportUtils.h"#include"mozilla/WheelHandlingHelper.h" // for WheelHandlingUtils#include"nsAnimationManager.h"#include"nsAtom.h"#include"nsBidiPresUtils.h"#include"nsBlockFrame.h"#include"nsCanvasFrame.h"#include"nsCaret.h"#include"nsCharTraits.h"#include"nsCOMPtr.h"#include"nsComputedDOMStyle.h"#include"nsCSSAnonBoxes.h"#include"nsCSSColorUtils.h"#include"nsCSSFrameConstructor.h"#include"nsCSSProps.h"#include"nsCSSPseudoElements.h"#include"nsCSSRendering.h"#include"nsTHashMap.h"#include"nsDeckFrame.h"#include"nsDisplayList.h"#include"nsFlexContainerFrame.h"#include"nsFontInflationData.h"#include"nsFontMetrics.h"#include"nsFrameList.h"#include"nsFrameSelection.h"#include"nsGenericHTMLElement.h"#include"nsGkAtoms.h"#include"nsICanvasRenderingContextInternal.h"#include"nsIContent.h"#include"nsIContentViewer.h"#include"nsIDocShell.h"#include"nsIFrameInlines.h"#include"nsIImageLoadingContent.h"#include"nsIInterfaceRequestorUtils.h"#include"nsIScrollableFrame.h"#include"nsIWidget.h"#include"nsListControlFrame.h"#include"nsPIDOMWindow.h"#include"nsPlaceholderFrame.h"#include"nsPresContext.h"#include"nsPresContextInlines.h"#include"nsRefreshDriver.h"#include"nsRegion.h"#include"nsStyleConsts.h"#include"nsStyleStructInlines.h"#include"nsStyleTransformMatrix.h"#include"nsSubDocumentFrame.h"#include"nsTableWrapperFrame.h"#include"nsTArray.h"#include"nsTextFragment.h"#include"nsTextFrame.h"#include"nsTransitionManager.h"#include"nsView.h"#include"nsViewManager.h"#include"prenv.h"#include"RegionBuilder.h"#include"RetainedDisplayListBuilder.h"#include"TextDrawTarget.h"#include"UnitTransforms.h"#include"ViewportFrame.h"#ifdef MOZ_XUL# include "nsXULPopupManager.h"#endif// Make sure getpid() works.#ifdef XP_WIN# include <process.h># define getpid _getpid#else# include <unistd.h>#endifusingnamespacemozilla;usingnamespacemozilla::dom;usingnamespacemozilla::image;usingnamespacemozilla::layers;usingnamespacemozilla::layout;usingnamespacemozilla::gfx;usingmozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;usingmozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING;#ifdef DEBUG// TODO: remove, see bug 598468.boolnsLayoutUtils::gPreventAssertInCompareTreePosition=false;#endif // DEBUGtypedefScrollableLayerGuid::ViewIDViewID;typedefnsStyleTransformMatrix::TransformReferenceBoxTransformReferenceBox;staticViewIDsScrollIdCounter=ScrollableLayerGuid::START_SCROLL_ID;typedefnsTHashMap<nsUint64HashKey,nsIContent*>ContentMap;staticContentMap*sContentMap=nullptr;staticContentMap&GetContentMap(){if(!sContentMap){sContentMap=newContentMap();}return*sContentMap;}template<typenameTestType>staticboolHasMatchingAnimations(EffectSet&aEffects,TestType&&aTest){for(KeyframeEffect*effect:aEffects){if(!effect->GetAnimation()||!effect->GetAnimation()->IsRelevant()){continue;}if(aTest(*effect,aEffects)){returntrue;}}returnfalse;}template<typenameTestType>staticboolHasMatchingAnimations(constnsIFrame*aFrame,constnsCSSPropertyIDSet&aPropertySet,TestType&&aTest){MOZ_ASSERT(aFrame);if(aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())&&!aFrame->MayHaveOpacityAnimation()){returnfalse;}if(aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())&&!aFrame->MayHaveTransformAnimation()){returnfalse;}EffectSet*effectSet=EffectSet::GetEffectSetForFrame(aFrame,aPropertySet);if(!effectSet){returnfalse;}returnHasMatchingAnimations(*effectSet,aTest);}/* static */boolnsLayoutUtils::HasAnimationOfPropertySet(constnsIFrame*aFrame,constnsCSSPropertyIDSet&aPropertySet){returnHasMatchingAnimations(aFrame,aPropertySet,[&aPropertySet](KeyframeEffect&aEffect,constEffectSet&){returnaEffect.HasAnimationOfPropertySet(aPropertySet);});}/* static */boolnsLayoutUtils::HasAnimationOfPropertySet(constnsIFrame*aFrame,constnsCSSPropertyIDSet&aPropertySet,EffectSet*aEffectSet){MOZ_ASSERT(!aEffectSet||EffectSet::GetEffectSetForFrame(aFrame,aPropertySet)==aEffectSet,"The EffectSet, if supplied, should match what we would otherwise fetch");if(!aEffectSet){returnnsLayoutUtils::HasAnimationOfPropertySet(aFrame,aPropertySet);}if(aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())&&!aEffectSet->MayHaveTransformAnimation()){returnfalse;}if(aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())&&!aEffectSet->MayHaveOpacityAnimation()){returnfalse;}returnHasMatchingAnimations(*aEffectSet,[&aPropertySet](KeyframeEffect&aEffect,constEffectSet&aEffectSet){returnaEffect.HasAnimationOfPropertySet(aPropertySet);});}/* static */boolnsLayoutUtils::HasAnimationOfTransformAndMotionPath(constnsIFrame*aFrame){returnnsLayoutUtils::HasAnimationOfPropertySet(aFrame,nsCSSPropertyIDSet{eCSSProperty_transform,eCSSProperty_translate,eCSSProperty_rotate,eCSSProperty_scale,eCSSProperty_offset_path})||(!aFrame->StyleDisplay()->mOffsetPath.IsNone()&&nsLayoutUtils::HasAnimationOfPropertySet(aFrame,nsCSSPropertyIDSet::MotionPathProperties()));}/* static */boolnsLayoutUtils::HasEffectiveAnimation(constnsIFrame*aFrame,constnsCSSPropertyIDSet&aPropertySet){returnHasMatchingAnimations(aFrame,aPropertySet,[&aPropertySet](KeyframeEffect&aEffect,constEffectSet&aEffectSet){returnaEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,aEffectSet);});}/* static */nsCSSPropertyIDSetnsLayoutUtils::GetAnimationPropertiesForCompositor(constnsIFrame*aStyleFrame){nsCSSPropertyIDSetproperties;// We fetch the effects for the style frame here since this method is called// by RestyleManager::AddLayerChangesForAnimation which takes care to apply// the relevant hints to the primary frame as needed.EffectSet*effects=EffectSet::GetEffectSetForStyleFrame(aStyleFrame);if(!effects){returnproperties;}AnimationPerformanceWarning::Typewarning;if(!EffectCompositor::AllowCompositorAnimationsOnFrame(aStyleFrame,warning)){returnproperties;}for(constKeyframeEffect*effect:*effects){properties|=effect->GetPropertiesForCompositor(*effects,aStyleFrame);}// If properties only have motion-path properties, we have to make sure they// have effects. i.e. offset-path is not none or we have offset-path// animations.if(properties.IsSubsetOf(nsCSSPropertyIDSet::MotionPathProperties())&&!properties.HasProperty(eCSSProperty_offset_path)&&aStyleFrame->StyleDisplay()->mOffsetPath.IsNone()){properties.Empty();}returnproperties;}staticfloatGetSuitableScale(floataMaxScale,floataMinScale,nscoordaVisibleDimension,nscoordaDisplayDimension){floatdisplayVisibleRatio=float(aDisplayDimension)/float(aVisibleDimension);// We want to rasterize based on the largest scale used during the// transform animation, unless that would make us rasterize something// larger than the screen. But we never want to go smaller than the// minimum scale over the animation.if(FuzzyEqualsMultiplicative(displayVisibleRatio,aMaxScale,.01f)){// Using aMaxScale may make us rasterize something a fraction larger than// the screen. However, if aMaxScale happens to be the final scale of a// transform animation it is better to use aMaxScale so that for the// fraction of a second before we delayerize the composited texture it has// a better chance of being pixel aligned and composited without resampling// (avoiding visually clunky delayerization).returnaMaxScale;}returnstd::max(std::min(aMaxScale,displayVisibleRatio),aMinScale);}// The first value in this pair is the min scale, and the second one is the max// scale.usingMinAndMaxScale=std::pair<Size,Size>;staticinlinevoidUpdateMinMaxScale(constnsIFrame*aFrame,constAnimationValue&aValue,MinAndMaxScale&aMinAndMaxScale){Sizesize=aValue.GetScaleValue(aFrame);Size&minScale=aMinAndMaxScale.first;Size&maxScale=aMinAndMaxScale.second;minScale=Min(minScale,size);maxScale=Max(maxScale,size);}// The final transform matrix is calculated by merging the final results of each// transform-like properties, so do the scale factors. In other words, the// potential min/max scales could be gotten by multiplying the max/min scales of// each properties.//// For example, there is an animation:// from { "transform: scale(1, 1)", "scale: 3, 3" };// to { "transform: scale(2, 2)", "scale: 1, 1" };//// the min scale is (1, 1) * (1, 1) = (1, 1), and// The max scale is (2, 2) * (3, 3) = (6, 6).// This means we multiply the min/max scale factor of transform property and the// min/max scale factor of scale property to get the final max/min scale factor.staticArray<MinAndMaxScale,2>GetMinAndMaxScaleForAnimationProperty(constnsIFrame*aFrame,constnsTArray<RefPtr<dom::Animation>>&aAnimations){// We use a fixed array to store the min/max scales for each property.// The first element in the array is for eCSSProperty_transform, and the// second one is for eCSSProperty_scale.constMinAndMaxScaledefaultValue=std::make_pair(Size(std::numeric_limits<float>::max(),std::numeric_limits<float>::max()),Size(std::numeric_limits<float>::min(),std::numeric_limits<float>::min()));Array<MinAndMaxScale,2>minAndMaxScales(defaultValue,defaultValue);for(dom::Animation*anim:aAnimations){// This method is only expected to be passed animations that are running on// the compositor and we only pass playing animations to the compositor,// which are, by definition, "relevant" animations (animations that are// not yet finished or which are filling forwards).MOZ_ASSERT(anim->IsRelevant());constdom::KeyframeEffect*effect=anim->GetEffect()?anim->GetEffect()->AsKeyframeEffect():nullptr;MOZ_ASSERT(effect,"A playing animation should have a keyframe effect");for(constAnimationProperty&prop:effect->Properties()){if(prop.mProperty!=eCSSProperty_transform&&prop.mProperty!=eCSSProperty_scale){continue;}// 0: eCSSProperty_transform.// 1: eCSSProperty_scale.MinAndMaxScale&scales=minAndMaxScales[prop.mProperty==eCSSProperty_transform?0:1];// We need to factor in the scale of the base style if the base style// will be used on the compositor.constAnimationValue&baseStyle=effect->BaseStyle(prop.mProperty);if(!baseStyle.IsNull()){UpdateMinMaxScale(aFrame,baseStyle,scales);}for(constAnimationPropertySegment&segment:prop.mSegments){// In case of add or accumulate composite, StyleAnimationValue does// not have a valid value.if(segment.HasReplaceableFromValue()){UpdateMinMaxScale(aFrame,segment.mFromValue,scales);}if(segment.HasReplaceableToValue()){UpdateMinMaxScale(aFrame,segment.mToValue,scales);}}}}returnminAndMaxScales;}SizensLayoutUtils::ComputeSuitableScaleForAnimation(constnsIFrame*aFrame,constnsSize&aVisibleSize,constnsSize&aDisplaySize){constnsTArray<RefPtr<dom::Animation>>compositorAnimations=EffectCompositor::GetAnimationsForCompositor(aFrame,nsCSSPropertyIDSet{eCSSProperty_transform,eCSSProperty_scale});if(compositorAnimations.IsEmpty()){returnSize(1.0,1.0);}constArray<MinAndMaxScale,2>minAndMaxScales=GetMinAndMaxScaleForAnimationProperty(aFrame,compositorAnimations);// This might cause an issue if users use std::numeric_limits<float>::min()// (or max()) as the scale value. However, in this case, we may render an// extreme small (or large) element, so this may not be a problem. If so,// please fix this.SizemaxScale(std::numeric_limits<float>::min(),std::numeric_limits<float>::min());SizeminScale(std::numeric_limits<float>::max(),std::numeric_limits<float>::max());autoisUnset=[](constSize&aMax,constSize&aMin){returnaMax.width==std::numeric_limits<float>::min()&&aMax.height==std::numeric_limits<float>::min()&&aMin.width==std::numeric_limits<float>::max()&&aMin.height==std::numeric_limits<float>::max();};// Iterate the slots to get the final scale value.for(constauto&pair:minAndMaxScales){constSize&currMinScale=pair.first;constSize&currMaxScale=pair.second;if(isUnset(currMaxScale,currMinScale)){// We don't have this animation property, so skip.continue;}if(isUnset(maxScale,minScale)){// Initialize maxScale and minScale.maxScale=currMaxScale;minScale=currMinScale;}else{// The scale factors of each transform-like property should be multiplied// by others because we merge their sampled values as a final matrix by// matrix multiplication, so here we multiply the scale factors by the// previous one to get the possible max and min scale factors.maxScale=maxScale*currMaxScale;minScale=minScale*currMinScale;}}if(isUnset(maxScale,minScale)){// We didn't encounter any transform-like property.returnSize(1.0,1.0);}returnSize(GetSuitableScale(maxScale.width,minScale.width,aVisibleSize.width,aDisplaySize.width),GetSuitableScale(maxScale.height,minScale.height,aVisibleSize.height,aDisplaySize.height));}boolnsLayoutUtils::AreAsyncAnimationsEnabled(){returnStaticPrefs::layers_offmainthreadcomposition_async_animations()&&gfxPlatform::OffMainThreadCompositingEnabled();}boolnsLayoutUtils::AreRetainedDisplayListsEnabled(){#ifdef MOZ_WIDGET_ANDROIDreturnStaticPrefs::layout_display_list_retain();#elseif(XRE_IsContentProcess()){returnStaticPrefs::layout_display_list_retain();}if(XRE_IsE10sParentProcess()){returnStaticPrefs::layout_display_list_retain_chrome();}// Retained display lists require e10s.returnfalse;#endif}boolnsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame*aFrame){constnsIFrame*displayRoot=nsLayoutUtils::GetDisplayRootFrame(aFrame);MOZ_ASSERT(displayRoot);returndisplayRoot->HasProperty(RetainedDisplayListBuilder::Cached());}boolnsLayoutUtils::GPUImageScalingEnabled(){staticboolsGPUImageScalingEnabled;staticboolsGPUImageScalingPrefInitialised=false;if(!sGPUImageScalingPrefInitialised){sGPUImageScalingPrefInitialised=true;sGPUImageScalingEnabled=Preferences::GetBool("layout.gpu-image-scaling.enabled",false);}returnsGPUImageScalingEnabled;}voidnsLayoutUtils::UnionChildOverflow(nsIFrame*aFrame,OverflowAreas&aOverflowAreas,FrameChildListIDsaSkipChildLists){// Iterate over all children except pop-ups.FrameChildListIDsskip(aSkipChildLists);skip+={nsIFrame::kSelectPopupList,nsIFrame::kPopupList};for(constauto&[list,listID]:aFrame->ChildLists()){if(skip.contains(listID)){continue;}for(nsIFrame*child:list){aOverflowAreas.UnionWith(child->GetOverflowAreasRelativeToParent());}}}staticvoidDestroyViewID(void*aObject,nsAtom*aPropertyName,void*aPropertyValue,void*aData){ViewID*id=static_cast<ViewID*>(aPropertyValue);GetContentMap().Remove(*id);deleteid;}/** * A namespace class for static layout utilities. */boolnsLayoutUtils::FindIDFor(constnsIContent*aContent,ViewID*aOutViewId){void*scrollIdProperty=aContent->GetProperty(nsGkAtoms::RemoteId);if(scrollIdProperty){*aOutViewId=*static_cast<ViewID*>(scrollIdProperty);returntrue;}returnfalse;}ViewIDnsLayoutUtils::FindOrCreateIDFor(nsIContent*aContent){ViewIDscrollId;if(!FindIDFor(aContent,&scrollId)){scrollId=sScrollIdCounter++;aContent->SetProperty(nsGkAtoms::RemoteId,newViewID(scrollId),DestroyViewID);GetContentMap().InsertOrUpdate(scrollId,aContent);}returnscrollId;}nsIContent*nsLayoutUtils::FindContentFor(ViewIDaId){MOZ_ASSERT(aId!=ScrollableLayerGuid::NULL_SCROLL_ID,"Cannot find a content element in map for null IDs.");nsIContent*content;boolexists=GetContentMap().Get(aId,&content);if(exists){returncontent;}else{returnnullptr;}}nsIFrame*nsLayoutUtils::GetScrollFrameFromContent(nsIContent*aContent){nsIFrame*frame=aContent->GetPrimaryFrame();if(aContent->OwnerDoc()->GetRootElement()==aContent){PresShell*presShell=frame?frame->PresShell():nullptr;if(!presShell){presShell=aContent->OwnerDoc()->GetPresShell();}// We want the scroll frame, the root scroll frame differs from all// others in that the primary frame is not the scroll frame.nsIFrame*rootScrollFrame=presShell?presShell->GetRootScrollFrame():nullptr;if(rootScrollFrame){frame=rootScrollFrame;}}returnframe;}nsIScrollableFrame*nsLayoutUtils::FindScrollableFrameFor(nsIContent*aContent){nsIFrame*scrollFrame=GetScrollFrameFromContent(aContent);returnscrollFrame?scrollFrame->GetScrollTargetFrame():nullptr;}nsIScrollableFrame*nsLayoutUtils::FindScrollableFrameFor(ViewIDaId){nsIContent*content=FindContentFor(aId);if(!content){returnnullptr;}returnFindScrollableFrameFor(content);}ViewIDnsLayoutUtils::FindIDForScrollableFrame(nsIScrollableFrame*aScrollable){if(!aScrollable){returnScrollableLayerGuid::NULL_SCROLL_ID;}nsIFrame*scrollFrame=do_QueryFrame(aScrollable);nsIContent*scrollContent=scrollFrame->GetContent();ScrollableLayerGuid::ViewIDscrollId;if(scrollContent&&nsLayoutUtils::FindIDFor(scrollContent,&scrollId)){returnscrollId;}returnScrollableLayerGuid::NULL_SCROLL_ID;}boolnsLayoutUtils::UsesAsyncScrolling(nsIFrame*aFrame){#ifdef MOZ_WIDGET_ANDROID// We always have async scrolling for androidreturntrue;#endifreturnAsyncPanZoomEnabled(aFrame);}boolnsLayoutUtils::AsyncPanZoomEnabled(constnsIFrame*aFrame){// We use this as a shortcut, since if the compositor will never use APZ,// no widget will either.if(!gfxPlatform::AsyncPanZoomEnabled()){returnfalse;}constnsIFrame*frame=nsLayoutUtils::GetDisplayRootFrame(aFrame);nsIWidget*widget=frame->GetNearestWidget();if(!widget){returnfalse;}returnwidget->AsyncPanZoomEnabled();}boolnsLayoutUtils::AllowZoomingForDocument(constmozilla::dom::Document*aDocument){if(aDocument->GetPresShell()&&!aDocument->GetPresShell()->AsyncPanZoomEnabled()){returnfalse;}// True if we allow zooming for all documents on this platform, or if we are// in RDM and handling meta viewports, which force zoom under some// circumstances.BrowsingContext*bc=aDocument?aDocument->GetBrowsingContext():nullptr;returnStaticPrefs::apz_allow_zooming()||(bc&&bc->InRDMPane()&&nsLayoutUtils::ShouldHandleMetaViewport(aDocument));}floatnsLayoutUtils::GetCurrentAPZResolutionScale(PresShell*aPresShell){returnaPresShell?aPresShell->GetCumulativeResolution():1.0;}staticboolHasVisibleAnonymousContents(Document*aDoc){for(RefPtr<AnonymousContent>&ac:aDoc->GetAnonymousContents()){// We check to see if the anonymous content node has a frame. If it doesn't,// that means that's not visible to the user because e.g. it's display:none.// For now we assume that if it has a frame, it is visible. We might be able// to refine this further by adding complexity if it turns out this// condition results in a lot of false positives.if(ac->ContentNode().GetPrimaryFrame()){returntrue;}}returnfalse;}boolnsLayoutUtils::ShouldDisableApzForElement(nsIContent*aContent){if(!aContent){returnfalse;}if(aContent->GetProperty(nsGkAtoms::apzDisabled)){returntrue;}Document*doc=aContent->GetComposedDoc();if(PresShell*rootPresShell=APZCCallbackHelper::GetRootContentDocumentPresShellForContent(aContent)){if(Document*rootDoc=rootPresShell->GetDocument()){nsIContent*rootContent=rootPresShell->GetRootScrollFrame()?rootPresShell->GetRootScrollFrame()->GetContent():rootDoc->GetDocumentElement();// For the AccessibleCaret and other anonymous contents: disable APZ on// any scrollable subframes that are not the root scrollframe of a// document, if the document has any visible anonymous contents.//// If we find this is triggering in too many scenarios then we might// want to tighten this check further. The main use cases for which we// want to disable APZ as of this writing are listed in bug 1316318.if(aContent!=rootContent&&HasVisibleAnonymousContents(rootDoc)){returntrue;}}}if(!doc){returnfalse;}if(PresShell*presShell=doc->GetPresShell()){if(RefPtr<AccessibleCaretEventHub>eventHub=presShell->GetAccessibleCaretEventHub()){// Disable APZ for all elements if AccessibleCaret tells us to do so.if(eventHub->ShouldDisableApz()){returntrue;}}}returnStaticPrefs::apz_disable_for_scroll_linked_effects()&&doc->HasScrollLinkedEffect();}voidnsLayoutUtils::NotifyPaintSkipTransaction(ViewIDaScrollId){if(nsIScrollableFrame*scrollFrame=nsLayoutUtils::FindScrollableFrameFor(aScrollId)){#ifdef DEBUGnsIFrame*f=do_QueryFrame(scrollFrame);MOZ_ASSERT(f&&f->PresShell()&&!f->PresShell()->IsResolutionUpdated());#endifscrollFrame->NotifyApzTransaction();}}nsContainerFrame*nsLayoutUtils::LastContinuationWithChild(nsContainerFrame*aFrame){MOZ_ASSERT(aFrame,"NULL frame pointer");for(autof=aFrame->LastContinuation();f;f=f->GetPrevContinuation()){for(constauto&childList:f->ChildLists()){if(MOZ_LIKELY(!childList.mList.IsEmpty())){returnstatic_cast<nsContainerFrame*>(f);}}}returnaFrame;}// staticFrameChildListIDnsLayoutUtils::GetChildListNameFor(nsIFrame*aChildFrame){nsIFrame::ChildListIDid=nsIFrame::kPrincipalList;MOZ_DIAGNOSTIC_ASSERT(!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));if(aChildFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)){nsIFrame*pif=aChildFrame->GetPrevInFlow();if(pif->GetParent()==aChildFrame->GetParent()){id=nsIFrame::kExcessOverflowContainersList;}else{id=nsIFrame::kOverflowContainersList;}}else{LayoutFrameTypechildType=aChildFrame->Type();if(LayoutFrameType::MenuPopup==childType){nsIFrame*parent=aChildFrame->GetParent();MOZ_ASSERT(parent,"nsMenuPopupFrame can't be the root frame");if(parent){if(parent->IsPopupSetFrame()){id=nsIFrame::kPopupList;}else{nsIFrame*firstPopup=parent->GetChildList(nsIFrame::kPopupList).FirstChild();MOZ_ASSERT(!firstPopup||!firstPopup->GetNextSibling(),"We assume popupList only has one child, but it has more.");id=firstPopup==aChildFrame?nsIFrame::kPopupList:nsIFrame::kPrincipalList;}}else{id=nsIFrame::kPrincipalList;}}elseif(LayoutFrameType::TableColGroup==childType){id=nsIFrame::kColGroupList;}elseif(aChildFrame->IsTableCaption()){id=nsIFrame::kCaptionList;}else{id=nsIFrame::kPrincipalList;}}#ifdef DEBUG// Verify that the frame is actually in that child list or in the// corresponding overflow list.nsContainerFrame*parent=aChildFrame->GetParent();boolfound=parent->GetChildList(id).ContainsFrame(aChildFrame);if(!found){found=parent->GetChildList(nsIFrame::kOverflowList).ContainsFrame(aChildFrame);MOZ_ASSERT(found,"not in child list");}#endifreturnid;}staticElement*GetPseudo(constnsIContent*aContent,nsAtom*aPseudoProperty){MOZ_ASSERT(aPseudoProperty==nsGkAtoms::beforePseudoProperty||aPseudoProperty==nsGkAtoms::afterPseudoProperty||aPseudoProperty==nsGkAtoms::markerPseudoProperty);if(!aContent->MayHaveAnonymousChildren()){returnnullptr;}returnstatic_cast<Element*>(aContent->GetProperty(aPseudoProperty));}/*static*/Element*nsLayoutUtils::GetBeforePseudo(constnsIContent*aContent){returnGetPseudo(aContent,nsGkAtoms::beforePseudoProperty);}/*static*/nsIFrame*nsLayoutUtils::GetBeforeFrame(constnsIContent*aContent){Element*pseudo=GetBeforePseudo(aContent);returnpseudo?pseudo->GetPrimaryFrame():nullptr;}/*static*/Element*nsLayoutUtils::GetAfterPseudo(constnsIContent*aContent){returnGetPseudo(aContent,nsGkAtoms::afterPseudoProperty);}/*static*/nsIFrame*nsLayoutUtils::GetAfterFrame(constnsIContent*aContent){Element*pseudo=GetAfterPseudo(aContent);returnpseudo?pseudo->GetPrimaryFrame():nullptr;}/*static*/Element*nsLayoutUtils::GetMarkerPseudo(constnsIContent*aContent){returnGetPseudo(aContent,nsGkAtoms::markerPseudoProperty);}/*static*/nsIFrame*nsLayoutUtils::GetMarkerFrame(constnsIContent*aContent){Element*pseudo=GetMarkerPseudo(aContent);returnpseudo?pseudo->GetPrimaryFrame():nullptr;}#ifdef ACCESSIBILITYvoidnsLayoutUtils::GetMarkerSpokenText(constnsIContent*aContent,nsAString&aText){MOZ_ASSERT(aContent&&aContent->IsGeneratedContentContainerForMarker());aText.Truncate();nsIFrame*frame=aContent->GetPrimaryFrame();if(!frame){return;}if(frame->StyleContent()->ContentCount()>0){for(nsIFrame*child:frame->PrincipalChildList()){nsIFrame::RenderedTexttext=child->GetRenderedText();aText+=text.mString;}return;}if(!frame->StyleList()->mListStyleImage.IsNone()){// ::marker is an image, so use default bullet character.staticconstchar16_tkDiscMarkerString[]={0x2022,' ',0};aText.AssignLiteral(kDiscMarkerString);return;}frame->PresContext()->FrameConstructor()->CounterManager()->GetSpokenCounterText(frame,aText);}#endif// staticnsIFrame*nsLayoutUtils::GetClosestFrameOfType(nsIFrame*aFrame,LayoutFrameTypeaFrameType,nsIFrame*aStopAt){for(nsIFrame*frame=aFrame;frame;frame=frame->GetParent()){if(frame->Type()==aFrameType){returnframe;}if(frame==aStopAt){break;}}returnnullptr;}/* static */nsIFrame*nsLayoutUtils::GetPageFrame(nsIFrame*aFrame){returnGetClosestFrameOfType(aFrame,LayoutFrameType::Page);}/* static */nsIFrame*nsLayoutUtils::GetStyleFrame(nsIFrame*aPrimaryFrame){MOZ_ASSERT(aPrimaryFrame);if(aPrimaryFrame->IsTableWrapperFrame()){nsIFrame*inner=aPrimaryFrame->PrincipalChildList().FirstChild();// inner may be null, if aPrimaryFrame is mid-destructionreturninner;}returnaPrimaryFrame;}constnsIFrame*nsLayoutUtils::GetStyleFrame(constnsIFrame*aPrimaryFrame){returnnsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aPrimaryFrame));}nsIFrame*nsLayoutUtils::GetStyleFrame(constnsIContent*aContent){nsIFrame*frame=aContent->GetPrimaryFrame();if(!frame){returnnullptr;}returnnsLayoutUtils::GetStyleFrame(frame);}CSSIntCoordnsLayoutUtils::UnthemedScrollbarSize(StyleScrollbarWidthaWidth){switch(aWidth){caseStyleScrollbarWidth::Auto:return12;caseStyleScrollbarWidth::Thin:return6;caseStyleScrollbarWidth::None:return0;}return0;}/* static */nsIFrame*nsLayoutUtils::GetPrimaryFrameFromStyleFrame(nsIFrame*aStyleFrame){nsIFrame*parent=aStyleFrame->GetParent();returnparent&&parent->IsTableWrapperFrame()?parent:aStyleFrame;}/* static */constnsIFrame*nsLayoutUtils::GetPrimaryFrameFromStyleFrame(constnsIFrame*aStyleFrame){returnnsLayoutUtils::GetPrimaryFrameFromStyleFrame(const_cast<nsIFrame*>(aStyleFrame));}/*static*/boolnsLayoutUtils::IsPrimaryStyleFrame(constnsIFrame*aFrame){if(aFrame->IsTableWrapperFrame()){returnfalse;}constnsIFrame*parent=aFrame->GetParent();if(parent&&parent->IsTableWrapperFrame()){returnparent->PrincipalChildList().FirstChild()==aFrame;}returnaFrame->IsPrimaryFrame();}nsIFrame*nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame*aFrame){NS_ASSERTION(aFrame->IsPlaceholderFrame(),"Must have a placeholder here");if(aFrame->HasAnyStateBits(PLACEHOLDER_FOR_FLOAT)){nsIFrame*outOfFlowFrame=nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);NS_ASSERTION(outOfFlowFrame&&outOfFlowFrame->IsFloating(),"How did that happen?");returnoutOfFlowFrame;}returnnullptr;}// staticnsIFrame*nsLayoutUtils::GetCrossDocParentFrameInProcess(constnsIFrame*aFrame,nsPoint*aCrossDocOffset){nsIFrame*p=aFrame->GetParent();if(p){returnp;}nsView*v=aFrame->GetView();if(!v){returnnullptr;}v=v->GetParent();// anonymous inner viewif(!v){returnnullptr;}v=v->GetParent();// subdocumentframe's viewif(!v){returnnullptr;}p=v->GetFrame();if(p&&aCrossDocOffset){nsSubDocumentFrame*subdocumentFrame=do_QueryFrame(p);MOZ_ASSERT(subdocumentFrame);*aCrossDocOffset+=subdocumentFrame->GetExtraOffset();}returnp;}// staticnsIFrame*nsLayoutUtils::GetCrossDocParentFrame(constnsIFrame*aFrame,nsPoint*aCrossDocOffset){returnGetCrossDocParentFrameInProcess(aFrame,aCrossDocOffset);}// staticboolnsLayoutUtils::IsProperAncestorFrameCrossDoc(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){if(aFrame==aAncestorFrame)returnfalse;returnIsAncestorFrameCrossDoc(aAncestorFrame,aFrame,aCommonAncestor);}// staticboolnsLayoutUtils::IsProperAncestorFrameCrossDocInProcess(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){if(aFrame==aAncestorFrame)returnfalse;returnIsAncestorFrameCrossDocInProcess(aAncestorFrame,aFrame,aCommonAncestor);}// staticboolnsLayoutUtils::IsAncestorFrameCrossDoc(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){for(constnsIFrame*f=aFrame;f!=aCommonAncestor;f=GetCrossDocParentFrameInProcess(f)){if(f==aAncestorFrame)returntrue;}returnaCommonAncestor==aAncestorFrame;}// staticboolnsLayoutUtils::IsAncestorFrameCrossDocInProcess(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){for(constnsIFrame*f=aFrame;f!=aCommonAncestor;f=GetCrossDocParentFrameInProcess(f)){if(f==aAncestorFrame)returntrue;}returnaCommonAncestor==aAncestorFrame;}// staticboolnsLayoutUtils::IsProperAncestorFrame(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){if(aFrame==aAncestorFrame)returnfalse;for(constnsIFrame*f=aFrame;f!=aCommonAncestor;f=f->GetParent()){if(f==aAncestorFrame)returntrue;}returnaCommonAncestor==aAncestorFrame;}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIContent*aContent1,nsIContent*aContent2,int32_taIf1Ancestor,int32_taIf2Ancestor,constnsIContent*aCommonAncestor){MOZ_ASSERT(aIf1Ancestor==-1||aIf1Ancestor==0||aIf1Ancestor==1);MOZ_ASSERT(aIf2Ancestor==-1||aIf2Ancestor==0||aIf2Ancestor==1);MOZ_ASSERT(aContent1,"aContent1 must not be null");MOZ_ASSERT(aContent2,"aContent2 must not be null");AutoTArray<nsINode*,32>content1Ancestors;nsINode*c1;for(c1=aContent1;c1&&c1!=aCommonAncestor;c1=c1->GetParentOrShadowHostNode()){content1Ancestors.AppendElement(c1);}if(!c1&&aCommonAncestor){// So, it turns out aCommonAncestor was not an ancestor of c1. Oops.// Never mind. We can continue as if aCommonAncestor was null.aCommonAncestor=nullptr;}AutoTArray<nsINode*,32>content2Ancestors;nsINode*c2;for(c2=aContent2;c2&&c2!=aCommonAncestor;c2=c2->GetParentOrShadowHostNode()){content2Ancestors.AppendElement(c2);}if(!c2&&aCommonAncestor){// So, it turns out aCommonAncestor was not an ancestor of c2.// We need to retry with no common ancestor hint.returnDoCompareTreePosition(aContent1,aContent2,aIf1Ancestor,aIf2Ancestor,nullptr);}intlast1=content1Ancestors.Length()-1;intlast2=content2Ancestors.Length()-1;nsINode*content1Ancestor=nullptr;nsINode*content2Ancestor=nullptr;while(last1>=0&&last2>=0&&((content1Ancestor=content1Ancestors.ElementAt(last1))==(content2Ancestor=content2Ancestors.ElementAt(last2)))){last1--;last2--;}if(last1<0){if(last2<0){NS_ASSERTION(aContent1==aContent2,"internal error?");return0;}// aContent1 is an ancestor of aContent2returnaIf1Ancestor;}if(last2<0){// aContent2 is an ancestor of aContent1returnaIf2Ancestor;}// content1Ancestor != content2Ancestor, so they must be siblings with the// same parentnsINode*parent=content1Ancestor->GetParentOrShadowHostNode();#ifdef DEBUG// TODO: remove the uglyness, see bug 598468.NS_ASSERTION(gPreventAssertInCompareTreePosition||parent,"no common ancestor at all???");#endif // DEBUGif(!parent){// different documents??return0;}int32_tindex1=parent->ComputeIndexOf(content1Ancestor);int32_tindex2=parent->ComputeIndexOf(content2Ancestor);// None of the nodes are anonymous, just do a regular comparison.if(index1>=0&&index2>=0){returnindex1-index2;}// Otherwise handle pseudo-element and anonymous content ordering.//// ::marker -> ::before -> anon siblings -> regular siblings -> ::afterautoPseudoIndex=[](constnsINode*aNode,int32_taNodeIndex)->int32_t{if(aNodeIndex>=0){return1;// Not a pseudo.}if(aNode->IsContent()){if(aNode->AsContent()->IsGeneratedContentContainerForMarker()){return-2;}if(aNode->AsContent()->IsGeneratedContentContainerForBefore()){return-1;}if(aNode->AsContent()->IsGeneratedContentContainerForAfter()){return2;}}return0;};returnPseudoIndex(content1Ancestor,index1)-PseudoIndex(content2Ancestor,index2);}// staticnsIFrame*nsLayoutUtils::FillAncestors(nsIFrame*aFrame,nsIFrame*aStopAtAncestor,nsTArray<nsIFrame*>*aAncestors){while(aFrame&&aFrame!=aStopAtAncestor){aAncestors->AppendElement(aFrame);aFrame=nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);}returnaFrame;}// Return true if aFrame1 is after aFrame2staticboolIsFrameAfter(nsIFrame*aFrame1,nsIFrame*aFrame2){nsIFrame*f=aFrame2;do{f=f->GetNextSibling();if(f==aFrame1)returntrue;}while(f);returnfalse;}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIFrame*aFrame1,nsIFrame*aFrame2,int32_taIf1Ancestor,int32_taIf2Ancestor,nsIFrame*aCommonAncestor){MOZ_ASSERT(aIf1Ancestor==-1||aIf1Ancestor==0||aIf1Ancestor==1);MOZ_ASSERT(aIf2Ancestor==-1||aIf2Ancestor==0||aIf2Ancestor==1);MOZ_ASSERT(aFrame1,"aFrame1 must not be null");MOZ_ASSERT(aFrame2,"aFrame2 must not be null");AutoTArray<nsIFrame*,20>frame2Ancestors;nsIFrame*nonCommonAncestor=FillAncestors(aFrame2,aCommonAncestor,&frame2Ancestors);returnDoCompareTreePosition(aFrame1,aFrame2,frame2Ancestors,aIf1Ancestor,aIf2Ancestor,nonCommonAncestor?aCommonAncestor:nullptr);}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIFrame*aFrame1,nsIFrame*aFrame2,nsTArray<nsIFrame*>&aFrame2Ancestors,int32_taIf1Ancestor,int32_taIf2Ancestor,nsIFrame*aCommonAncestor){MOZ_ASSERT(aIf1Ancestor==-1||aIf1Ancestor==0||aIf1Ancestor==1);MOZ_ASSERT(aIf2Ancestor==-1||aIf2Ancestor==0||aIf2Ancestor==1);MOZ_ASSERT(aFrame1,"aFrame1 must not be null");MOZ_ASSERT(aFrame2,"aFrame2 must not be null");nsPresContext*presContext=aFrame1->PresContext();if(presContext!=aFrame2->PresContext()){NS_ERROR("no common ancestor at all, different documents");return0;}AutoTArray<nsIFrame*,20>frame1Ancestors;if(aCommonAncestor&&!FillAncestors(aFrame1,aCommonAncestor,&frame1Ancestors)){// We reached the root of the frame tree ... if aCommonAncestor was set,// it is wrongreturnDoCompareTreePosition(aFrame1,aFrame2,aIf1Ancestor,aIf2Ancestor,nullptr);}int32_tlast1=int32_t(frame1Ancestors.Length())-1;int32_tlast2=int32_t(aFrame2Ancestors.Length())-1;while(last1>=0&&last2>=0&&frame1Ancestors[last1]==aFrame2Ancestors[last2]){last1--;last2--;}if(last1<0){if(last2<0){NS_ASSERTION(aFrame1==aFrame2,"internal error?");return0;}// aFrame1 is an ancestor of aFrame2returnaIf1Ancestor;}if(last2<0){// aFrame2 is an ancestor of aFrame1returnaIf2Ancestor;}nsIFrame*ancestor1=frame1Ancestors[last1];nsIFrame*ancestor2=aFrame2Ancestors[last2];// Now we should be able to walk sibling chains to find which one is firstif(IsFrameAfter(ancestor2,ancestor1))return-1;if(IsFrameAfter(ancestor1,ancestor2))return1;NS_WARNING("Frames were in different child lists???");return0;}// staticnsIFrame*nsLayoutUtils::GetLastSibling(nsIFrame*aFrame){if(!aFrame){returnnullptr;}nsIFrame*next;while((next=aFrame->GetNextSibling())!=nullptr){aFrame=next;}returnaFrame;}// staticnsView*nsLayoutUtils::FindSiblingViewFor(nsView*aParentView,nsIFrame*aFrame){nsIFrame*parentViewFrame=aParentView->GetFrame();nsIContent*parentViewContent=parentViewFrame?parentViewFrame->GetContent():nullptr;for(nsView*insertBefore=aParentView->GetFirstChild();insertBefore;insertBefore=insertBefore->GetNextSibling()){nsIFrame*f=insertBefore->GetFrame();if(!f){// this view could be some anonymous view attached to a meaningful parentfor(nsView*searchView=insertBefore->GetParent();searchView;searchView=searchView->GetParent()){f=searchView->GetFrame();if(f){break;}}NS_ASSERTION(f,"Can't find a frame anywhere!");}if(!f||!aFrame->GetContent()||!f->GetContent()||CompareTreePosition(aFrame->GetContent(),f->GetContent(),parentViewContent)>0){// aFrame's content is after f's content (or we just don't know),// so put our view before f's viewreturninsertBefore;}}returnnullptr;}// staticnsIScrollableFrame*nsLayoutUtils::GetScrollableFrameFor(constnsIFrame*aScrolledFrame){nsIFrame*frame=aScrolledFrame->GetParent();nsIScrollableFrame*sf=do_QueryFrame(frame);return(sf&&sf->GetScrolledFrame()==aScrolledFrame)?sf:nullptr;}/* static */SideBitsnsLayoutUtils::GetSideBitsForFixedPositionContent(constnsIFrame*aFixedPosFrame){returnGetSideBitsAndAdjustAnchorForFixedPositionContent(nullptr,aFixedPosFrame,nullptr,nullptr);}/* static */SideBitsnsLayoutUtils::GetSideBitsAndAdjustAnchorForFixedPositionContent(constnsIFrame*aViewportFrame,constnsIFrame*aFixedPosFrame,LayerPoint*aAnchor,constRect*aAnchorRect){SideBitssides=SideBits::eNone;if(aFixedPosFrame!=aViewportFrame){constnsStylePosition*position=aFixedPosFrame->StylePosition();if(!position->mOffset.Get(eSideRight).IsAuto()){sides|=SideBits::eRight;if(!position->mOffset.Get(eSideLeft).IsAuto()){sides|=SideBits::eLeft;if(aAnchor){aAnchor->x=aAnchorRect->x+aAnchorRect->width/2.f;}}else{if(aAnchor){aAnchor->x=aAnchorRect->XMost();}}}elseif(!position->mOffset.Get(eSideLeft).IsAuto()){sides|=SideBits::eLeft;}if(!position->mOffset.Get(eSideBottom).IsAuto()){sides|=SideBits::eBottom;if(!position->mOffset.Get(eSideTop).IsAuto()){sides|=SideBits::eTop;if(aAnchor){aAnchor->y=aAnchorRect->y+aAnchorRect->height/2.f;}}else{if(aAnchor){aAnchor->y=aAnchorRect->YMost();}}}elseif(!position->mOffset.Get(eSideTop).IsAuto()){sides|=SideBits::eTop;}}returnsides;}ScrollableLayerGuid::ViewIDnsLayoutUtils::ScrollIdForRootScrollFrame(nsPresContext*aPresContext){ViewIDid=ScrollableLayerGuid::NULL_SCROLL_ID;if(nsIFrame*rootScrollFrame=aPresContext->PresShell()->GetRootScrollFrame()){if(nsIContent*content=rootScrollFrame->GetContent()){id=FindOrCreateIDFor(content);}}returnid;}// staticnsIScrollableFrame*nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame*aFrame,ScrollDirectionsaDirections){NS_ASSERTION(aFrame,"GetNearestScrollableFrameForDirection expects a non-null frame");for(nsIFrame*f=aFrame;f;f=nsLayoutUtils::GetCrossDocParentFrame(f)){nsIScrollableFrame*scrollableFrame=do_QueryFrame(f);if(scrollableFrame){ScrollDirectionsdirections=scrollableFrame->GetAvailableScrollingDirectionsForUserInputEvents();if(aDirections.contains(ScrollDirection::eVertical)){if(directions.contains(ScrollDirection::eVertical)){returnscrollableFrame;}}if(aDirections.contains(ScrollDirection::eHorizontal)){if(directions.contains(ScrollDirection::eHorizontal)){returnscrollableFrame;}}}}returnnullptr;}staticnsIFrame*GetNearestScrollableOrOverflowClipFrame(nsIFrame*aFrame,uint32_taFlags,conststd::function<bool(constnsIFrame*aCurrentFrame)>&aClipFrameCheck=nullptr){MOZ_ASSERT(aFrame,"GetNearestScrollableOrOverflowClipFrame expects a non-null frame");autoGetNextFrame=[aFlags](constnsIFrame*aFrame)->nsIFrame*{if(aFlags&nsLayoutUtils::SCROLLABLE_FOLLOW_OOF_TO_PLACEHOLDER){return(aFlags&nsLayoutUtils::SCROLLABLE_SAME_DOC)?nsLayoutUtils::GetParentOrPlaceholderFor(aFrame):nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(aFrame);}return(aFlags&nsLayoutUtils::SCROLLABLE_SAME_DOC)?aFrame->GetParent():nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);};for(nsIFrame*f=aFrame;f;f=GetNextFrame(f)){if(aClipFrameCheck&&aClipFrameCheck(f)){returnf;}if((aFlags&nsLayoutUtils::SCROLLABLE_STOP_AT_PAGE)&&f->IsPageFrame()){break;}if(nsIScrollableFrame*scrollableFrame=do_QueryFrame(f)){if(aFlags&nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE){if(scrollableFrame->WantAsyncScroll()){returnf;}}else{ScrollStylesss=scrollableFrame->GetScrollStyles();if((aFlags&nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN)||ss.mVertical!=StyleOverflow::Hidden||ss.mHorizontal!=StyleOverflow::Hidden){returnf;}}if(aFlags&nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT){PresShell*presShell=f->PresShell();if(presShell->GetRootScrollFrame()==f&&presShell->GetDocument()&&presShell->GetDocument()->IsRootDisplayDocument()){returnf;}}}if((aFlags&nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT)&&f->StyleDisplay()->mPosition==StylePositionProperty::Fixed&&nsLayoutUtils::IsReallyFixedPos(f)){returnf->PresShell()->GetRootScrollFrame();}}returnnullptr;}// staticnsIScrollableFrame*nsLayoutUtils::GetNearestScrollableFrame(nsIFrame*aFrame,uint32_taFlags){nsIFrame*found=GetNearestScrollableOrOverflowClipFrame(aFrame,aFlags);if(!found){returnnullptr;}returndo_QueryFrame(found);}// staticnsIFrame*nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame*aFrame){returnGetNearestScrollableOrOverflowClipFrame(aFrame,SCROLLABLE_SAME_DOC|SCROLLABLE_INCLUDE_HIDDEN,[](constnsIFrame*currentFrame)->bool{// In cases of SVG Inner/Outer frames it basically clips descendants// unless overflow: visible is explicitly specified.LayoutFrameTypetype=currentFrame->Type();return((type==LayoutFrameType::SVGOuterSVG||type==LayoutFrameType::SVGInnerSVG)&&(currentFrame->StyleDisplay()->mOverflowX!=StyleOverflow::Visible&¤tFrame->StyleDisplay()->mOverflowY!=StyleOverflow::Visible));});}// staticnsRectnsLayoutUtils::GetScrolledRect(nsIFrame*aScrolledFrame,constnsRect&aScrolledFrameOverflowArea,constnsSize&aScrollPortSize,StyleDirectionaDirection){WritingModewm=aScrolledFrame->GetWritingMode();// Potentially override the frame's direction to use the direction found// by ScrollFrameHelper::GetScrolledFrameDir()wm.SetDirectionFromBidiLevel(aDirection==StyleDirection::Rtl?1:0);nscoordx1=aScrolledFrameOverflowArea.x,x2=aScrolledFrameOverflowArea.XMost(),y1=aScrolledFrameOverflowArea.y,y2=aScrolledFrameOverflowArea.YMost();constboolisHorizontalWM=!wm.IsVertical();constboolisVerticalWM=wm.IsVertical();boolisInlineFlowFromTopOrLeft=!wm.IsInlineReversed();boolisBlockFlowFromTopOrLeft=isHorizontalWM||wm.IsVerticalLR();if(aScrolledFrame->IsFlexContainerFrame()){// In a flex container, the children flow (and overflow) along the flex// container's main axis and cross axis. These are analogous to the// inline/block axes, and by default they correspond exactly to those axes;// but the flex container's CSS (e.g. flex-direction: column-reverse) may// have swapped and/or reversed them, and we need to account for that here.FlexboxAxisInfoinfo(aScrolledFrame);if(info.mIsRowOriented){// The flex container's inline axis is the main axis.isInlineFlowFromTopOrLeft=isInlineFlowFromTopOrLeft==!info.mIsMainAxisReversed;isBlockFlowFromTopOrLeft=isBlockFlowFromTopOrLeft==!info.mIsCrossAxisReversed;}else{// The flex container's block axis is the main axis.isBlockFlowFromTopOrLeft=isBlockFlowFromTopOrLeft==!info.mIsMainAxisReversed;isInlineFlowFromTopOrLeft=isInlineFlowFromTopOrLeft==!info.mIsCrossAxisReversed;}}// Clamp the horizontal start-edge (x1 or x2, depending whether the logical// axis that corresponds to horizontal progresses from left-to-right or// right-to-left).if((isHorizontalWM&&isInlineFlowFromTopOrLeft)||(isVerticalWM&&isBlockFlowFromTopOrLeft)){if(x1<0){x1=0;}}else{if(x2>aScrollPortSize.width){x2=aScrollPortSize.width;}// When the scrolled frame chooses a size larger than its available width// (because its padding alone is larger than the available width), we need// to keep the start-edge of the scroll frame anchored to the start-edge of// the scrollport.// When the scrolled frame is RTL, this means moving it in our left-based// coordinate system, so we need to compensate for its extra width here by// effectively repositioning the frame.nscoordextraWidth=std::max(0,aScrolledFrame->GetSize().width-aScrollPortSize.width);x2+=extraWidth;}// Similarly, clamp the vertical start-edge (y1 or y2, depending whether the// logical axis that corresponds to vertical progresses from top-to-bottom or// buttom-to-top).if((isHorizontalWM&&isBlockFlowFromTopOrLeft)||(isVerticalWM&&isInlineFlowFromTopOrLeft)){if(y1<0){y1=0;}}else{if(y2>aScrollPortSize.height){y2=aScrollPortSize.height;}nscoordextraHeight=std::max(0,aScrolledFrame->GetSize().height-aScrollPortSize.height);y2+=extraHeight;}returnnsRect(x1,y1,x2-x1,y2-y1);}// staticboolnsLayoutUtils::HasPseudoStyle(nsIContent*aContent,ComputedStyle*aComputedStyle,PseudoStyleTypeaPseudoElement,nsPresContext*aPresContext){MOZ_ASSERT(aPresContext,"Must have a prescontext");RefPtr<ComputedStyle>pseudoContext;if(aContent){pseudoContext=aPresContext->StyleSet()->ProbePseudoElementStyle(*aContent->AsElement(),aPseudoElement,aComputedStyle);}returnpseudoContext!=nullptr;}nsPointnsLayoutUtils::GetDOMEventCoordinatesRelativeTo(Event*aDOMEvent,nsIFrame*aFrame){if(!aDOMEvent)returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);WidgetEvent*event=aDOMEvent->WidgetEventPtr();if(!event)returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);returnGetEventCoordinatesRelativeTo(event,RelativeTo{aFrame});}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(constWidgetEvent*aEvent,RelativeToaFrame){if(!aEvent||(aEvent->mClass!=eMouseEventClass&&aEvent->mClass!=eMouseScrollEventClass&&aEvent->mClass!=eWheelEventClass&&aEvent->mClass!=eDragEventClass&&aEvent->mClass!=eSimpleGestureEventClass&&aEvent->mClass!=ePointerEventClass&&aEvent->mClass!=eGestureNotifyEventClass&&aEvent->mClass!=eTouchEventClass&&aEvent->mClass!=eQueryContentEventClass))returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);returnGetEventCoordinatesRelativeTo(aEvent,aEvent->AsGUIEvent()->mRefPoint,aFrame);}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(constWidgetEvent*aEvent,constLayoutDeviceIntPoint&aPoint,RelativeToaFrame){if(!aFrame.mFrame){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsIWidget*widget=aEvent->AsGUIEvent()->mWidget;if(!widget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}returnGetEventCoordinatesRelativeTo(widget,aPoint,aFrame);}nsPointGetEventCoordinatesRelativeTo(nsIWidget*aWidget,constLayoutDeviceIntPoint&aPoint,RelativeToaFrame){constnsIFrame*frame=aFrame.mFrame;if(!frame||!aWidget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsView*view=frame->GetView();if(view){nsIWidget*frameWidget=view->GetWidget();if(frameWidget&&frameWidget==aWidget){// Special case this cause it happens a lot.// This also fixes bug 664707, events in the extra-special case of select// dropdown popups that are transformed.nsPresContext*presContext=frame->PresContext();nsPointpt(presContext->DevPixelsToAppUnits(aPoint.x),presContext->DevPixelsToAppUnits(aPoint.y));returnpt-view->ViewToWidgetOffset();}}/* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space. */constnsIFrame*rootFrame=frame;booltransformFound=false;for(constnsIFrame*f=frame;f;f=nsLayoutUtils::GetCrossDocParentFrameInProcess(f)){if(f->IsTransformed()||ViewportUtils::IsZoomedContentRoot(f)){transformFound=true;}rootFrame=f;}nsView*rootView=rootFrame->GetView();if(!rootView){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsPointwidgetToView=nsLayoutUtils::TranslateWidgetToView(rootFrame->PresContext(),aWidget,aPoint,rootView);if(widgetToView==nsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE)){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}// Convert from root document app units to app units of the document aFrame// is in.int32_trootAPD=rootFrame->PresContext()->AppUnitsPerDevPixel();int32_tlocalAPD=frame->PresContext()->AppUnitsPerDevPixel();widgetToView=widgetToView.ScaleToOtherAppUnits(rootAPD,localAPD);/* If we encountered a transform, we can't do simple arithmetic to figure * out how to convert back to aFrame's coordinates and must use the CTM. */if(transformFound||SVGUtils::IsInSVGTextSubtree(frame)){returnnsLayoutUtils::TransformRootPointToFrame(ViewportType::Visual,aFrame,widgetToView);}/* Otherwise, all coordinate systems are translations of one another, * so we can just subtract out the difference. */returnwidgetToView-frame->GetOffsetToCrossDoc(rootFrame);}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget*aWidget,constLayoutDeviceIntPoint&aPoint,RelativeToaFrame){nsPointresult=::GetEventCoordinatesRelativeTo(aWidget,aPoint,aFrame);if(aFrame.mViewportType==ViewportType::Layout&&aFrame.mFrame&&aFrame.mFrame->Type()==LayoutFrameType::Viewport&&aFrame.mFrame->PresContext()->IsRootContentDocumentCrossProcess()){result=ViewportUtils::VisualToLayout(result,aFrame.mFrame->PresShell());}returnresult;}nsIFrame*nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext*aPresContext,constWidgetEvent*aEvent){#ifdef MOZ_XULnsXULPopupManager*pm=nsXULPopupManager::GetInstance();if(!pm){returnnullptr;}nsTArray<nsIFrame*>popups;pm->GetVisiblePopups(popups);uint32_ti;// Search from top to bottomfor(i=0;i<popups.Length();i++){nsIFrame*popup=popups[i];if(popup->PresContext()->GetRootPresContext()==aPresContext&&popup->ScrollableOverflowRect().Contains(GetEventCoordinatesRelativeTo(aEvent,RelativeTo{popup}))){returnpopup;}}#endifreturnnullptr;}voidnsLayoutUtils::GetContainerAndOffsetAtEvent(PresShell*aPresShell,constWidgetEvent*aEvent,nsIContent**aContainer,int32_t*aOffset){MOZ_ASSERT(aContainer||aOffset);if(aContainer){*aContainer=nullptr;}if(aOffset){*aOffset=0;}if(!aPresShell){return;}aPresShell->FlushPendingNotifications(FlushType::Layout);RefPtr<nsPresContext>presContext=aPresShell->GetPresContext();if(!presContext){return;}nsIFrame*targetFrame=presContext->EventStateManager()->GetEventTarget();if(!targetFrame){return;}WidgetEvent*openingEvent=nullptr;// For popupshowing events, redirect via the original mouse event// that triggered the popup to open.if(aEvent->mMessage==eXULPopupShowing){if(auto*pm=nsXULPopupManager::GetInstance()){if(Event*openingPopupEvent=pm->GetOpeningPopupEvent()){openingEvent=openingPopupEvent->WidgetEventPtr();}}}nsPointpoint=nsLayoutUtils::GetEventCoordinatesRelativeTo(openingEvent?openingEvent:aEvent,RelativeTo{targetFrame});if(aContainer){// TODO: This result may be useful to change to Selection. However, this// may return improper node (e.g., native anonymous node) for the// Selection. Perhaps, this should take Selection optionally and// if it's specified, needs to check if it's proper for the// Selection.nsCOMPtr<nsIContent>container=targetFrame->GetContentOffsetsFromPoint(point).content;if(container&&(!container->ChromeOnlyAccess()||nsContentUtils::CanAccessNativeAnon())){container.forget(aContainer);}}if(aOffset){*aOffset=targetFrame->GetContentOffsetsFromPoint(point).offset;}}voidnsLayoutUtils::ConstrainToCoordValues(float&aStart,float&aSize){MOZ_ASSERT(aSize>=0);// Here we try to make sure that the resulting nsRect will continue to cover// as much of the area that was covered by the original gfx Rect as possible.// We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since// nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this// range:floatend=aStart+aSize;aStart=clamped(aStart,float(nscoord_MIN),float(nscoord_MAX));end=clamped(end,float(nscoord_MIN),float(nscoord_MAX));aSize=end-aStart;// We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()// can't return a value greater than nscoord_MAX. If aSize is greater than// nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect// centered:if(aSize>float(nscoord_MAX)){floatexcess=aSize-float(nscoord_MAX);excess/=2;aStart+=excess;aSize=(float)nscoord_MAX;}}/** * Given a gfxFloat, constrains its value to be between nscoord_MIN and * nscoord_MAX. * * @param aVal The value to constrain (in/out) */staticvoidConstrainToCoordValues(gfxFloat&aVal){if(aVal<=nscoord_MIN)aVal=nscoord_MIN;elseif(aVal>=nscoord_MAX)aVal=nscoord_MAX;}voidnsLayoutUtils::ConstrainToCoordValues(gfxFloat&aStart,gfxFloat&aSize){gfxFloatmax=aStart+aSize;// Clamp the end points to within nscoord range::ConstrainToCoordValues(aStart);::ConstrainToCoordValues(max);aSize=max-aStart;// If the width if still greater than the max nscoord, then bring both// endpoints in by the same amount until it fits.if(aSize>nscoord_MAX){gfxFloatexcess=aSize-nscoord_MAX;excess/=2;aStart+=excess;aSize=nscoord_MAX;}elseif(aSize<nscoord_MIN){gfxFloatexcess=aSize-nscoord_MIN;excess/=2;aStart-=excess;aSize=nscoord_MIN;}}nsRegionnsLayoutUtils::RoundedRectIntersectRect(constnsRect&aRoundedRect,constnscoordaRadii[8],constnsRect&aContainedRect){// rectFullHeight and rectFullWidth together will approximately contain// the total area of the frame minus the rounded corners.nsRectrectFullHeight=aRoundedRect;nscoordxDiff=std::max(aRadii[eCornerTopLeftX],aRadii[eCornerBottomLeftX]);rectFullHeight.x+=xDiff;rectFullHeight.width-=std::max(aRadii[eCornerTopRightX],aRadii[eCornerBottomRightX])+xDiff;nsRectr1;r1.IntersectRect(rectFullHeight,aContainedRect);nsRectrectFullWidth=aRoundedRect;nscoordyDiff=std::max(aRadii[eCornerTopLeftY],aRadii[eCornerTopRightY]);rectFullWidth.y+=yDiff;rectFullWidth.height-=std::max(aRadii[eCornerBottomLeftY],aRadii[eCornerBottomRightY])+yDiff;nsRectr2;r2.IntersectRect(rectFullWidth,aContainedRect);nsRegionresult;result.Or(r1,r2);returnresult;}nsIntRegionnsLayoutUtils::RoundedRectIntersectIntRect(constnsIntRect&aRoundedRect,constRectCornerRadii&aCornerRadii,constnsIntRect&aContainedRect){// rectFullHeight and rectFullWidth together will approximately contain// the total area of the frame minus the rounded corners.nsIntRectrectFullHeight=aRoundedRect;uint32_txDiff=std::max(aCornerRadii.TopLeft().width,aCornerRadii.BottomLeft().width);rectFullHeight.x+=xDiff;rectFullHeight.width-=std::max(aCornerRadii.TopRight().width,aCornerRadii.BottomRight().width)+xDiff;nsIntRectr1;r1.IntersectRect(rectFullHeight,aContainedRect);nsIntRectrectFullWidth=aRoundedRect;uint32_tyDiff=std::max(aCornerRadii.TopLeft().height,aCornerRadii.TopRight().height);rectFullWidth.y+=yDiff;rectFullWidth.height-=std::max(aCornerRadii.BottomLeft().height,aCornerRadii.BottomRight().height)+yDiff;nsIntRectr2;r2.IntersectRect(rectFullWidth,aContainedRect);nsIntRegionresult;result.Or(r1,r2);returnresult;}// Helper for RoundedRectIntersectsRect.staticboolCheckCorner(nscoordaXOffset,nscoordaYOffset,nscoordaXRadius,nscoordaYRadius){MOZ_ASSERT(aXOffset>0&&aYOffset>0,"must not pass nonpositives to CheckCorner");MOZ_ASSERT(aXRadius>=0&&aYRadius>=0,"must not pass negatives to CheckCorner");// Avoid floating point math unless we're either (1) within the// quarter-ellipse area at the rounded corner or (2) outside the// rounding.if(aXOffset>=aXRadius||aYOffset>=aYRadius)returntrue;// Convert coordinates to a unit circle with (0,0) as the center of// curvature, and see if we're inside the circle or outside.floatscaledX=float(aXRadius-aXOffset)/float(aXRadius);floatscaledY=float(aYRadius-aYOffset)/float(aYRadius);returnscaledX*scaledX+scaledY*scaledY<1.0f;}boolnsLayoutUtils::RoundedRectIntersectsRect(constnsRect&aRoundedRect,constnscoordaRadii[8],constnsRect&aTestRect){if(!aTestRect.Intersects(aRoundedRect))returnfalse;// distances from this edge of aRoundedRect to opposite edge of aTestRect,// which we know are positive due to the Intersects check above.nsMargininsets;insets.top=aTestRect.YMost()-aRoundedRect.y;insets.right=aRoundedRect.XMost()-aTestRect.x;insets.bottom=aRoundedRect.YMost()-aTestRect.y;insets.left=aTestRect.XMost()-aRoundedRect.x;// Check whether the bottom-right corner of aTestRect is inside the// top left corner of aBounds when rounded by aRadii, etc. If any// corner is not, then fail; otherwise succeed.returnCheckCorner(insets.left,insets.top,aRadii[eCornerTopLeftX],aRadii[eCornerTopLeftY])&&CheckCorner(insets.right,insets.top,aRadii[eCornerTopRightX],aRadii[eCornerTopRightY])&&CheckCorner(insets.right,insets.bottom,aRadii[eCornerBottomRightX],aRadii[eCornerBottomRightY])&&CheckCorner(insets.left,insets.bottom,aRadii[eCornerBottomLeftX],aRadii[eCornerBottomLeftY]);}nsRectnsLayoutUtils::MatrixTransformRect(constnsRect&aBounds,constMatrix4x4&aMatrix,floataFactor){RectDoubleimage=RectDouble(NSAppUnitsToDoublePixels(aBounds.x,aFactor),NSAppUnitsToDoublePixels(aBounds.y,aFactor),NSAppUnitsToDoublePixels(aBounds.width,aFactor),NSAppUnitsToDoublePixels(aBounds.height,aFactor));RectDoublemaxBounds=RectDouble(double(nscoord_MIN)/aFactor*0.5,double(nscoord_MIN)/aFactor*0.5,double(nscoord_MAX)/aFactor,double(nscoord_MAX)/aFactor);image=aMatrix.TransformAndClipBounds(image,maxBounds);returnRoundGfxRectToAppRect(ThebesRect(image),aFactor);}nsRectnsLayoutUtils::MatrixTransformRect(constnsRect&aBounds,constMatrix4x4Flagged&aMatrix,floataFactor){RectDoubleimage=RectDouble(NSAppUnitsToDoublePixels(aBounds.x,aFactor),NSAppUnitsToDoublePixels(aBounds.y,aFactor),NSAppUnitsToDoublePixels(aBounds.width,aFactor),NSAppUnitsToDoublePixels(aBounds.height,aFactor));RectDoublemaxBounds=RectDouble(double(nscoord_MIN)/aFactor*0.5,double(nscoord_MIN)/aFactor*0.5,double(nscoord_MAX)/aFactor,double(nscoord_MAX)/aFactor);image=aMatrix.TransformAndClipBounds(image,maxBounds);returnRoundGfxRectToAppRect(ThebesRect(image),aFactor);}nsPointnsLayoutUtils::MatrixTransformPoint(constnsPoint&aPoint,constMatrix4x4&aMatrix,floataFactor){gfxPointimage=gfxPoint(NSAppUnitsToFloatPixels(aPoint.x,aFactor),NSAppUnitsToFloatPixels(aPoint.y,aFactor));image=aMatrix.TransformPoint(image);returnnsPoint(NSFloatPixelsToAppUnits(float(image.x),aFactor),NSFloatPixelsToAppUnits(float(image.y),aFactor));}voidnsLayoutUtils::PostTranslate(Matrix4x4&aTransform,constnsPoint&aOrigin,floataAppUnitsPerPixel,boolaRounded){Point3DgfxOrigin=Point3D(NSAppUnitsToFloatPixels(aOrigin.x,aAppUnitsPerPixel),NSAppUnitsToFloatPixels(aOrigin.y,aAppUnitsPerPixel),0.0f);if(aRounded){gfxOrigin.x=NS_round(gfxOrigin.x);gfxOrigin.y=NS_round(gfxOrigin.y);}aTransform.PostTranslate(gfxOrigin);}boolnsLayoutUtils::ShouldSnapToGrid(constnsIFrame*aFrame){return!aFrame||!aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)||aFrame->IsSVGOuterSVGAnonChildFrame();}Matrix4x4FlaggednsLayoutUtils::GetTransformToAncestor(RelativeToaFrame,RelativeToaAncestor,uint32_taFlags,nsIFrame**aOutAncestor){nsIFrame*parent;Matrix4x4Flaggedctm;// Make sure we don't get an invalid combination of source and destination// RelativeTo values.MOZ_ASSERT(!(aFrame.mViewportType==ViewportType::Visual&&aAncestor.mViewportType==ViewportType::Layout));if(aFrame==aAncestor){returnctm;}ctm=aFrame.mFrame->GetTransformMatrix(aFrame.mViewportType,aAncestor,&parent,aFlags);if(!aFrame.mFrame->Combines3DTransformWithAncestors()){ctm.ProjectTo2D();}while(parent&&parent!=aAncestor.mFrame&&(!(aFlags&nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT)||(!parent->IsStackingContext()&&!DisplayPortUtils::FrameHasDisplayPort(parent)))){nsIFrame*cur=parent;ctm=ctm*cur->GetTransformMatrix(aFrame.mViewportType,aAncestor,&parent,aFlags);if(!cur->Combines3DTransformWithAncestors()){ctm.ProjectTo2D();}}if(aOutAncestor){*aOutAncestor=parent;}returnctm;}gfxSizensLayoutUtils::GetTransformToAncestorScale(constnsIFrame*aFrame){Matrix4x4Flaggedtransform=GetTransformToAncestor(RelativeTo{aFrame},RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});Matrixtransform2D;if(transform.Is2D(&transform2D)){returnThebesMatrix(transform2D).ScaleFactors();}returngfxSize(1,1);}staticMatrix4x4FlaggedGetTransformToAncestorExcludingAnimated(nsIFrame*aFrame,constnsIFrame*aAncestor){nsIFrame*parent;Matrix4x4Flaggedctm;if(aFrame==aAncestor){returnctm;}if(ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame)){returnctm;}ctm=aFrame->GetTransformMatrix(ViewportType::Layout,RelativeTo{aAncestor},&parent);while(parent&&parent!=aAncestor){if(ActiveLayerTracker::IsScaleSubjectToAnimation(parent)){returnMatrix4x4Flagged();}if(!parent->Extend3DContext()){ctm.ProjectTo2D();}ctm=ctm*parent->GetTransformMatrix(ViewportType::Layout,RelativeTo{aAncestor},&parent);}returnctm;}gfxSizensLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(nsIFrame*aFrame){Matrix4x4Flaggedtransform=GetTransformToAncestorExcludingAnimated(aFrame,nsLayoutUtils::GetDisplayRootFrame(aFrame));Matrixtransform2D;if(transform.Is2D(&transform2D)){returnThebesMatrix(transform2D).ScaleFactors();}returngfxSize(1,1);}constnsIFrame*nsLayoutUtils::FindNearestCommonAncestorFrame(constnsIFrame*aFrame1,constnsIFrame*aFrame2){AutoTArray<constnsIFrame*,100>ancestors1;AutoTArray<constnsIFrame*,100>ancestors2;constnsIFrame*commonAncestor=nullptr;if(aFrame1->PresContext()==aFrame2->PresContext()){commonAncestor=aFrame1->PresShell()->GetRootFrame();}for(constnsIFrame*f=aFrame1;f!=commonAncestor;f=nsLayoutUtils::GetCrossDocParentFrameInProcess(f)){ancestors1.AppendElement(f);}for(constnsIFrame*f=aFrame2;f!=commonAncestor;f=nsLayoutUtils::GetCrossDocParentFrameInProcess(f)){ancestors2.AppendElement(f);}uint32_tminLengths=std::min(ancestors1.Length(),ancestors2.Length());for(uint32_ti=1;i<=minLengths;++i){if(ancestors1[ancestors1.Length()-i]==ancestors2[ancestors2.Length()-i]){commonAncestor=ancestors1[ancestors1.Length()-i];}else{break;}}returncommonAncestor;}constnsIFrame*nsLayoutUtils::FindNearestCommonAncestorFrameWithinBlock(constnsTextFrame*aFrame1,constnsTextFrame*aFrame2){MOZ_ASSERT(aFrame1);MOZ_ASSERT(aFrame2);constnsIFrame*f1=aFrame1;constnsIFrame*f2=aFrame2;intn1=1;intn2=1;for(autof=f1->GetParent();;){NS_ASSERTION(f,"All text frames should have a block ancestor");if(!f){returnnullptr;}if(f->IsBlockFrameOrSubclass()){break;}++n1;f=f->GetParent();}for(autof=f2->GetParent();;){NS_ASSERTION(f,"All text frames should have a block ancestor");if(!f){returnnullptr;}if(f->IsBlockFrameOrSubclass()){break;}++n2;f=f->GetParent();}if(n1>n2){std::swap(n1,n2);std::swap(f1,f2);}while(n2>n1){f2=f2->GetParent();--n2;}while(n2>=0){if(f1==f2){returnf1;}f1=f1->GetParent();f2=f2->GetParent();--n2;}returnnullptr;}boolnsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(StyleAppearanceaAppearance){returnaAppearance==StyleAppearance::NumberInput||aAppearance==StyleAppearance::Button||aAppearance==StyleAppearance::Textfield||aAppearance==StyleAppearance::Textarea||aAppearance==StyleAppearance::Listbox||aAppearance==StyleAppearance::Menulist||aAppearance==StyleAppearance::MenulistButton;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformPoints(nsIFrame*aFromFrame,nsIFrame*aToFrame,uint32_taPointCount,CSSPoint*aPoints){constnsIFrame*nearestCommonAncestor=FindNearestCommonAncestorFrame(aFromFrame,aToFrame);if(!nearestCommonAncestor){returnNO_COMMON_ANCESTOR;}Matrix4x4FlaggeddownToDest=GetTransformToAncestor(RelativeTo{aToFrame},RelativeTo{nearestCommonAncestor});if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4FlaggedupToAncestor=GetTransformToAncestor(RelativeTo{aFromFrame},RelativeTo{nearestCommonAncestor});CSSToLayoutDeviceScaledevPixelsPerCSSPixelFromFrame=aFromFrame->PresContext()->CSSToDevPixelScale();CSSToLayoutDeviceScaledevPixelsPerCSSPixelToFrame=aToFrame->PresContext()->CSSToDevPixelScale();for(uint32_ti=0;i<aPointCount;++i){LayoutDevicePointdevPixels=aPoints[i]*devPixelsPerCSSPixelFromFrame;// What should the behaviour be if some of the points aren't invertible// and others are? Just assume all points are for now.PointtoDevPixels=downToDest.ProjectPoint((upToAncestor.TransformPoint(Point(devPixels.x,devPixels.y)))).As2DPoint();// Divide here so that when the devPixelsPerCSSPixels are the same, we get// the correct answer instead of some inaccuracy multiplying a number by its// reciprocal.aPoints[i]=LayoutDevicePoint(toDevPixels.x,toDevPixels.y)/devPixelsPerCSSPixelToFrame;}returnTRANSFORM_SUCCEEDED;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformPoint(RelativeToaFromFrame,RelativeToaToFrame,nsPoint&aPoint){// Conceptually, {ViewportFrame, Visual} is an ancestor of// {ViewportFrame, Layout}, so factor that into the nearest ancestor// computation.RelativeTonearestCommonAncestor{FindNearestCommonAncestorFrame(aFromFrame.mFrame,aToFrame.mFrame),aFromFrame.mViewportType==ViewportType::Visual||aToFrame.mViewportType==ViewportType::Visual?ViewportType::Visual:ViewportType::Layout};if(!nearestCommonAncestor.mFrame){returnNO_COMMON_ANCESTOR;}Matrix4x4FlaggeddownToDest=GetTransformToAncestor(aToFrame,nearestCommonAncestor);if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4FlaggedupToAncestor=GetTransformToAncestor(aFromFrame,nearestCommonAncestor);floatdevPixelsPerAppUnitFromFrame=1.0f/aFromFrame.mFrame->PresContext()->AppUnitsPerDevPixel();floatdevPixelsPerAppUnitToFrame=1.0f/aToFrame.mFrame->PresContext()->AppUnitsPerDevPixel();Point4DtoDevPixels=downToDest.ProjectPoint(upToAncestor.TransformPoint(Point(aPoint.x*devPixelsPerAppUnitFromFrame,aPoint.y*devPixelsPerAppUnitFromFrame)));if(!toDevPixels.HasPositiveWCoord()){// Not strictly true, but we failed to get a valid point in this// coordinate space.returnNONINVERTIBLE_TRANSFORM;}aPoint.x=NSToCoordRound(toDevPixels.x/devPixelsPerAppUnitToFrame);aPoint.y=NSToCoordRound(toDevPixels.y/devPixelsPerAppUnitToFrame);returnTRANSFORM_SUCCEEDED;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformRect(constnsIFrame*aFromFrame,constnsIFrame*aToFrame,nsRect&aRect){constnsIFrame*nearestCommonAncestor=FindNearestCommonAncestorFrame(aFromFrame,aToFrame);if(!nearestCommonAncestor){returnNO_COMMON_ANCESTOR;}Matrix4x4FlaggeddownToDest=GetTransformToAncestor(RelativeTo{aToFrame},RelativeTo{nearestCommonAncestor});if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4FlaggedupToAncestor=GetTransformToAncestor(RelativeTo{aFromFrame},RelativeTo{nearestCommonAncestor});floatdevPixelsPerAppUnitFromFrame=1.0f/aFromFrame->PresContext()->AppUnitsPerDevPixel();floatdevPixelsPerAppUnitToFrame=1.0f/aToFrame->PresContext()->AppUnitsPerDevPixel();gfx::RecttoDevPixels=downToDest.ProjectRectBounds(upToAncestor.ProjectRectBounds(gfx::Rect(aRect.x*devPixelsPerAppUnitFromFrame,aRect.y*devPixelsPerAppUnitFromFrame,aRect.width*devPixelsPerAppUnitFromFrame,aRect.height*devPixelsPerAppUnitFromFrame),Rect(-std::numeric_limits<Float>::max()*0.5f,-std::numeric_limits<Float>::max()*0.5f,std::numeric_limits<Float>::max(),std::numeric_limits<Float>::max())),Rect(-std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame*0.5f,-std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame*0.5f,std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame,std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame));aRect.x=NSToCoordRound(toDevPixels.x/devPixelsPerAppUnitToFrame);aRect.y=NSToCoordRound(toDevPixels.y/devPixelsPerAppUnitToFrame);aRect.width=NSToCoordRound(toDevPixels.width/devPixelsPerAppUnitToFrame);aRect.height=NSToCoordRound(toDevPixels.height/devPixelsPerAppUnitToFrame);returnTRANSFORM_SUCCEEDED;}nsRectnsLayoutUtils::GetRectRelativeToFrame(Element*aElement,nsIFrame*aFrame){if(!aElement||!aFrame){returnnsRect();}nsIFrame*frame=aElement->GetPrimaryFrame();if(!frame){returnnsRect();}nsRectrect=frame->GetRectRelativeToSelf();nsLayoutUtils::TransformResultrv=nsLayoutUtils::TransformRect(frame,aFrame,rect);if(rv!=nsLayoutUtils::TRANSFORM_SUCCEEDED){returnnsRect();}returnrect;}boolnsLayoutUtils::ContainsPoint(constnsRect&aRect,constnsPoint&aPoint,nscoordaInflateSize){nsRectrect=aRect;rect.Inflate(aInflateSize);returnrect.Contains(aPoint);}nsRectnsLayoutUtils::ClampRectToScrollFrames(nsIFrame*aFrame,constnsRect&aRect){nsIFrame*closestScrollFrame=nsLayoutUtils::GetClosestFrameOfType(aFrame,LayoutFrameType::Scroll);nsRectresultRect=aRect;while(closestScrollFrame){nsIScrollableFrame*sf=do_QueryFrame(closestScrollFrame);nsRectscrollPortRect=sf->GetScrollPortRect();nsLayoutUtils::TransformRect(closestScrollFrame,aFrame,scrollPortRect);resultRect=resultRect.Intersect(scrollPortRect);// Check whether aRect is visible in the scroll frame or not.if(resultRect.IsEmpty()){break;}// Get next ancestor scroll frame.closestScrollFrame=nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),LayoutFrameType::Scroll);}returnresultRect;}boolnsLayoutUtils::GetLayerTransformForFrame(nsIFrame*aFrame,Matrix4x4Flagged*aTransform){// FIXME/bug 796690: we can sometimes compute a transform in these// cases, it just increases complexity considerably. Punt for now.if(aFrame->Extend3DContext()||aFrame->GetTransformGetter()){returnfalse;}nsIFrame*root=nsLayoutUtils::GetDisplayRootFrame(aFrame);if(root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)){// Content may have been invalidated, so we can't reliably compute// the "layer transform" in general.returnfalse;}// If the caller doesn't care about the value, early-return to skip// overhead below.if(!aTransform){returntrue;}nsDisplayListBuilderbuilder(root,nsDisplayListBuilderMode::TransformComputation,false/*don't build caret*/);builder.BeginFrame();nsDisplayListlist;nsDisplayTransform*item=MakeDisplayItem<nsDisplayTransform>(&builder,aFrame,&list,nsRect());MOZ_ASSERT(item);*aTransform=item->GetTransform();item->Destroy(&builder);builder.EndFrame();returntrue;}staticboolTransformGfxPointFromAncestor(RelativeToaFrame,constPoint&aPoint,RelativeToaAncestor,Point*aOut){Matrix4x4Flaggedctm=nsLayoutUtils::GetTransformToAncestor(aFrame,aAncestor);ctm.Invert();Point4Dpoint=ctm.ProjectPoint(aPoint);if(!point.HasPositiveWCoord()){returnfalse;}*aOut=point.As2DPoint();returntrue;}staticRectTransformGfxRectToAncestor(RelativeToaFrame,constRect&aRect,RelativeToaAncestor,bool*aPreservesAxisAlignedRectangles=nullptr,Maybe<Matrix4x4Flagged>*aMatrixCache=nullptr,boolaStopAtStackingContextAndDisplayPortAndOOFFrame=false,nsIFrame**aOutAncestor=nullptr){Matrix4x4Flaggedctm;if(aMatrixCache&&*aMatrixCache){// We are given a matrix to use, so use itctm=aMatrixCache->value();}else{// Else, compute ituint32_tflags=0;if(aStopAtStackingContextAndDisplayPortAndOOFFrame){flags|=nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT;}ctm=nsLayoutUtils::GetTransformToAncestor(aFrame,aAncestor,flags,aOutAncestor);if(aMatrixCache){// and put it in the cache, if provided*aMatrixCache=Some(ctm);}}// Fill out the axis-alignment flagif(aPreservesAxisAlignedRectangles){Matrixmatrix2d;*aPreservesAxisAlignedRectangles=ctm.Is2D(&matrix2d)&&matrix2d.PreservesAxisAlignedRectangles();}constnsIFrame*ancestor=aOutAncestor?*aOutAncestor:aAncestor.mFrame;floatfactor=ancestor->PresContext()->AppUnitsPerDevPixel();RectmaxBounds=Rect(float(nscoord_MIN)/factor*0.5,float(nscoord_MIN)/factor*0.5,float(nscoord_MAX)/factor,float(nscoord_MAX)/factor);returnctm.TransformAndClipBounds(aRect,maxBounds);}staticSVGTextFrame*GetContainingSVGTextFrame(constnsIFrame*aFrame){if(!SVGUtils::IsInSVGTextSubtree(aFrame)){returnnullptr;}returnstatic_cast<SVGTextFrame*>(nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),LayoutFrameType::SVGText));}nsPointnsLayoutUtils::TransformAncestorPointToFrame(RelativeToaFrame,constnsPoint&aPoint,RelativeToaAncestor){SVGTextFrame*text=GetContainingSVGTextFrame(aFrame.mFrame);floatfactor=aFrame.mFrame->PresContext()->AppUnitsPerDevPixel();Pointresult(NSAppUnitsToFloatPixels(aPoint.x,factor),NSAppUnitsToFloatPixels(aPoint.y,factor));if(!TransformGfxPointFromAncestor(text?RelativeTo{text,aFrame.mViewportType}:aFrame,result,aAncestor,&result)){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}if(text){result=text->TransformFramePointToTextChild(result,aFrame.mFrame);}returnnsPoint(NSFloatPixelsToAppUnits(float(result.x),factor),NSFloatPixelsToAppUnits(float(result.y),factor));}nsRectnsLayoutUtils::TransformFrameRectToAncestor(constnsIFrame*aFrame,constnsRect&aRect,RelativeToaAncestor,bool*aPreservesAxisAlignedRectangles/* = nullptr */,Maybe<Matrix4x4Flagged>*aMatrixCache/* = nullptr */,boolaStopAtStackingContextAndDisplayPortAndOOFFrame/* = false */,nsIFrame**aOutAncestor/* = nullptr */){MOZ_ASSERT(IsAncestorFrameCrossDocInProcess(aAncestor.mFrame,aFrame),"Fix the caller");SVGTextFrame*text=GetContainingSVGTextFrame(aFrame);floatsrcAppUnitsPerDevPixel=aFrame->PresContext()->AppUnitsPerDevPixel();Rectresult;if(text){result=ToRect(text->TransformFrameRectFromTextChild(aRect,aFrame));// |result| from TransformFrameRectFromTextChild() is in user space (css// pixel), should convert to device pixelfloatdevPixelPerCSSPixel=float(AppUnitsPerCSSPixel())/srcAppUnitsPerDevPixel;result.Scale(devPixelPerCSSPixel);result=TransformGfxRectToAncestor(RelativeTo{text},result,aAncestor,nullptr,aMatrixCache,aStopAtStackingContextAndDisplayPortAndOOFFrame,aOutAncestor);// TransformFrameRectFromTextChild could involve any kind of transform, we// could drill down into it to get an answer out of it but we don't yet.if(aPreservesAxisAlignedRectangles)*aPreservesAxisAlignedRectangles=false;}else{result=Rect(NSAppUnitsToFloatPixels(aRect.x,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.y,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.width,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.height,srcAppUnitsPerDevPixel));result=TransformGfxRectToAncestor(RelativeTo{aFrame},result,aAncestor,aPreservesAxisAlignedRectangles,aMatrixCache,aStopAtStackingContextAndDisplayPortAndOOFFrame,aOutAncestor);}floatdestAppUnitsPerDevPixel=aAncestor.mFrame->PresContext()->AppUnitsPerDevPixel();returnnsRect(NSFloatPixelsToAppUnits(float(result.x),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.y),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.width),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.height),destAppUnitsPerDevPixel));}staticLayoutDeviceIntPointGetWidgetOffset(nsIWidget*aWidget,nsIWidget*&aRootWidget){LayoutDeviceIntPointoffset(0,0);while(aWidget->WindowType()==eWindowType_child){nsIWidget*parent=aWidget->GetParent();if(!parent){break;}LayoutDeviceIntRectbounds=aWidget->GetBounds();offset+=bounds.TopLeft();aWidget=parent;}aRootWidget=aWidget;returnoffset;}LayoutDeviceIntPointnsLayoutUtils::WidgetToWidgetOffset(nsIWidget*aFrom,nsIWidget*aTo){nsIWidget*fromRoot;LayoutDeviceIntPointfromOffset=GetWidgetOffset(aFrom,fromRoot);nsIWidget*toRoot;LayoutDeviceIntPointtoOffset=GetWidgetOffset(aTo,toRoot);if(fromRoot==toRoot){returnfromOffset-toOffset;}returnaFrom->WidgetToScreenOffset()-aTo->WidgetToScreenOffset();}nsPointnsLayoutUtils::TranslateWidgetToView(nsPresContext*aPresContext,nsIWidget*aWidget,constLayoutDeviceIntPoint&aPt,nsView*aView){nsPointviewOffset;nsIWidget*viewWidget=aView->GetNearestWidget(&viewOffset);if(!viewWidget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}LayoutDeviceIntPointwidgetPoint=aPt+WidgetToWidgetOffset(aWidget,viewWidget);nsPointwidgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),aPresContext->DevPixelsToAppUnits(widgetPoint.y));returnwidgetAppUnits-viewOffset;}LayoutDeviceIntPointnsLayoutUtils::TranslateViewToWidget(nsPresContext*aPresContext,nsView*aView,nsPointaPt,ViewportTypeaViewportType,nsIWidget*aWidget){nsPointviewOffset;nsIWidget*viewWidget=aView->GetNearestWidget(&viewOffset);if(!viewWidget){returnLayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsPointpt=(aPt+viewOffset);// The target coordinates are visual, so perform a layout-to-visual// conversion if the incoming coordinates are layout.if(aViewportType==ViewportType::Layout&&aPresContext->GetPresShell()){pt=ViewportUtils::LayoutToVisual(pt,aPresContext->GetPresShell());}LayoutDeviceIntPointrelativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x),aPresContext->AppUnitsToDevPixels(pt.y));returnrelativeToViewWidget+WidgetToWidgetOffset(viewWidget,aWidget);}// Combine aNewBreakType with aOrigBreakType, but limit the break types// to StyleClear::Left, Right, Both.StyleClearnsLayoutUtils::CombineBreakType(StyleClearaOrigBreakType,StyleClearaNewBreakType){StyleClearbreakType=aOrigBreakType;switch(breakType){caseStyleClear::Left:if(StyleClear::Right==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=StyleClear::Both;}break;caseStyleClear::Right:if(StyleClear::Left==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=StyleClear::Both;}break;caseStyleClear::None:if(StyleClear::Left==aNewBreakType||StyleClear::Right==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=aNewBreakType;}break;default:break;}returnbreakType;}#ifdef MOZ_DUMP_PAINTING# include <stdio.h>staticboolgDumpEventList=false;// nsLayoutUtils::PaintFrame() can call itself recursively, so rather than// maintaining a single paint count, we need a stack.StaticAutoPtr<nsTArray<int>>gPaintCountStack;structAutoNestedPaintCount{AutoNestedPaintCount(){gPaintCountStack->AppendElement(0);}~AutoNestedPaintCount(){gPaintCountStack->RemoveLastElement();}};#endifnsIFrame*nsLayoutUtils::GetFrameForPoint(RelativeToaRelativeTo,nsPointaPt,constFrameForPointOptions&aOptions){AUTO_PROFILER_LABEL("nsLayoutUtils::GetFrameForPoint",LAYOUT);nsresultrv;AutoTArray<nsIFrame*,8>outFrames;rv=GetFramesForArea(aRelativeTo,nsRect(aPt,nsSize(1,1)),outFrames,aOptions);NS_ENSURE_SUCCESS(rv,nullptr);returnoutFrames.Length()?outFrames.ElementAt(0):nullptr;}nsresultnsLayoutUtils::GetFramesForArea(RelativeToaRelativeTo,constnsRect&aRect,nsTArray<nsIFrame*>&aOutFrames,constFrameForPointOptions&aOptions){AUTO_PROFILER_LABEL("nsLayoutUtils::GetFramesForArea",LAYOUT);nsIFrame*frame=const_cast<nsIFrame*>(aRelativeTo.mFrame);nsDisplayListBuilderbuilder(frame,nsDisplayListBuilderMode::EventDelivery,false);builder.BeginFrame();nsDisplayListlist;if(aOptions.mBits.contains(FrameForPointOption::IgnorePaintSuppression)){builder.IgnorePaintSuppression();}if(aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame)){nsIFrame*rootScrollFrame=frame->PresShell()->GetRootScrollFrame();if(rootScrollFrame){builder.SetIgnoreScrollFrame(rootScrollFrame);}}if(aRelativeTo.mViewportType==ViewportType::Layout){builder.SetIsRelativeToLayoutViewport();}if(aOptions.mBits.contains(FrameForPointOption::IgnoreCrossDoc)){builder.SetDescendIntoSubdocuments(false);}if(aOptions.mBits.contains(FrameForPointOption::OnlyVisible)){builder.SetHitTestIsForVisibility(aOptions.mVisibleThreshold);}builder.EnterPresShell(frame);builder.SetVisibleRect(aRect);builder.SetDirtyRect(aRect);frame->BuildDisplayListForStackingContext(&builder,&list);builder.LeavePresShell(frame,nullptr);#ifdef MOZ_DUMP_PAINTINGif(gDumpEventList){fprintf_stderr(stderr,"Event handling --- (%d,%d):\n",aRect.x,aRect.y);std::stringstreamss;nsIFrame::PrintDisplayList(&builder,list,ss);print_stderr(ss);}#endifnsDisplayItem::HitTestStatehitTestState;list.HitTest(&builder,aRect,&hitTestState,&aOutFrames);list.DeleteAll(&builder);builder.EndFrame();returnNS_OK;}// aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrameFrameMetricsnsLayoutUtils::CalculateBasicFrameMetrics(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.FrameMetricsmetrics;nsPresContext*presContext=frame->PresContext();PresShell*presShell=presContext->PresShell();CSSToLayoutDeviceScaledeviceScale=presContext->CSSToDevPixelScale();floatresolution=1.0f;boolisRcdRsf=aScrollFrame->IsRootScrollFrameOfDocument()&&presContext->IsRootContentDocumentCrossProcess();metrics.SetIsRootContent(isRcdRsf);if(isRcdRsf){// Only the root content document's root scrollable frame should pick up// the presShell's resolution. All the other frames are 1.0.resolution=presShell->GetResolution();}// Note: unlike in ComputeFrameMetrics(), we don't know the full cumulative// resolution including FrameMetrics::mExtraResolution, because layout hasn't// chosen a resolution to paint at yet. However, the display port calculation// divides out mExtraResolution anyways, so we get the correct result by// setting the mCumulativeResolution to everything except the extra resolution// and leaving mExtraResolution at 1.LayoutDeviceToLayerScale2DcumulativeResolution(presShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(frame));LayerToParentLayerScalelayerToParentLayerScale(1.0f);metrics.SetDevPixelsPerCSSPixel(deviceScale);metrics.SetPresShellResolution(resolution);metrics.SetCumulativeResolution(cumulativeResolution);metrics.SetZoom(deviceScale*cumulativeResolution*layerToParentLayerScale);// Only the size of the composition bounds is relevant to the// displayport calculation, not its origin.nsSizecompositionSize=nsLayoutUtils::CalculateCompositionSizeForFrame(frame);LayoutDeviceToParentLayerScale2DcompBoundsScale;if(frame==presShell->GetRootScrollFrame()&&presContext->IsRootContentDocumentCrossProcess()){if(presContext->GetParentPresContext()){floatres=presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();compBoundsScale=LayoutDeviceToParentLayerScale2D(LayoutDeviceToParentLayerScale(res));}}else{compBoundsScale=cumulativeResolution*layerToParentLayerScale;}metrics.SetCompositionBounds(LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0,0),compositionSize),presContext->AppUnitsPerDevPixel())*compBoundsScale);metrics.SetBoundingCompositionSize(nsLayoutUtils::CalculateBoundingCompositionSize(frame,false,metrics));metrics.SetLayoutViewport(CSSRect::FromAppUnits(nsRect(aScrollFrame->GetScrollPosition(),aScrollFrame->GetScrollPortRect().Size())));metrics.SetVisualScrollOffset(isRcdRsf?CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset()):metrics.GetLayoutViewport().TopLeft());metrics.SetScrollableRect(CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrame,nullptr)));returnmetrics;}nsIScrollableFrame*nsLayoutUtils::GetAsyncScrollableAncestorFrame(nsIFrame*aTarget){uint32_tflags=nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT|nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE|nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT|nsLayoutUtils::SCROLLABLE_FOLLOW_OOF_TO_PLACEHOLDER;returnnsLayoutUtils::GetNearestScrollableFrame(aTarget,flags);}voidnsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder*aBuilder,nsDisplayList*aList,nsIFrame*aFrame,constnsRect&aCanvasArea,constnsRegion&aVisibleRegion,nscoloraBackstop){LayoutFrameTypeframeType=aFrame->Type();nsPresContext*presContext=aFrame->PresContext();PresShell*presShell=presContext->PresShell();// For the viewport frame in print preview/page layout we want to paint// the grey background behind the page, not the canvas color.if(frameType==LayoutFrameType::Viewport&&nsLayoutUtils::NeedsPrintPreviewBackground(presContext)){nsRectbounds=nsRect(aBuilder->ToReferenceFrame(aFrame),aFrame->GetSize());nsDisplayListBuilder::AutoBuildingDisplayListbuildingDisplayList(aBuilder,aFrame,bounds,bounds);presShell->AddPrintPreviewBackgroundItem(aBuilder,aList,aFrame,bounds);}elseif(frameType!=LayoutFrameType::Page){// For printing, this function is first called on an nsPageFrame, which// creates a display list with a PageContent item. The PageContent item's// paint function calls this function on the nsPageFrame's child which is// an nsPageContentFrame. We only want to add the canvas background color// item once, for the nsPageContentFrame.// Add the canvas background color to the bottom of the list. This// happens after we've built the list so that AddCanvasBackgroundColorItem// can monkey with the contents if necessary.nsRectcanvasArea=aVisibleRegion.GetBounds();canvasArea.IntersectRect(aCanvasArea,canvasArea);nsDisplayListBuilder::AutoBuildingDisplayListbuildingDisplayList(aBuilder,aFrame,canvasArea,canvasArea);presShell->AddCanvasBackgroundColorItem(aBuilder,aList,aFrame,canvasArea,aBackstop);}}/** * Returns a retained display list builder for frame |aFrame|. If there is no * retained display list builder property set for the frame, and if the flag * |aRetainingEnabled| is true, a new retained display list builder is created, * stored as a property for the frame, and returned. */staticRetainedDisplayListBuilder*GetOrCreateRetainedDisplayListBuilder(nsIFrame*aFrame,boolaRetainingEnabled,boolaBuildCaret){RetainedDisplayListBuilder*retainedBuilder=aFrame->GetProperty(RetainedDisplayListBuilder::Cached());if(retainedBuilder){returnretainedBuilder;}if(aRetainingEnabled){retainedBuilder=newRetainedDisplayListBuilder(aFrame,nsDisplayListBuilderMode::Painting,aBuildCaret);aFrame->SetProperty(RetainedDisplayListBuilder::Cached(),retainedBuilder);}returnretainedBuilder;}// #define PRINT_HITTESTINFO_STATS#ifdef PRINT_HITTESTINFO_STATSvoidPrintHitTestInfoStatsInternal(nsDisplayList*aList,int&aTotal,int&aHitTest,int&aVisible,int&aSpecial){for(nsDisplayItem*i:*aList){aTotal++;if(i->GetChildren()){PrintHitTestInfoStatsInternal(i->GetChildren(),aTotal,aHitTest,aVisible,aSpecial);}if(i->GetType()==DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO){aHitTest++;constauto&hitTestInfo=static_cast<nsDisplayHitTestInfoBase*>(i)->HitTestFlags();if(hitTestInfo.size()>1){aSpecial++;continue;}if(hitTestInfo==CompositorHitTestVisibleToHit){aVisible++;continue;}aSpecial++;}}}voidPrintHitTestInfoStats(nsDisplayList*aList){inttotal=0;inthitTest=0;intvisible=0;intspecial=0;PrintHitTestInfoStatsInternal(aList,total,hitTest,visible,special);doubleratio=(double)hitTest/(double)total;printf("List %p: total items: %d, hit test items: %d, ratio: %f, visible: %d, ""special: %d\n",aList,total,hitTest,ratio,visible,special);}#endif// Apply a batch of effects updates generated during a paint to their// respective remote browsers.staticvoidApplyEffectsUpdates(constnsTHashMap<nsPtrHashKey<RemoteBrowser>,EffectsInfo>&aUpdates){for(constauto&entry:aUpdates){auto*browser=entry.GetKey();constauto&update=entry.GetData();browser->UpdateEffects(update);}}staticvoidDumpBeforePaintDisplayList(UniquePtr<std::stringstream>&aStream,nsDisplayListBuilder*aBuilder,nsDisplayList*aList,constnsRect&aVisibleRect){#ifdef MOZ_DUMP_PAINTINGif(gfxEnv::DumpPaintToFile()){nsCStringstring("dump-");// Include the process ID in the dump file name, to make sure that in an// e10s setup different processes don't clobber each other's dump files.string.AppendInt(getpid());for(intpaintCount:*gPaintCountStack){string.AppendLiteral("-");string.AppendInt(paintCount);}string.AppendLiteral(".html");gfxUtils::sDumpPaintFile=fopen(string.BeginReading(),"w");}else{gfxUtils::sDumpPaintFile=stderr;}if(gfxEnv::DumpPaintToFile()){*aStream<<"<html><head><script>\n""var array = {};\n""function ViewImage(index) { \n"" var image = document.getElementById(index);\n"" if (image.src) {\n"" image.removeAttribute('src');\n"" } else {\n"" image.src = array[index];\n"" }\n""}</script></head><body>";}#endif*aStream<<nsPrintfCString("Painting --- before optimization (dirty %d,%d,%d,%d):\n",aVisibleRect.x,aVisibleRect.y,aVisibleRect.width,aVisibleRect.height).get();nsIFrame::PrintDisplayList(aBuilder,*aList,*aStream,gfxEnv::DumpPaintToFile());if(gfxEnv::DumpPaint()||gfxEnv::DumpPaintItems()){// Flush stream now to avoid reordering dump output relative to// messages dumped by PaintRoot below.fprint_stderr(gfxUtils::sDumpPaintFile,*aStream);aStream=MakeUnique<std::stringstream>();}}staticvoidDumpAfterPaintDisplayList(UniquePtr<std::stringstream>&aStream,nsDisplayListBuilder*aBuilder,nsDisplayList*aList){*aStream<<"Painting --- after optimization:\n";nsIFrame::PrintDisplayList(aBuilder,*aList,*aStream,gfxEnv::DumpPaintToFile());fprint_stderr(gfxUtils::sDumpPaintFile,*aStream);#ifdef MOZ_DUMP_PAINTINGif(gfxEnv::DumpPaintToFile()){*aStream<<"</body></html>";}if(gfxEnv::DumpPaintToFile()){fclose(gfxUtils::sDumpPaintFile);}#endifstd::stringstreamlsStream;nsIFrame::PrintDisplayList(aBuilder,*aList,lsStream);}structTemporaryDisplayListBuilder{TemporaryDisplayListBuilder(nsIFrame*aFrame,nsDisplayListBuilderModeaBuilderMode,constboolaBuildCaret):mBuilder(aFrame,aBuilderMode,aBuildCaret){}~TemporaryDisplayListBuilder(){mList.DeleteAll(&mBuilder);}nsDisplayListBuildermBuilder;nsDisplayListmList;RetainedDisplayListMetricsmMetrics;};voidnsLayoutUtils::PaintFrame(gfxContext*aRenderingContext,nsIFrame*aFrame,constnsRegion&aDirtyRegion,nscoloraBackstop,nsDisplayListBuilderModeaBuilderMode,PaintFrameFlagsaFlags){AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame",GRAPHICS);#ifdef MOZ_DUMP_PAINTINGif(!gPaintCountStack){gPaintCountStack=newnsTArray<int>();ClearOnShutdown(&gPaintCountStack);gPaintCountStack->AppendElement(0);}++gPaintCountStack->LastElement();AutoNestedPaintCountnestedPaintCount;#endifif(aFlags&PaintFrameFlags::WidgetLayers){nsView*view=aFrame->GetView();if(!(view&&view->GetWidget()&&GetDisplayRootFrame(aFrame)==aFrame)){aFlags&=~PaintFrameFlags::WidgetLayers;NS_ASSERTION(aRenderingContext,"need a rendering context");}}nsPresContext*presContext=aFrame->PresContext();PresShell*presShell=presContext->PresShell();TimeStampstartBuildDisplayList=TimeStamp::Now();autodlTimerId=mozilla::glean::paint::build_displaylist_time.Start();constboolbuildCaret=!(aFlags&PaintFrameFlags::HideCaret);// Note that isForPainting here does not include the PaintForPrinting builder// mode; that's OK because there is no point in using retained display lists// for a print destination.constboolisForPainting=(aFlags&PaintFrameFlags::WidgetLayers)&&aBuilderMode==nsDisplayListBuilderMode::Painting;// Only allow retaining for painting when preffed on, and for root frames// (since the modified frame tracking is per-root-frame).constboolretainingEnabled=isForPainting&&AreRetainedDisplayListsEnabled()&&!aFrame->GetParent();RetainedDisplayListBuilder*retainedBuilder=GetOrCreateRetainedDisplayListBuilder(aFrame,retainingEnabled,buildCaret);// Only use the retained display list builder if the retaining is currently// enabled. This check is needed because it is possible that the pref has been// disabled after creating the retained display list builder.constbooluseRetainedBuilder=retainedBuilder&&retainingEnabled;Maybe<TemporaryDisplayListBuilder>temporaryBuilder;nsDisplayListBuilder*builder=nullptr;nsDisplayList*list=nullptr;RetainedDisplayListMetrics*metrics=nullptr;if(useRetainedBuilder){builder=retainedBuilder->Builder();list=retainedBuilder->List();metrics=retainedBuilder->Metrics();}else{temporaryBuilder.emplace(aFrame,aBuilderMode,buildCaret);builder=&temporaryBuilder->mBuilder;list=&temporaryBuilder->mList;metrics=&temporaryBuilder->mMetrics;}MOZ_ASSERT(builder&&list&&metrics);// Retained builder exists, but display list retaining is disabled.if(!useRetainedBuilder&&retainedBuilder){// Clear the modified frames lists and frame properties.retainedBuilder->ClearFramesWithProps();// Clear the retained display list.retainedBuilder->List()->DeleteAll(retainedBuilder->Builder());}metrics->Reset();metrics->StartBuild();builder->BeginFrame();if(aFlags&PaintFrameFlags::InTransform){builder->SetInTransform(true);}if(aFlags&PaintFrameFlags::SyncDecodeImages){builder->SetSyncDecodeImages(true);}if(aFlags&(PaintFrameFlags::WidgetLayers|PaintFrameFlags::ToWindow)){builder->SetPaintingToWindow(true);}if(aFlags&PaintFrameFlags::UseHighQualityScaling){builder->SetUseHighQualityScaling(true);}if(aFlags&PaintFrameFlags::ForWebRender){builder->SetPaintingForWebRender(true);}if(aFlags&PaintFrameFlags::IgnoreSuppression){builder->IgnorePaintSuppression();}if(BrowsingContext*bc=presContext->Document()->GetBrowsingContext()){builder->SetInActiveDocShell(bc->IsActive());}nsRectrootInkOverflow=aFrame->InkOverflowRectRelativeToSelf();// If we are in a remote browser, then apply clipping from ancestor browsersif(BrowserChild*browserChild=BrowserChild::GetFrom(presShell)){if(!browserChild->IsTopLevel()){Maybe<nsRect>unscaledVisibleRect=browserChild->GetVisibleRect();if(!unscaledVisibleRect){unscaledVisibleRect=Some(nsRect());}rootInkOverflow.IntersectRect(rootInkOverflow,*unscaledVisibleRect);}}builder->ClearHaveScrollableDisplayPort();if(builder->IsPaintingToWindow()){DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(aFrame,builder);}nsIFrame*rootScrollFrame=presShell->GetRootScrollFrame();if(rootScrollFrame&&!aFrame->GetParent()){nsIScrollableFrame*rootScrollableFrame=presShell->GetRootScrollFrameAsScrollable();MOZ_ASSERT(rootScrollableFrame);nsRectdisplayPortBase=rootInkOverflow;nsRecttemp=displayPortBase;Unused<<rootScrollableFrame->DecideScrollableLayer(builder,&displayPortBase,&temp,/* aSetBase = */true);}nsRegionvisibleRegion;if(aFlags&PaintFrameFlags::WidgetLayers){// This layer tree will be reused, so we'll need to calculate it// for the whole "visible" area of the window//// |ignoreViewportScrolling| and |usingDisplayPort| are persistent// document-rendering state. We rely on PresShell to flush// retained layers as needed when that persistent state changes.visibleRegion=rootInkOverflow;}else{visibleRegion=aDirtyRegion;}Maybe<nsPoint>originalScrollPosition;automaybeResetScrollPosition=MakeScopeExit([&](){if(originalScrollPosition&&rootScrollFrame){nsIScrollableFrame*rootScrollableFrame=presShell->GetRootScrollFrameAsScrollable();MOZ_ASSERT(rootScrollableFrame->GetScrolledFrame()->GetPosition()==nsPoint());rootScrollableFrame->GetScrolledFrame()->SetPosition(*originalScrollPosition);}});nsRectcanvasArea(nsPoint(0,0),aFrame->GetSize());boolignoreViewportScrolling=!aFrame->GetParent()&&presShell->IgnoringViewportScrolling();if(ignoreViewportScrolling&&rootScrollFrame){nsIScrollableFrame*rootScrollableFrame=presShell->GetRootScrollFrameAsScrollable();if(aFlags&PaintFrameFlags::ResetViewportScrolling){// Temporarily scroll the root scroll frame to 0,0 so that position:fixed// elements will appear fixed to the top-left of the document. We manually// set the position of the scrolled frame instead of using ScrollTo, since// the latter fires scroll listeners, which we don't want.originalScrollPosition.emplace(rootScrollableFrame->GetScrolledFrame()->GetPosition());rootScrollableFrame->GetScrolledFrame()->SetPosition(nsPoint());}if(aFlags&PaintFrameFlags::DocumentRelative){// Make visibleRegion and aRenderingContext relative to the// scrolled frame instead of the root frame.nsPointpos=rootScrollableFrame->GetScrollPosition();visibleRegion.MoveBy(-pos);if(aRenderingContext){gfxPointdevPixelOffset=nsLayoutUtils::PointToGfxPoint(pos,presContext->AppUnitsPerDevPixel());aRenderingContext->SetMatrixDouble(aRenderingContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));}}builder->SetIgnoreScrollFrame(rootScrollFrame);nsCanvasFrame*canvasFrame=do_QueryFrame(rootScrollableFrame->GetScrolledFrame());if(canvasFrame){// Use UnionRect here to ensure that areas where the scrollbars// were are still filled with the background color.canvasArea.UnionRect(canvasArea,canvasFrame->CanvasArea()+builder->ToReferenceFrame(canvasFrame));}}nsRectvisibleRect=visibleRegion.GetBounds();PartialUpdateResultupdateState=PartialUpdateResult::Failed;{AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_DisplayListBuilding);AUTO_PROFILER_TRACING_MARKER("Paint","DisplayList",GRAPHICS);PerfStats::AutoMetricRecording<PerfStats::Metric::DisplayListBuilding>autoRecording;{ViewIDid=ScrollableLayerGuid::NULL_SCROLL_ID;if(presShell->GetDocument()&&presShell->GetDocument()->IsRootDisplayDocument()&&!presShell->GetRootScrollFrame()){// In cases where the root document is a XUL document, we want to take// the ViewID from the root element, as that will be the ViewID of the// root APZC in the tree. Skip doing this in cases where we know// nsGfxScrollFrame::BuilDisplayList will do it instead.if(dom::Element*element=presShell->GetDocument()->GetDocumentElement()){id=nsLayoutUtils::FindOrCreateIDFor(element);}// In some cases we get a root document here on an APZ-enabled window// that doesn't have the root displayport initialized yet, even though// the ChromeProcessController is supposed to do it when the widget is// created. This can happen simply because the ChromeProcessController// does it on the next spin of the event loop, and we can trigger a// paint synchronously after window creation but before that runs. In// that case we should initialize the root displayport here before we do// the paint.}elseif(XRE_IsParentProcess()&&presContext->IsRoot()&&presShell->GetDocument()!=nullptr&&presShell->GetRootScrollFrame()!=nullptr&&nsLayoutUtils::UsesAsyncScrolling(presShell->GetRootScrollFrame())){if(dom::Element*element=presShell->GetDocument()->GetDocumentElement()){if(!DisplayPortUtils::HasNonMinimalDisplayPort(element)){APZCCallbackHelper::InitializeRootDisplayport(presShell);}}}nsDisplayListBuilder::AutoCurrentScrollParentIdSetteridSetter(builder,id);builder->SetVisibleRect(visibleRect);builder->SetIsBuilding(true);builder->SetAncestorHasApzAwareEventHandler(gfxPlatform::AsyncPanZoomEnabled()&&nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell));// If a pref is toggled that adds or removes display list items,// we need to rebuild the display list. The pref may be toggled// manually by the user, or during test setup.boolshouldAttemptPartialUpdate=useRetainedBuilder;if(builder->ShouldRebuildDisplayListDueToPrefChange()){shouldAttemptPartialUpdate=false;}// Attempt to do a partial build and merge into the existing list.// This calls BuildDisplayListForStacking context on a subset of the// viewport.if(shouldAttemptPartialUpdate){updateState=retainedBuilder->AttemptPartialUpdate(aBackstop);metrics->EndPartialBuild(updateState);}else{// Partial updates are disabled.metrics->mPartialUpdateResult=PartialUpdateResult::Failed;metrics->mPartialUpdateFailReason=PartialUpdateFailReason::Disabled;}// Rebuild the full display list if the partial display list build failed.booldoFullRebuild=updateState==PartialUpdateResult::Failed;if(StaticPrefs::layout_display_list_build_twice()){// Build display list twice to compare partial and full display list// build times.metrics->StartBuild();doFullRebuild=true;}if(doFullRebuild){list->DeleteAll(builder);list->RestoreState();builder->ClearRetainedWindowRegions();builder->ClearWillChangeBudgets();builder->EnterPresShell(aFrame);builder->SetDirtyRect(visibleRect);aFrame->BuildDisplayListForStackingContext(builder,list);AddExtraBackgroundItems(builder,list,aFrame,canvasArea,visibleRegion,aBackstop);builder->LeavePresShell(aFrame,list);metrics->EndFullBuild();updateState=PartialUpdateResult::Updated;}}builder->SetIsBuilding(false);builder->IncrementPresShellPaintCount(presShell);}MOZ_ASSERT(updateState!=PartialUpdateResult::Failed);builder->Check();constdoublegeckoDLBuildTime=(TimeStamp::Now()-startBuildDisplayList).ToMilliseconds();mozilla::glean::paint::build_displaylist_time.StopAndAccumulate(std::move(dlTimerId));boolconsoleNeedsDisplayList=(gfxUtils::DumpDisplayList()||gfxEnv::DumpPaint())&&builder->IsInActiveDocShell();#ifdef MOZ_DUMP_PAINTINGFILE*savedDumpFile=gfxUtils::sDumpPaintFile;#endifUniquePtr<std::stringstream>ss;if(consoleNeedsDisplayList){ss=MakeUnique<std::stringstream>();Document*doc=presContext->Document();nsAutoStringuri;if(doc&&doc->GetDocumentURI(uri)==NS_OK){*ss<<"Display list for "<<uri<<"\n";}DumpBeforePaintDisplayList(ss,builder,list,visibleRect);}uint32_tflags=nsDisplayList::PAINT_DEFAULT;if(aFlags&PaintFrameFlags::WidgetLayers){flags|=nsDisplayList::PAINT_USE_WIDGET_LAYERS;if(!(aFlags&PaintFrameFlags::DocumentRelative)){nsIWidget*widget=aFrame->GetNearestWidget();if(widget){// If we're finished building display list items for painting of the// outermost pres shell, notify the widget about any toolbars we've// encountered.widget->UpdateThemeGeometries(builder->GetThemeGeometries());}}}if(aFlags&PaintFrameFlags::ExistingTransaction){flags|=nsDisplayList::PAINT_EXISTING_TRANSACTION;}if(aFlags&PaintFrameFlags::NoComposite){flags|=nsDisplayList::PAINT_NO_COMPOSITE;}if(aFlags&PaintFrameFlags::Compressed){flags|=nsDisplayList::PAINT_COMPRESSED;}if(updateState==PartialUpdateResult::NoChange&&!aRenderingContext){flags|=nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST;}#ifdef PRINT_HITTESTINFO_STATSif(XRE_IsContentProcess()){PrintHitTestInfoStats(list);}#endifTimeStamppaintStart=TimeStamp::Now();list->PaintRoot(builder,aRenderingContext,flags,Some(geckoDLBuildTime));Telemetry::AccumulateTimeDelta(Telemetry::PAINT_RASTERIZE_TIME,paintStart);if(builder->IsPaintingToWindow()){presShell->EndPaint();}builder->Check();if(consoleNeedsDisplayList){DumpAfterPaintDisplayList(ss,builder,list);}#ifdef MOZ_DUMP_PAINTINGgfxUtils::sDumpPaintFile=savedDumpFile;#endif// Update the widget's opaque region information. This sets// glass boundaries on Windows. Also set up the window dragging region.if((aFlags&PaintFrameFlags::WidgetLayers)&&!(aFlags&PaintFrameFlags::DocumentRelative)){nsIWidget*widget=aFrame->GetNearestWidget();if(widget){nsRegionopaqueRegion;opaqueRegion.And(builder->GetWindowExcludeGlassRegion(),builder->GetWindowOpaqueRegion());widget->UpdateOpaqueRegion(LayoutDeviceIntRegion::FromUnknownRegion(opaqueRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())));widget->UpdateWindowDraggingRegion(builder->GetWindowDraggingRegion());}}// Apply effects updates if we were actually paintingif(isForPainting){ApplyEffectsUpdates(builder->GetEffectUpdates());}builder->Check();{AUTO_PROFILER_TRACING_MARKER("Paint","DisplayListResources",GRAPHICS);builder->EndFrame();if(!useRetainedBuilder){temporaryBuilder.reset();}}#if 0 if (XRE_IsParentProcess()) { if (metrics->mPartialUpdateResult == PartialUpdateResult::Failed) { printf("DL partial update failed: %s, Frame: %p\n", metrics->FailReasonString(), aFrame); } else { printf( "DL partial build success!" " new: %d, reused: %d, rebuilt: %d, removed: %d, total: %d\n", metrics->mNewItems, metrics->mReusedItems, metrics->mRebuiltItems, metrics->mRemovedItems, metrics->mTotalItems); } }#endif}/** * Uses a binary search for find where the cursor falls in the line of text * It also keeps track of the part of the string that has already been measured * so it doesn't have to keep measuring the same text over and over * * @param "aBaseWidth" contains the width in twips of the portion * of the text that has already been measured, and aBaseInx contains * the index of the text that has already been measured. * * @param aTextWidth returns the (in twips) the length of the text that falls * before the cursor aIndex contains the index of the text where the cursor * falls */boolnsLayoutUtils::BinarySearchForPosition(DrawTarget*aDrawTarget,nsFontMetrics&aFontMetrics,constchar16_t*aText,int32_taBaseWidth,int32_taBaseInx,int32_taStartInx,int32_taEndInx,int32_taCursorPos,int32_t&aIndex,int32_t&aTextWidth){int32_trange=aEndInx-aStartInx;if((range==1)||(range==2&&NS_IS_HIGH_SURROGATE(aText[aStartInx]))){aIndex=aStartInx+aBaseInx;aTextWidth=nsLayoutUtils::AppUnitWidthOfString(aText,aIndex,aFontMetrics,aDrawTarget);returntrue;}int32_tinx=aStartInx+(range/2);// Make sure we don't leave a dangling low surrogateif(NS_IS_HIGH_SURROGATE(aText[inx-1]))inx++;int32_ttextWidth=nsLayoutUtils::AppUnitWidthOfString(aText,inx,aFontMetrics,aDrawTarget);int32_tfullWidth=aBaseWidth+textWidth;if(fullWidth==aCursorPos){aTextWidth=textWidth;aIndex=inx;returntrue;}elseif(aCursorPos<fullWidth){aTextWidth=aBaseWidth;if(BinarySearchForPosition(aDrawTarget,aFontMetrics,aText,aBaseWidth,aBaseInx,aStartInx,inx,aCursorPos,aIndex,aTextWidth)){returntrue;}}else{aTextWidth=fullWidth;if(BinarySearchForPosition(aDrawTarget,aFontMetrics,aText,aBaseWidth,aBaseInx,inx,aEndInx,aCursorPos,aIndex,aTextWidth)){returntrue;}}returnfalse;}voidnsLayoutUtils::AddBoxesForFrame(nsIFrame*aFrame,nsLayoutUtils::BoxCallback*aCallback){autopseudoType=aFrame->Style()->GetPseudoType();if(pseudoType==PseudoStyleType::tableWrapper){AddBoxesForFrame(aFrame->PrincipalChildList().FirstChild(),aCallback);if(aCallback->mIncludeCaptionBoxForTable){nsIFrame*kid=aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();if(kid){AddBoxesForFrame(kid,aCallback);}}}elseif(pseudoType==PseudoStyleType::mozBlockInsideInlineWrapper||pseudoType==PseudoStyleType::mozMathMLAnonymousBlock||pseudoType==PseudoStyleType::mozXULAnonymousBlock){for(nsIFrame*kid:aFrame->PrincipalChildList()){AddBoxesForFrame(kid,aCallback);}}else{aCallback->AddBox(aFrame);}}voidnsLayoutUtils::GetAllInFlowBoxes(nsIFrame*aFrame,BoxCallback*aCallback){aCallback->mInTargetContinuation=false;while(aFrame){AddBoxesForFrame(aFrame,aCallback);aFrame=nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);aCallback->mInTargetContinuation=true;}}nsIFrame*nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame*aFrame){while(aFrame){autopseudoType=aFrame->Style()->GetPseudoType();if(pseudoType==PseudoStyleType::tableWrapper){nsIFrame*f=GetFirstNonAnonymousFrame(aFrame->PrincipalChildList().FirstChild());if(f){returnf;}nsIFrame*kid=aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();if(kid){f=GetFirstNonAnonymousFrame(kid);if(f){returnf;}}}elseif(pseudoType==PseudoStyleType::mozBlockInsideInlineWrapper||pseudoType==PseudoStyleType::mozMathMLAnonymousBlock||pseudoType==PseudoStyleType::mozXULAnonymousBlock){for(nsIFrame*kid:aFrame->PrincipalChildList()){nsIFrame*f=GetFirstNonAnonymousFrame(kid);if(f){returnf;}}}else{returnaFrame;}aFrame=nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);}returnnullptr;}structBoxToRect:publicnsLayoutUtils::BoxCallback{constnsIFrame*mRelativeTo;RectCallback*mCallback;uint32_tmFlags;// If the frame we're measuring relative to is the root, we know all frames// are descendants of it, so we don't need to compute the common ancestor// between a frame and mRelativeTo.boolmRelativeToIsRoot;// For the same reason, if the frame we're measuring relative to is the target// (this is useful for IntersectionObserver), we know all frames are// descendants of it except if we're in a continuation or ib-split-sibling of// it.boolmRelativeToIsTarget;BoxToRect(constnsIFrame*aTargetFrame,constnsIFrame*aRelativeTo,RectCallback*aCallback,uint32_taFlags):mRelativeTo(aRelativeTo),mCallback(aCallback),mFlags(aFlags),mRelativeToIsRoot(!aRelativeTo->GetParent()),mRelativeToIsTarget(aRelativeTo==aTargetFrame){}voidAddBox(nsIFrame*aFrame)override{nsRectr;nsIFrame*outer=SVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame,&r);constboolusingSVGOuterFrame=!!outer;if(!outer){outer=aFrame;switch(mFlags&nsLayoutUtils::RECTS_WHICH_BOX_MASK){casensLayoutUtils::RECTS_USE_CONTENT_BOX:r=aFrame->GetContentRectRelativeToSelf();break;casensLayoutUtils::RECTS_USE_PADDING_BOX:r=aFrame->GetPaddingRectRelativeToSelf();break;casensLayoutUtils::RECTS_USE_MARGIN_BOX:r=aFrame->GetMarginRectRelativeToSelf();break;default:// Use the border boxr=aFrame->GetRectRelativeToSelf();}}if(mFlags&nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS){constboolisAncestorKnown=[&]{if(mRelativeToIsRoot){returntrue;}if(mRelativeToIsTarget&&!mInTargetContinuation){return!usingSVGOuterFrame;}returnfalse;}();if(isAncestorKnown){r=nsLayoutUtils::TransformFrameRectToAncestor(outer,r,mRelativeTo);}else{nsLayoutUtils::TransformRect(outer,mRelativeTo,r);}}else{r+=outer->GetOffsetTo(mRelativeTo);}mCallback->AddRect(r);}};structMOZ_RAIIBoxToRectAndText:publicBoxToRect{Sequence<nsString>*mTextList;BoxToRectAndText(constnsIFrame*aTargetFrame,constnsIFrame*aRelativeTo,RectCallback*aCallback,Sequence<nsString>*aTextList,uint32_taFlags):BoxToRect(aTargetFrame,aRelativeTo,aCallback,aFlags),mTextList(aTextList){}staticvoidAccumulateText(nsIFrame*aFrame,nsAString&aResult){MOZ_ASSERT(aFrame);// Get all the text in aFrame and child frames, while respecting// the content offsets in each of the nsTextFrames.if(aFrame->IsTextFrame()){nsTextFrame*textFrame=static_cast<nsTextFrame*>(aFrame);nsIFrame::RenderedTextrenderedText=textFrame->GetRenderedText(textFrame->GetContentOffset(),textFrame->GetContentOffset()+textFrame->GetContentLength(),nsIFrame::TextOffsetType::OffsetsInContentText,nsIFrame::TrailingWhitespace::DontTrim);aResult.Append(renderedText.mString);}for(nsIFrame*child=aFrame->PrincipalChildList().FirstChild();child;child=child->GetNextSibling()){AccumulateText(child,aResult);}}voidAddBox(nsIFrame*aFrame)override{BoxToRect::AddBox(aFrame);if(mTextList){nsString*textForFrame=mTextList->AppendElement(fallible);if(textForFrame){AccumulateText(aFrame,*textForFrame);}}}};voidnsLayoutUtils::GetAllInFlowRects(nsIFrame*aFrame,constnsIFrame*aRelativeTo,RectCallback*aCallback,uint32_taFlags){BoxToRectconverter(aFrame,aRelativeTo,aCallback,aFlags);GetAllInFlowBoxes(aFrame,&converter);}voidnsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame*aFrame,constnsIFrame*aRelativeTo,RectCallback*aCallback,Sequence<nsString>*aTextList,uint32_taFlags){BoxToRectAndTextconverter(aFrame,aRelativeTo,aCallback,aTextList,aFlags);GetAllInFlowBoxes(aFrame,&converter);}nsLayoutUtils::RectAccumulator::RectAccumulator():mSeenFirstRect(false){}voidnsLayoutUtils::RectAccumulator::AddRect(constnsRect&aRect){mResultRect.UnionRect(mResultRect,aRect);if(!mSeenFirstRect){mSeenFirstRect=true;mFirstRect=aRect;}}nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList*aList):mRectList(aList){}voidnsLayoutUtils::RectListBuilder::AddRect(constnsRect&aRect){RefPtr<DOMRect>rect=newDOMRect(mRectList);rect->SetLayoutRect(aRect);mRectList->Append(rect);}nsIFrame*nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame*aFrame){returnaFrame->PresShell()->GetRootFrame();}nsRectnsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame*aFrame,constnsIFrame*aRelativeTo,uint32_taFlags){RectAccumulatoraccumulator;GetAllInFlowRects(aFrame,aRelativeTo,&accumulator,aFlags);returnaccumulator.mResultRect.IsEmpty()?accumulator.mFirstRect:accumulator.mResultRect;}nsRectnsLayoutUtils::GetTextShadowRectsUnion(constnsRect&aTextAndDecorationsRect,nsIFrame*aFrame,uint32_taFlags){constnsStyleText*textStyle=aFrame->StyleText();autoshadows=textStyle->mTextShadow.AsSpan();if(shadows.IsEmpty()){returnaTextAndDecorationsRect;}nsRectresultRect=aTextAndDecorationsRect;int32_tA2D=aFrame->PresContext()->AppUnitsPerDevPixel();for(auto&shadow:shadows){nsMarginblur=nsContextBoxBlur::GetBlurRadiusMargin(shadow.blur.ToAppUnits(),A2D);if((aFlags&EXCLUDE_BLUR_SHADOWS)&&blur!=nsMargin(0,0,0,0))continue;nsRecttmpRect(aTextAndDecorationsRect);tmpRect.MoveBy(nsPoint(shadow.horizontal.ToAppUnits(),shadow.vertical.ToAppUnits()));tmpRect.Inflate(blur);resultRect.UnionRect(resultRect,tmpRect);}returnresultRect;}enumObjectDimensionType{eWidth,eHeight};staticnscoordComputeMissingDimension(constnsSize&aDefaultObjectSize,constAspectRatio&aIntrinsicRatio,constMaybe<nscoord>&aSpecifiedWidth,constMaybe<nscoord>&aSpecifiedHeight,ObjectDimensionTypeaDimensionToCompute){// The "default sizing algorithm" computes the missing dimension as follows:// (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )// 1. "If the object has an intrinsic aspect ratio, the missing dimension of// the concrete object size is calculated using the intrinsic aspect// ratio and the present dimension."if(aIntrinsicRatio){// Fill in the missing dimension using the intrinsic aspect ratio.if(aDimensionToCompute==eWidth){returnaIntrinsicRatio.ApplyTo(*aSpecifiedHeight);}returnaIntrinsicRatio.Inverted().ApplyTo(*aSpecifiedWidth);}// 2. "Otherwise, if the missing dimension is present in the object's// intrinsic dimensions, [...]"// NOTE: *Skipping* this case, because we already know it's not true -- we're// in this function because the missing dimension is *not* present in// the object's intrinsic dimensions.// 3. "Otherwise, the missing dimension of the concrete object size is taken// from the default object size. "return(aDimensionToCompute==eWidth)?aDefaultObjectSize.width:aDefaultObjectSize.height;}/* * This computes & returns the concrete object size of replaced content, if * that content were to be rendered with "object-fit: none". (Or, if the * element has neither an intrinsic height nor width, this method returns an * empty Maybe<> object.) * * As specced... * http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none * ..we use "the default sizing algorithm with no specified size, * and a default object size equal to the replaced element's used width and * height." * * The default sizing algorithm is described here: * http://dev.w3.org/csswg/css-images-3/#default-sizing * Quotes in the function-impl are taken from that ^ spec-text. * * Per its final bulleted section: since there's no specified size, * we run the default sizing algorithm using the object's intrinsic size in * place of the specified size. But if the object has neither an intrinsic * height nor an intrinsic width, then we instead return without populating our * outparam, and we let the caller figure out the size (using a contain * constraint). */staticMaybe<nsSize>MaybeComputeObjectFitNoneSize(constnsSize&aDefaultObjectSize,constIntrinsicSize&aIntrinsicSize,constAspectRatio&aIntrinsicRatio){// "If the object has an intrinsic height or width, its size is resolved as// if its intrinsic dimensions were given as the specified size."//// So, first we check if we have an intrinsic height and/or width:constMaybe<nscoord>&specifiedWidth=aIntrinsicSize.width;constMaybe<nscoord>&specifiedHeight=aIntrinsicSize.height;Maybe<nsSize>noneSize;// (the value we'll return)if(specifiedWidth||specifiedHeight){// We have at least one specified dimension; use whichever dimension is// specified, and compute the other one using our intrinsic ratio, or (if// no valid ratio) using the default object size.noneSize.emplace();noneSize->width=specifiedWidth?*specifiedWidth:ComputeMissingDimension(aDefaultObjectSize,aIntrinsicRatio,specifiedWidth,specifiedHeight,eWidth);noneSize->height=specifiedHeight?*specifiedHeight:ComputeMissingDimension(aDefaultObjectSize,aIntrinsicRatio,specifiedWidth,specifiedHeight,eHeight);}// [else:] "Otherwise [if there's neither an intrinsic height nor width], its// size is resolved as a contain constraint against the default object size."// We'll let our caller do that, to share code & avoid redundant// computations; so, we return w/out populating noneSize.returnnoneSize;}// Computes the concrete object size to render into, as described at// http://dev.w3.org/csswg/css-images-3/#concrete-size-resolutionstaticnsSizeComputeConcreteObjectSize(constnsSize&aConstraintSize,constIntrinsicSize&aIntrinsicSize,constAspectRatio&aIntrinsicRatio,StyleObjectFitaObjectFit){// Handle default behavior (filling the container) w/ fast early return.// (Also: if there's no valid intrinsic ratio, then we have the "fill"// behavior & just use the constraint size.)if(MOZ_LIKELY(aObjectFit==StyleObjectFit::Fill)||!aIntrinsicRatio){returnaConstraintSize;}// The type of constraint to compute (cover/contain), if needed:Maybe<nsImageRenderer::FitType>fitType;Maybe<nsSize>noneSize;if(aObjectFit==StyleObjectFit::None||aObjectFit==StyleObjectFit::ScaleDown){noneSize=MaybeComputeObjectFitNoneSize(aConstraintSize,aIntrinsicSize,aIntrinsicRatio);if(!noneSize||aObjectFit==StyleObjectFit::ScaleDown){// Need to compute a 'CONTAIN' constraint (either for the 'none' size// itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)fitType.emplace(nsImageRenderer::CONTAIN);}}elseif(aObjectFit==StyleObjectFit::Cover){fitType.emplace(nsImageRenderer::COVER);}elseif(aObjectFit==StyleObjectFit::Contain){fitType.emplace(nsImageRenderer::CONTAIN);}Maybe<nsSize>constrainedSize;if(fitType){constrainedSize.emplace(nsImageRenderer::ComputeConstrainedSize(aConstraintSize,aIntrinsicRatio,*fitType));}// Now, we should have all the sizing information that we need.switch(aObjectFit){// skipping StyleObjectFit::Fill; we handled it w/ early-return.caseStyleObjectFit::Contain:caseStyleObjectFit::Cover:MOZ_ASSERT(constrainedSize);return*constrainedSize;caseStyleObjectFit::None:if(noneSize){return*noneSize;}MOZ_ASSERT(constrainedSize);return*constrainedSize;caseStyleObjectFit::ScaleDown:MOZ_ASSERT(constrainedSize);if(noneSize){constrainedSize->width=std::min(constrainedSize->width,noneSize->width);constrainedSize->height=std::min(constrainedSize->height,noneSize->height);}return*constrainedSize;default:MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");returnaConstraintSize;// fall back to (default) 'fill' behavior}}// (Helper for HasInitialObjectFitAndPosition, to check// each "object-position" coord.)staticboolIsCoord50Pct(constLengthPercentage&aCoord){returnaCoord.ConvertsToPercentage()&&aCoord.ToPercentage()==0.5f;}// Indicates whether the given nsStylePosition has the initial values// for the "object-fit" and "object-position" properties.staticboolHasInitialObjectFitAndPosition(constnsStylePosition*aStylePos){constPosition&objectPos=aStylePos->mObjectPosition;returnaStylePos->mObjectFit==StyleObjectFit::Fill&&IsCoord50Pct(objectPos.horizontal)&&IsCoord50Pct(objectPos.vertical);}/* static */nsRectnsLayoutUtils::ComputeObjectDestRect(constnsRect&aConstraintRect,constIntrinsicSize&aIntrinsicSize,constAspectRatio&aIntrinsicRatio,constnsStylePosition*aStylePos,nsPoint*aAnchorPoint){// Step 1: Figure out our "concrete object size"// (the size of the region we'll actually draw our image's pixels into).nsSizeconcreteObjectSize=ComputeConcreteObjectSize(aConstraintRect.Size(),aIntrinsicSize,aIntrinsicRatio,aStylePos->mObjectFit);// Step 2: Figure out how to align that region in the element's content-box.nsPointimageTopLeftPt,imageAnchorPt;nsImageRenderer::ComputeObjectAnchorPoint(aStylePos->mObjectPosition,aConstraintRect.Size(),concreteObjectSize,&imageTopLeftPt,&imageAnchorPt);// Right now, we're with respect to aConstraintRect's top-left point. We add// that point here, to convert to the same broader coordinate space that// aConstraintRect is in.imageTopLeftPt+=aConstraintRect.TopLeft();imageAnchorPt+=aConstraintRect.TopLeft();if(aAnchorPoint){// Special-case: if our "object-fit" and "object-position" properties have// their default values ("object-fit: fill; object-position:50% 50%"), then// we'll override the calculated imageAnchorPt, and instead use the// object's top-left corner.//// This special case is partly for backwards compatibility (since// traditionally we've pixel-aligned the top-left corner of e.g. <img>// elements), and partly because ComputeSnappedDrawingParameters produces// less error if the anchor point is at the top-left corner. So, all other// things being equal, we prefer that code path with less error.if(HasInitialObjectFitAndPosition(aStylePos)){*aAnchorPoint=imageTopLeftPt;}else{*aAnchorPoint=imageAnchorPt;}}returnnsRect(imageTopLeftPt,concreteObjectSize);}already_AddRefed<nsFontMetrics>nsLayoutUtils::GetFontMetricsForFrame(constnsIFrame*aFrame,floataInflation){ComputedStyle*computedStyle=aFrame->Style();uint8_tvariantWidth=NS_FONT_VARIANT_WIDTH_NORMAL;if(computedStyle->IsTextCombined()){MOZ_ASSERT(aFrame->IsTextFrame());autotextFrame=static_cast<constnsTextFrame*>(aFrame);autoclusters=textFrame->CountGraphemeClusters();if(clusters==2){variantWidth=NS_FONT_VARIANT_WIDTH_HALF;}elseif(clusters==3){variantWidth=NS_FONT_VARIANT_WIDTH_THIRD;}elseif(clusters==4){variantWidth=NS_FONT_VARIANT_WIDTH_QUARTER;}}returnGetFontMetricsForComputedStyle(computedStyle,aFrame->PresContext(),aInflation,variantWidth);}already_AddRefed<nsFontMetrics>nsLayoutUtils::GetFontMetricsForComputedStyle(ComputedStyle*aComputedStyle,nsPresContext*aPresContext,floataInflation,uint8_taVariantWidth){WritingModewm(aComputedStyle);constnsStyleFont*styleFont=aComputedStyle->StyleFont();nsFontMetrics::Paramsparams;params.language=styleFont->mLanguage;params.explicitLanguage=styleFont->mExplicitLanguage;params.orientation=wm.IsVertical()&&!wm.IsSideways()?nsFontMetrics::eVertical:nsFontMetrics::eHorizontal;// pass the user font set object into the device context to// pass along to CreateFontGroupparams.userFontSet=aPresContext->GetUserFontSet();params.textPerf=aPresContext->GetTextPerfMetrics();params.featureValueLookup=aPresContext->GetFontFeatureValuesLookup();// When aInflation is 1.0 and we don't require width variant, avoid// making a local copy of the nsFont.// This also avoids running font.size through floats when it is large,// which would be lossy. Fortunately, in such cases, aInflation is// guaranteed to be 1.0f.if(aInflation==1.0f&&aVariantWidth==NS_FONT_VARIANT_WIDTH_NORMAL){returnaPresContext->GetMetricsFor(styleFont->mFont,params);}nsFontfont=styleFont->mFont;MOZ_ASSERT(!IsNaN(float(font.size.ToCSSPixels())),"Style font should never be NaN");font.size.ScaleBy(aInflation);if(MOZ_UNLIKELY(IsNaN(float(font.size.ToCSSPixels())))){font.size={0};}font.variantWidth=aVariantWidth;returnaPresContext->GetMetricsFor(font,params);}nsIFrame*nsLayoutUtils::FindChildContainingDescendant(nsIFrame*aParent,nsIFrame*aDescendantFrame){nsIFrame*result=aDescendantFrame;while(result){nsIFrame*parent=result->GetParent();if(parent==aParent){break;}// The frame is not an immediate child of aParent so walk up another levelresult=parent;}returnresult;}nsBlockFrame*nsLayoutUtils::FindNearestBlockAncestor(nsIFrame*aFrame){nsIFrame*nextAncestor;for(nextAncestor=aFrame->GetParent();nextAncestor;nextAncestor=nextAncestor->GetParent()){nsBlockFrame*block=do_QueryFrame(nextAncestor);if(block)returnblock;}returnnullptr;}nsIFrame*nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame*aFrame){if(!aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT))returnaFrame;nsIFrame*f=aFrame;do{f=GetParentOrPlaceholderFor(f);}while(f->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT));returnf;}nsIFrame*nsLayoutUtils::GetParentOrPlaceholderFor(constnsIFrame*aFrame){if(aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)&&!aFrame->GetPrevInFlow()){returnaFrame->GetProperty(nsIFrame::PlaceholderFrameProperty());}returnaFrame->GetParent();}nsIFrame*nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(constnsIFrame*aFrame){nsIFrame*f=GetParentOrPlaceholderFor(aFrame);if(f)returnf;returnGetCrossDocParentFrameInProcess(aFrame);}nsIFrame*nsLayoutUtils::GetDisplayListParent(nsIFrame*aFrame){if(aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT)){returnaFrame->GetParent();}returnnsLayoutUtils::GetParentOrPlaceholderForCrossDoc(aFrame);}nsIFrame*nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(constnsIFrame*aFrame){if(nsIFrame*result=aFrame->GetPrevContinuation()){returnresult;}if(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)){// We are the first frame in the continuation chain. Get the ib-split prev// sibling property stored in us.returnaFrame->GetProperty(nsIFrame::IBSplitPrevSibling());}returnnullptr;}nsIFrame*nsLayoutUtils::GetNextContinuationOrIBSplitSibling(constnsIFrame*aFrame){if(nsIFrame*result=aFrame->GetNextContinuation()){returnresult;}if(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)){// We only store the ib-split sibling annotation with the first frame in the// continuation chain.returnaFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());}returnnullptr;}nsIFrame*nsLayoutUtils::FirstContinuationOrIBSplitSibling(constnsIFrame*aFrame){nsIFrame*result=aFrame->FirstContinuation();if(result->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)){while(auto*f=result->GetProperty(nsIFrame::IBSplitPrevSibling())){result=f;}}returnresult;}nsIFrame*nsLayoutUtils::LastContinuationOrIBSplitSibling(constnsIFrame*aFrame){nsIFrame*result=aFrame->FirstContinuation();if(result->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)){while(auto*f=result->GetProperty(nsIFrame::IBSplitSibling())){result=f;}}returnresult->LastContinuation();}boolnsLayoutUtils::IsFirstContinuationOrIBSplitSibling(constnsIFrame*aFrame){if(aFrame->GetPrevContinuation()){returnfalse;}if(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)&&aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())){returnfalse;}returntrue;}boolnsLayoutUtils::IsViewportScrollbarFrame(nsIFrame*aFrame){if(!aFrame)returnfalse;nsIFrame*rootScrollFrame=aFrame->PresShell()->GetRootScrollFrame();if(!rootScrollFrame)returnfalse;nsIScrollableFrame*rootScrollableFrame=do_QueryFrame(rootScrollFrame);NS_ASSERTION(rootScrollableFrame,"The root scorollable frame is null");if(!IsProperAncestorFrame(rootScrollFrame,aFrame))returnfalse;nsIFrame*rootScrolledFrame=rootScrollableFrame->GetScrolledFrame();return!(rootScrolledFrame==aFrame||IsProperAncestorFrame(rootScrolledFrame,aFrame));}/** * Use only for paddings / widths / heights, since it clamps negative calc() to * 0. */template<typenameLengthPercentageLike>staticboolGetAbsoluteCoord(constLengthPercentageLike&aStyle,nscoord&aResult){if(!aStyle.ConvertsToLength()){returnfalse;}aResult=std::max(0,aStyle.ToLength());returntrue;}staticnscoordGetBSizePercentBasisAdjustment(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaHorizontalAxis,boolaResolvesAgainstPaddingBox);staticboolGetPercentBSize(constLengthPercentage&aStyle,nsIFrame*aFrame,boolaHorizontalAxis,nscoord&aResult);// Only call on style coords for which GetAbsoluteCoord returned false.template<typenameSizeOrMaxSize>staticboolGetPercentBSize(constSizeOrMaxSize&aStyle,nsIFrame*aFrame,boolaHorizontalAxis,nscoord&aResult){if(!aStyle.IsLengthPercentage()){returnfalse;}returnGetPercentBSize(aStyle.AsLengthPercentage(),aFrame,aHorizontalAxis,aResult);}staticboolGetPercentBSize(constLengthPercentage&aStyle,nsIFrame*aFrame,boolaHorizontalAxis,nscoord&aResult){if(!aStyle.HasPercent()){returnfalse;}MOZ_ASSERT(!aStyle.ConvertsToLength(),"GetAbsoluteCoord should have handled this");// During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses// SetComputedHeight on the reflow input for its child to propagate its// computed height to the scrolled content. So here we skip to the scroll// frame that contains this scrolled content in order to get the same// behavior as layout when computing percentage heights.nsIFrame*f=aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);if(!f){MOZ_ASSERT_UNREACHABLE("top of frame tree not a containing block");returnfalse;}WritingModewm=f->GetWritingMode();constnsStylePosition*pos=f->StylePosition();constauto&bSizeCoord=pos->BSize(wm);nscoordh;if(!GetAbsoluteCoord(bSizeCoord,h)&&!GetPercentBSize(bSizeCoord,f,aHorizontalAxis,h)){LayoutFrameTypefType=f->Type();if(fType!=LayoutFrameType::Viewport&&fType!=LayoutFrameType::Canvas&&fType!=LayoutFrameType::PageContent){// There's no basis for the percentage height, so it acts like auto.// Should we consider a max-height < min-height pair a basis for// percentage heights? The spec is somewhat unclear, and not doing// so is simpler and avoids troubling discontinuities in behavior,// so I'll choose not to. -LDBreturnfalse;}// For the viewport, canvas, and page-content kids, the percentage// basis is just the parent block-size.h=f->BSize(wm);if(h==NS_UNCONSTRAINEDSIZE){// We don't have a percentage basis after allreturnfalse;}}constauto&maxBSizeCoord=pos->MaxBSize(wm);nscoordmaxh;if(GetAbsoluteCoord(maxBSizeCoord,maxh)||GetPercentBSize(maxBSizeCoord,f,aHorizontalAxis,maxh)){if(maxh<h)h=maxh;}constauto&minBSizeCoord=pos->MinBSize(wm);nscoordminh;if(GetAbsoluteCoord(minBSizeCoord,minh)||GetPercentBSize(minBSizeCoord,f,aHorizontalAxis,minh)){if(minh>h){h=minh;}}// If we're an abspos box, percentages in that case resolve against the// padding box.//// TODO: This could conceivably cause some problems with fieldsets (which are// the other place that wants to ignore padding), but solving that here// without hardcoding a check for f being a fieldset-content frame is a bit of// a pain.constboolresolvesAgainstPaddingBox=aFrame->IsAbsolutelyPositioned();h+=GetBSizePercentBasisAdjustment(pos->mBoxSizing,f,aHorizontalAxis,resolvesAgainstPaddingBox);aResult=std::max(aStyle.Resolve(std::max(h,0)),0);returntrue;}// Return true if aStyle can be resolved to a definite value and if so// return that value in aResult.staticboolGetDefiniteSize(constLengthPercentage&aStyle,nsIFrame*aFrame,boolaIsInlineAxis,constMaybe<LogicalSize>&aPercentageBasis,nscoord*aResult){if(aStyle.ConvertsToLength()){*aResult=aStyle.ToLength();returntrue;}if(!aPercentageBasis){returnfalse;}autowm=aFrame->GetWritingMode();nscoordpb=aIsInlineAxis?aPercentageBasis.value().ISize(wm):aPercentageBasis.value().BSize(wm);if(pb==NS_UNCONSTRAINEDSIZE){returnfalse;}*aResult=std::max(0,aStyle.Resolve(pb));returntrue;}// Return true if aStyle can be resolved to a definite value and if so// return that value in aResult.template<typenameSizeOrMaxSize>staticboolGetDefiniteSize(constSizeOrMaxSize&aStyle,nsIFrame*aFrame,boolaIsInlineAxis,constMaybe<LogicalSize>&aPercentageBasis,nscoord*aResult){if(!aStyle.IsLengthPercentage()){returnfalse;}returnGetDefiniteSize(aStyle.AsLengthPercentage(),aFrame,aIsInlineAxis,aPercentageBasis,aResult);}// NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug// 1363918). Please do not add new uses of this function.//// Get the amount of space to add or subtract out of aFrame's 'block-size' or// property value due its borders and paddings, given the box-sizing value in// aBoxSizing.//// aHorizontalAxis is true if our inline direction is horizontal and our block// direction is vertical. aResolvesAgainstPaddingBox is true if padding should// be added or not removed.staticnscoordGetBSizePercentBasisAdjustment(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaHorizontalAxis,boolaResolvesAgainstPaddingBox){nscoordadjustment=0;if(aBoxSizing==StyleBoxSizing::Border){constauto&border=aFrame->StyleBorder()->GetComputedBorder();adjustment-=aHorizontalAxis?border.TopBottom():border.LeftRight();}if((aBoxSizing==StyleBoxSizing::Border)==!aResolvesAgainstPaddingBox){constauto&stylePadding=aFrame->StylePadding()->mPadding;constLengthPercentage&paddingStart=stylePadding.Get(aHorizontalAxis?eSideTop:eSideLeft);constLengthPercentage&paddingEnd=stylePadding.Get(aHorizontalAxis?eSideBottom:eSideRight);nscoordpad;// XXXbz Calling GetPercentBSize on padding values looks bogus, since// percent padding is always a percentage of the inline-size of the// containing block. We should perhaps just treat non-absolute paddings// here as 0 instead, except that in some cases the width may in fact be// known. See bug 1231059.if(GetAbsoluteCoord(paddingStart,pad)||GetPercentBSize(paddingStart,aFrame,aHorizontalAxis,pad)){adjustment+=aResolvesAgainstPaddingBox?pad:-pad;}if(GetAbsoluteCoord(paddingEnd,pad)||GetPercentBSize(paddingEnd,aFrame,aHorizontalAxis,pad)){adjustment+=aResolvesAgainstPaddingBox?pad:-pad;}}returnadjustment;}// Get the amount of space taken out of aFrame's content area due to its// borders and paddings given the box-sizing value in aBoxSizing. We don't// get aBoxSizing from the frame because some callers want to compute this for// specific box-sizing values.// aIsInlineAxis is true if we're computing for aFrame's inline axis.// aIgnorePadding is true if padding should be ignored.staticnscoordGetDefiniteSizeTakenByBoxSizing(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaIsInlineAxis,boolaIgnorePadding,constMaybe<LogicalSize>&aPercentageBasis){nscoordsizeTakenByBoxSizing=0;if(MOZ_UNLIKELY(aBoxSizing==StyleBoxSizing::Border)){constboolisHorizontalAxis=aIsInlineAxis==!aFrame->GetWritingMode().IsVertical();constnsStyleBorder*styleBorder=aFrame->StyleBorder();sizeTakenByBoxSizing=isHorizontalAxis?styleBorder->GetComputedBorder().LeftRight():styleBorder->GetComputedBorder().TopBottom();if(!aIgnorePadding){constauto&stylePadding=aFrame->StylePadding()->mPadding;constLengthPercentage&pStart=stylePadding.Get(isHorizontalAxis?eSideLeft:eSideTop);constLengthPercentage&pEnd=stylePadding.Get(isHorizontalAxis?eSideRight:eSideBottom);nscoordpad;// XXXbz Calling GetPercentBSize on padding values looks bogus, since// percent padding is always a percentage of the inline-size of the// containing block. We should perhaps just treat non-absolute paddings// here as 0 instead, except that in some cases the width may in fact be// known. See bug 1231059.if(GetDefiniteSize(pStart,aFrame,aIsInlineAxis,aPercentageBasis,&pad)||(aPercentageBasis.isNothing()&&GetPercentBSize(pStart,aFrame,isHorizontalAxis,pad))){sizeTakenByBoxSizing+=pad;}if(GetDefiniteSize(pEnd,aFrame,aIsInlineAxis,aPercentageBasis,&pad)||(aPercentageBasis.isNothing()&&GetPercentBSize(pEnd,aFrame,isHorizontalAxis,pad))){sizeTakenByBoxSizing+=pad;}}}returnsizeTakenByBoxSizing;}// Handles only max-content and min-content, and// -moz-fit-content for min-width and max-width, since the others// (-moz-fit-content for width, and -moz-available) have no effect on// intrinsic widths.staticboolGetIntrinsicCoord(nsIFrame::ExtremumLengthaStyle,gfxContext*aRenderingContext,nsIFrame*aFrame,Maybe<nscoord>aInlineSizeFromAspectRatio,nsIFrame::SizePropertyaProperty,nscoord&aResult){if(aStyle==nsIFrame::ExtremumLength::MozAvailable){returnfalse;}if(aStyle==nsIFrame::ExtremumLength::FitContentFunction){// fit-content() should be handled by the caller.returnfalse;}if(aStyle==nsIFrame::ExtremumLength::MozFitContent){switch(aProperty){casensIFrame::SizeProperty::Size:// handle like 'width: auto'returnfalse;casensIFrame::SizeProperty::MaxSize:// constrain large 'width' values down to max-contentaStyle=nsIFrame::ExtremumLength::MaxContent;break;casensIFrame::SizeProperty::MinSize:// constrain small 'width' or 'max-width' values up to min-contentaStyle=nsIFrame::ExtremumLength::MinContent;break;}}NS_ASSERTION(aStyle==nsIFrame::ExtremumLength::MinContent||aStyle==nsIFrame::ExtremumLength::MaxContent,"should have reduced everything remaining to one of these");// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);if(aInlineSizeFromAspectRatio){aResult=*aInlineSizeFromAspectRatio;}elseif(aStyle==nsIFrame::ExtremumLength::MaxContent){aResult=aFrame->GetPrefISize(aRenderingContext);}else{aResult=aFrame->GetMinISize(aRenderingContext);}returntrue;}template<typenameSizeOrMaxSize>staticboolGetIntrinsicCoord(constSizeOrMaxSize&aStyle,gfxContext*aRenderingContext,nsIFrame*aFrame,Maybe<nscoord>aInlineSizeFromAspectRatio,nsIFrame::SizePropertyaProperty,nscoord&aResult){autolength=nsIFrame::ToExtremumLength(aStyle);if(!length){returnfalse;}returnGetIntrinsicCoord(*length,aRenderingContext,aFrame,aInlineSizeFromAspectRatio,aProperty,aResult);}#undef DEBUG_INTRINSIC_WIDTH#ifdef DEBUG_INTRINSIC_WIDTHstaticint32_tgNoiseIndent=0;#endifstaticnscoordGetFitContentSizeForMaxOrPreferredSize(constIntrinsicISizeTypeaType,constnsIFrame::SizePropertyaProperty,constnsIFrame*aFrame,constLengthPercentage&aStyleSize,constnscoordaInitialValue,constnscoordaMinContentSize,constnscoordaMaxContentSize){MOZ_ASSERT(aProperty!=nsIFrame::SizeProperty::MinSize);nscoordsize=NS_UNCONSTRAINEDSIZE;// 1. Treat fit-content()'s arg as a plain LengthPercentage// However, we have to handle the cyclic percentage contribution first.//// https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contributionif(aType==IntrinsicISizeType::MinISize&&aFrame->IsPercentageResolvedAgainstZero(aStyleSize,aProperty)){// Case (c) in the spec.// FIXME: This doesn't follow the spec for calc(). We should fix this in// Bug 1463700.size=0;}elseif(!GetAbsoluteCoord(aStyleSize,size)){// As initial value. Case (a) and (b) in the spec.size=aInitialValue;}// 2. Clamp size by min-content and max-content.returnstd::max(aMinContentSize,std::min(aMaxContentSize,size));}/** * Add aOffsets which describes what to add on outside of the content box * aContentSize (controlled by 'box-sizing') and apply min/max properties. * We have to account for these properties after getting all the offsets * (margin, border, padding) because percentages do not operate linearly. * Doing this is ok because although percentages aren't handled linearly, * they are handled monotonically. * * @param aContentSize the content size calculated so far (@see IntrinsicForContainer) * @param aContentMinSize ditto min content size * @param aStyleSize a 'width' or 'height' property value * @param aFixedMinSize if aStyleMinSize is a definite size then this points to * the value, otherwise nullptr * @param aStyleMinSize a 'min-width' or 'min-height' property value * @param aFixedMaxSize if aStyleMaxSize is a definite size then this points to * the value, otherwise nullptr * @param aStyleMaxSize a 'max-width' or 'max-height' property value * @param aInlineSizeFromAspectRatio the content-box inline size computed from * aspect-ratio and the definite block size. * We use this value to resolve * {min|max}-content. * @param aFlags same as for IntrinsicForContainer * @param aContainerWM the container's WM */staticnscoordAddIntrinsicSizeOffset(gfxContext*aRenderingContext,nsIFrame*aFrame,constnsIFrame::IntrinsicSizeOffsetData&aOffsets,IntrinsicISizeTypeaType,StyleBoxSizingaBoxSizing,nscoordaContentSize,nscoordaContentMinSize,constStyleSize&aStyleSize,constnscoord*aFixedMinSize,constStyleSize&aStyleMinSize,constnscoord*aFixedMaxSize,constStyleMaxSize&aStyleMaxSize,Maybe<nscoord>aInlineSizeFromAspectRatio,uint32_taFlags,PhysicalAxisaAxis){nscoordresult=aContentSize;nscoordmin=aContentMinSize;nscoordcoordOutsideSize=0;if(!(aFlags&nsLayoutUtils::IGNORE_PADDING)){coordOutsideSize+=aOffsets.padding;}coordOutsideSize+=aOffsets.border;if(aBoxSizing==StyleBoxSizing::Border){min+=coordOutsideSize;result=NSCoordSaturatingAdd(result,coordOutsideSize);coordOutsideSize=0;}coordOutsideSize+=aOffsets.margin;min+=coordOutsideSize;// Compute min-content/max-content for fit-content().nscoordminContent=0;nscoordmaxContent=NS_UNCONSTRAINEDSIZE;if(aStyleSize.IsFitContentFunction()||aStyleMaxSize.IsFitContentFunction()||aStyleMinSize.IsFitContentFunction()){if(aInlineSizeFromAspectRatio){minContent=maxContent=*aInlineSizeFromAspectRatio;}else{minContent=aFrame->GetMinISize(aRenderingContext);maxContent=aFrame->GetPrefISize(aRenderingContext);}}// Compute size.nscoordsize=NS_UNCONSTRAINEDSIZE;if(aType==IntrinsicISizeType::MinISize&&aFrame->IsPercentageResolvedAgainstZero(aStyleSize,aStyleMaxSize)){// XXX bug 1463700: this doesn't handle calc() according to specresult=0;// let |min| handle padding/border/margin}elseif(GetAbsoluteCoord(aStyleSize,size)||GetIntrinsicCoord(aStyleSize,aRenderingContext,aFrame,aInlineSizeFromAspectRatio,nsIFrame::SizeProperty::Size,size)){result=size+coordOutsideSize;}elseif(aStyleSize.IsFitContentFunction()){// |result| here is the content size or border size, depends on// StyleBoxSizing. We use it as the initial value when handling the cyclic// percentage.nscoordinitial=result;nscoordfitContentFuncSize=GetFitContentSizeForMaxOrPreferredSize(aType,nsIFrame::SizeProperty::Size,aFrame,aStyleSize.AsFitContentFunction(),initial,minContent,maxContent);// Add border and padding.result=NSCoordSaturatingAdd(fitContentFuncSize,coordOutsideSize);}else{result=NSCoordSaturatingAdd(result,coordOutsideSize);}// Compute max-size.nscoordmaxSize=aFixedMaxSize?*aFixedMaxSize:0;if(aFixedMaxSize||GetIntrinsicCoord(aStyleMaxSize,aRenderingContext,aFrame,aInlineSizeFromAspectRatio,nsIFrame::SizeProperty::MaxSize,maxSize)){maxSize+=coordOutsideSize;if(result>maxSize){result=maxSize;}}elseif(aStyleMaxSize.IsFitContentFunction()){nscoordfitContentFuncSize=GetFitContentSizeForMaxOrPreferredSize(aType,nsIFrame::SizeProperty::MaxSize,aFrame,aStyleMaxSize.AsFitContentFunction(),NS_UNCONSTRAINEDSIZE,minContent,maxContent);maxSize=NSCoordSaturatingAdd(fitContentFuncSize,coordOutsideSize);if(result>maxSize){result=maxSize;}}// Compute min-size.nscoordminSize=aFixedMinSize?*aFixedMinSize:0;if(aFixedMinSize||GetIntrinsicCoord(aStyleMinSize,aRenderingContext,aFrame,aInlineSizeFromAspectRatio,nsIFrame::SizeProperty::MinSize,minSize)){minSize+=coordOutsideSize;if(result<minSize){result=minSize;}}elseif(aStyleMinSize.IsFitContentFunction()){if(!GetAbsoluteCoord(aStyleMinSize.AsFitContentFunction(),minSize)){// FIXME: Bug 1463700, we should resolve only the percentage part to 0// such as min-width: fit-content(calc(50% + 50px)).minSize=0;}nscoordfitContentFuncSize=std::max(minContent,std::min(maxContent,minSize));minSize=NSCoordSaturatingAdd(fitContentFuncSize,coordOutsideSize);if(result<minSize){result=minSize;}}if(result<min){result=min;}constnsStyleDisplay*disp=aFrame->StyleDisplay();if(aFrame->IsThemed(disp)){LayoutDeviceIntSizedevSize;boolcanOverride=true;nsPresContext*pc=aFrame->PresContext();pc->Theme()->GetMinimumWidgetSize(pc,aFrame,disp->EffectiveAppearance(),&devSize,&canOverride);nscoordthemeSize=pc->DevPixelsToAppUnits(aAxis==eAxisVertical?devSize.height:devSize.width);// GetMinimumWidgetSize() returns a border-box width.themeSize+=aOffsets.margin;if(themeSize>result||!canOverride){result=themeSize;}}returnresult;}staticvoidAddStateBitToAncestors(nsIFrame*aFrame,nsFrameStateaBit){for(nsIFrame*f=aFrame;f;f=f->GetParent()){if(f->HasAnyStateBits(aBit)){break;}f->AddStateBits(aBit);}}/* static */nscoordnsLayoutUtils::IntrinsicForAxis(PhysicalAxisaAxis,gfxContext*aRenderingContext,nsIFrame*aFrame,IntrinsicISizeTypeaType,constMaybe<LogicalSize>&aPercentageBasis,uint32_taFlags,nscoordaMarginBoxMinSizeClamp){MOZ_ASSERT(aFrame,"null frame");MOZ_ASSERT(aFrame->GetParent(),"IntrinsicForAxis called on frame not in tree");MOZ_ASSERT(aFrame->GetParent()->Type()!=LayoutFrameType::GridContainer||aPercentageBasis.isSome(),"grid layout should always pass a percentage basis");constboolhorizontalAxis=MOZ_LIKELY(aAxis==eAxisHorizontal);#ifdef DEBUG_INTRINSIC_WIDTHnsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s %s intrinsic size for container:\n",aType==IntrinsicISizeType::MinISize?"min":"pref",horizontalAxis?"horizontal":"vertical");#endif// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);// We want the size this frame will contribute to the parent's inline-size,// so we work in the parent's writing mode; but if aFrame is orthogonal to// its parent, we'll need to look at its BSize instead of min/pref-ISize.constnsStylePosition*stylePos=aFrame->StylePosition();StyleBoxSizingboxSizing=stylePos->mBoxSizing;StyleSizestyleMinISize=horizontalAxis?stylePos->mMinWidth:stylePos->mMinHeight;StyleSizestyleISize=(aFlags&MIN_INTRINSIC_ISIZE)?styleMinISize:(horizontalAxis?stylePos->mWidth:stylePos->mHeight);MOZ_ASSERT(!(aFlags&MIN_INTRINSIC_ISIZE)||styleISize.IsAuto()||nsIFrame::ToExtremumLength(styleISize),"should only use MIN_INTRINSIC_ISIZE for intrinsic values");StyleMaxSizestyleMaxISize=horizontalAxis?stylePos->mMaxWidth:stylePos->mMaxHeight;PhysicalAxisourInlineAxis=aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);constboolisInlineAxis=aAxis==ourInlineAxis;autoresetIfKeywords=[](StyleSize&aSize,StyleSize&aMinSize,StyleMaxSize&aMaxSize){if(!aSize.IsLengthPercentage()){aSize=StyleSize::Auto();}if(!aMinSize.IsLengthPercentage()){aMinSize=StyleSize::Auto();}if(!aMaxSize.IsLengthPercentage()){aMaxSize=StyleMaxSize::None();}};// According to the spec, max-content and min-content should behave as the// property's initial values in block axis.// It also make senses to use the initial values for -moz-fit-content and// -moz-available for intrinsic size in block axis. Therefore, we reset them// if needed.if(!isInlineAxis){resetIfKeywords(styleISize,styleMinISize,styleMaxISize);}// We build up two values starting with the content box, and then// adding padding, border and margin. The result is normally// |result|. Then, when we handle 'width', 'min-width', and// 'max-width', we use the results we've been building in |min| as a// minimum, overriding 'min-width'. This ensures two things:// * that we don't let a value of 'box-sizing' specifying a width// smaller than the padding/border inside the box-sizing box give// a content width less than zero// * that we prevent tables from becoming smaller than their// intrinsic minimum widthnscoordresult=0,min=0;nscoordmaxISize;boolhaveFixedMaxISize=GetAbsoluteCoord(styleMaxISize,maxISize);nscoordminISize;// Treat "min-width: auto" as 0.boolhaveFixedMinISize;if(styleMinISize.IsAuto()){// NOTE: Technically, "auto" is supposed to behave like "min-content" on// flex items. However, we don't need to worry about that here, because// flex items' min-sizes are intentionally ignored until the flex// container explicitly considers them during space distribution.minISize=0;haveFixedMinISize=true;}else{haveFixedMinISize=GetAbsoluteCoord(styleMinISize,minISize);}autochildWM=aFrame->GetWritingMode();nscoordpmPercentageBasis=NS_UNCONSTRAINEDSIZE;if(aPercentageBasis.isSome()){// The padding/margin percentage basis is the inline-size in the parent's// writing-mode.pmPercentageBasis=aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM)?aPercentageBasis->BSize(childWM):aPercentageBasis->ISize(childWM);}nsIFrame::IntrinsicSizeOffsetDataoffsets=MOZ_LIKELY(isInlineAxis)?aFrame->IntrinsicISizeOffsets(pmPercentageBasis):aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);autogetContentBoxSizeToBoxSizingAdjust=[childWM,&offsets,&aFrame,isInlineAxis,pmPercentageBasis](constStyleBoxSizingaBoxSizing){returnaBoxSizing==StyleBoxSizing::Border?LogicalSize(childWM,(isInlineAxis?offsets:aFrame->IntrinsicISizeOffsets(pmPercentageBasis)).BorderPadding(),(!isInlineAxis?offsets:aFrame->IntrinsicBSizeOffsets(pmPercentageBasis)).BorderPadding()):LogicalSize(childWM);};Maybe<nscoord>inlineSizeFromAspectRatio;Maybe<LogicalSize>contentBoxSizeToBoxSizingAdjust;constboolignorePadding=(aFlags&IGNORE_PADDING)||aFrame->IsAbsolutelyPositioned();// If we have a specified width (or a specified 'min-width' greater// than the specified 'max-width', which works out to the same thing),// don't even bother getting the frame's intrinsic width, because in// this case GetAbsoluteCoord(styleISize, w) will always succeed, so// we'll never need the intrinsic dimensions.if(styleISize.IsMaxContent()||styleISize.IsMinContent()){MOZ_ASSERT(isInlineAxis);// -moz-fit-content and -moz-available enumerated widths compute intrinsic// widths just like auto.// For max-content and min-content, we handle them like// specified widths, but ignore box-sizing.boxSizing=StyleBoxSizing::Content;}elseif(!styleISize.ConvertsToLength()&&!(styleISize.IsFitContentFunction()&&styleISize.AsFitContentFunction().ConvertsToLength())&&!(haveFixedMinISize&&haveFixedMaxISize&&maxISize<=minISize)){#ifdef DEBUG_INTRINSIC_WIDTH++gNoiseIndent;#endifif(MOZ_UNLIKELY(!isInlineAxis)){IntrinsicSizeintrinsicSize=aFrame->GetIntrinsicSize();constauto&intrinsicBSize=horizontalAxis?intrinsicSize.width:intrinsicSize.height;if(intrinsicBSize){result=*intrinsicBSize;}else{// We don't have an intrinsic bsize and we need aFrame's block-dir size.if(aFlags&BAIL_IF_REFLOW_NEEDED){returnNS_INTRINSIC_ISIZE_UNKNOWN;}// XXX Unfortunately, we probably don't know this yet, so this is// wrong... but it's not clear what we should do. If aFrame's inline// size hasn't been determined yet, we can't necessarily figure out its// block size either. For now, authors who put orthogonal elements into// things like buttons or table cells may have to explicitly provide// sizes rather than expecting intrinsic sizing to work "perfectly" in// underspecified cases.result=aFrame->BSize();}}else{result=aType==IntrinsicISizeType::MinISize?aFrame->GetMinISize(aRenderingContext):aFrame->GetPrefISize(aRenderingContext);}#ifdef DEBUG_INTRINSIC_WIDTH--gNoiseIndent;nsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s %s intrinsic size from frame is %d.\n",aType==IntrinsicISizeType::MinISize?"min":"pref",horizontalAxis?"horizontal":"vertical",result);#endif// Handle elements with an intrinsic ratio (or size) and a specified// height, min-height, or max-height.// NOTE:// 1. We treat "min-height:auto" as "0" for the purpose of this code,// since that's what it means in all cases except for on flex items -- and// even there, we're supposed to ignore it (i.e. treat it as 0) until the// flex container explicitly considers it.// 2. The 'B' in |styleBSize|, |styleMinBSize|, and |styleMaxBSize|// represents the ratio-determining axis of |aFrame|. It could be the inline// axis or the block axis of |aFrame|. (So we are calculating the size// along the ratio-dependent axis in this if-branch.)StyleSizestyleBSize=horizontalAxis?stylePos->mHeight:stylePos->mWidth;StyleSizestyleMinBSize=horizontalAxis?stylePos->mMinHeight:stylePos->mMinWidth;StyleMaxSizestyleMaxBSize=horizontalAxis?stylePos->mMaxHeight:stylePos->mMaxWidth;// According to the spec, max-content and min-content should behave as the// property's initial values in block axis.// It also make senses to use the initial values for -moz-fit-content and// -moz-available for intrinsic size in block axis. Therefore, we reset them// if needed.if(isInlineAxis){resetIfKeywords(styleBSize,styleMinBSize,styleMaxBSize);}// FIXME(emilio): Why the minBsize == 0 special-case? Also, shouldn't this// use BehavesLikeInitialValueOnBlockAxis instead?if(!styleBSize.IsAuto()||!(styleMinBSize.IsAuto()||(styleMinBSize.ConvertsToLength()&&styleMinBSize.ToLength()==0))||!styleMaxBSize.IsNone()){if(AspectRatioratio=aFrame->GetAspectRatio()){AddStateBitToAncestors(aFrame,NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);nscoordbSizeTakenByBoxSizing=GetDefiniteSizeTakenByBoxSizing(boxSizing,aFrame,!isInlineAxis,ignorePadding,aPercentageBasis);contentBoxSizeToBoxSizingAdjust.emplace(getContentBoxSizeToBoxSizingAdjust(boxSizing));// NOTE: This is only the minContentSize if we've been passed// MIN_INTRINSIC_ISIZE (which is fine, because this should only be used// inside a check for that flag).nscoordminContentSize=result;nscoordh;if(GetDefiniteSize(styleBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);// We are computing the size of |aFrame|, so we use the inline & block// dimensions of |aFrame|.result=ratio.ComputeRatioDependentSize(isInlineAxis?eLogicalAxisInline:eLogicalAxisBlock,childWM,h,*contentBoxSizeToBoxSizingAdjust);// We have get the inlineSizeForAspectRatio value, so we don't have to// recompute this again below.inlineSizeFromAspectRatio.emplace(result);}if(GetDefiniteSize(styleMaxBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleMaxBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);nscoordmaxISize=ratio.ComputeRatioDependentSize(isInlineAxis?eLogicalAxisInline:eLogicalAxisBlock,childWM,h,*contentBoxSizeToBoxSizingAdjust);if(maxISize<result){result=maxISize;}if(maxISize<minContentSize){minContentSize=maxISize;}}if(GetDefiniteSize(styleMinBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleMinBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);nscoordminISize=ratio.ComputeRatioDependentSize(isInlineAxis?eLogicalAxisInline:eLogicalAxisBlock,childWM,h,*contentBoxSizeToBoxSizingAdjust);if(minISize>result){result=minISize;}if(minISize>minContentSize){minContentSize=minISize;}}if(MOZ_UNLIKELY(aFlags&nsLayoutUtils::MIN_INTRINSIC_ISIZE)&&// FIXME: Bug 1715681. Should we use eReplacedSizing instead// because eReplaced is set on some other frames which are// non-replaced elements, e.g. <select>?aFrame->IsFrameOfType(nsIFrame::eReplaced)){// This is the 'min-width/height:auto' "transferred size" piece of:// https://drafts.csswg.org/css-flexbox-1/#min-size-auto// https://drafts.csswg.org/css-grid/#min-size-auto// Per spec, we handle it only for replaced elements.result=std::min(result,minContentSize);}}}}if(aFrame->IsTableFrame()){// Tables can't shrink smaller than their intrinsic minimum width,// no matter what.min=aFrame->GetMinISize(aRenderingContext);}// If we have an aspect-ratio and a definite block size of |aFrame|, we// resolve the {min|max}-content size by the aspect-ratio and the block size.// If |aAxis| is not the inline axis of |aFrame|, {min|max}-content should// behaves as auto, so we don't need this.//// FIXME(emilio): For -moz-available it seems we shouldn't need this.//// https://github.com/w3c/csswg-drafts/issues/5032// FIXME: Bug 1670151: Use GetAspectRatio() to cover replaced elements (and// then we can drop the check of eSupportsAspectRatio).constAspectRatioar=stylePos->mAspectRatio.ToLayoutRatio();if(isInlineAxis&&ar&&nsIFrame::ToExtremumLength(styleISize)&&aFrame->IsFrameOfType(nsIFrame::eSupportsAspectRatio)&&!inlineSizeFromAspectRatio){// This 'B' in |styleBSize| means the block size of |aFrame|. We go into// this branch only if |aAxis| is the inline axis of |aFrame|.constStyleSize&styleBSize=horizontalAxis?stylePos->mHeight:stylePos->mWidth;nscoordbSize;if(GetDefiniteSize(styleBSize,aFrame,!isInlineAxis,aPercentageBasis,&bSize)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleBSize,aFrame,horizontalAxis,bSize))){// We cannot reuse |boxSizing| because it may be updated to content-box// in the above if-branch.constStyleBoxSizingboxSizingForAR=stylePos->mBoxSizing;if(!contentBoxSizeToBoxSizingAdjust){contentBoxSizeToBoxSizingAdjust.emplace(getContentBoxSizeToBoxSizingAdjust(boxSizingForAR));}nscoordbSizeTakenByBoxSizing=GetDefiniteSizeTakenByBoxSizing(boxSizingForAR,aFrame,!isInlineAxis,ignorePadding,aPercentageBasis);bSize-=bSizeTakenByBoxSizing;inlineSizeFromAspectRatio.emplace(ar.ComputeRatioDependentSize(LogicalAxis::eLogicalAxisInline,childWM,bSize,*contentBoxSizeToBoxSizingAdjust));}}nscoordcontentBoxSize=result;result=AddIntrinsicSizeOffset(aRenderingContext,aFrame,offsets,aType,boxSizing,result,min,styleISize,haveFixedMinISize?&minISize:nullptr,styleMinISize,haveFixedMaxISize?&maxISize:nullptr,styleMaxISize,inlineSizeFromAspectRatio,aFlags,aAxis);nscoordoverflow=result-aMarginBoxMinSizeClamp;if(MOZ_UNLIKELY(overflow>0)){nscoordnewContentBoxSize=std::max(nscoord(0),contentBoxSize-overflow);result-=contentBoxSize-newContentBoxSize;}#ifdef DEBUG_INTRINSIC_WIDTHnsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s %s intrinsic size for container is %d twips.\n",aType==IntrinsicISizeType::MinISize?"min":"pref",horizontalAxis?"horizontal":"vertical",result);#endifreturnresult;}/* static */nscoordnsLayoutUtils::IntrinsicForContainer(gfxContext*aRenderingContext,nsIFrame*aFrame,IntrinsicISizeTypeaType,uint32_taFlags){MOZ_ASSERT(aFrame&&aFrame->GetParent());// We want the size aFrame will contribute to its parent's inline-size.PhysicalAxisaxis=aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline);returnIntrinsicForAxis(axis,aRenderingContext,aFrame,aType,Nothing(),aFlags);}/* static */nscoordnsLayoutUtils::MinSizeContributionForAxis(PhysicalAxisaAxis,gfxContext*aRC,nsIFrame*aFrame,IntrinsicISizeTypeaType,constLogicalSize&aPercentageBasis,uint32_taFlags){MOZ_ASSERT(aFrame);MOZ_ASSERT(aFrame->IsFlexOrGridItem(),"only grid/flex items have this behavior currently");#ifdef DEBUG_INTRINSIC_WIDTHnsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s min-isize for %s WM:\n",aType==IntrinsicISizeType::MinISize?"min":"pref",aAxis==eAxisVertical?"vertical":"horizontal");#endif// Note: this method is only meant for grid/flex items.constnsStylePosition*conststylePos=aFrame->StylePosition();StyleSizesize=aAxis==eAxisHorizontal?stylePos->mMinWidth:stylePos->mMinHeight;StyleMaxSizemaxSize=aAxis==eAxisHorizontal?stylePos->mMaxWidth:stylePos->mMaxHeight;autochildWM=aFrame->GetWritingMode();PhysicalAxisourInlineAxis=childWM.PhysicalAxis(eLogicalAxisInline);// According to the spec, max-content and min-content should behave as the// property's initial values in block axis.// It also make senses to use the initial values for -moz-fit-content and// -moz-available for intrinsic size in block axis. Therefore, we reset them// if needed.if(aAxis!=ourInlineAxis){if(size.BehavesLikeInitialValueOnBlockAxis()){size=StyleSize::Auto();}if(maxSize.BehavesLikeInitialValueOnBlockAxis()){maxSize=StyleMaxSize::None();}}nscoordminSize;nscoord*fixedMinSize=nullptr;if(size.IsAuto()){if(aFrame->StyleDisplay()->mOverflowX==StyleOverflow::Visible){size=aAxis==eAxisHorizontal?stylePos->mWidth:stylePos->mHeight;// This is same as above: keywords should behaves as property's initial// values in block axis.if(aAxis!=ourInlineAxis&&size.BehavesLikeInitialValueOnBlockAxis()){size=StyleSize::Auto();}if(GetAbsoluteCoord(size,minSize)){// We have a definite width/height. This is the "specified size" in:// https://drafts.csswg.org/css-grid/#min-size-autofixedMinSize=&minSize;}elseif(aFrame->IsPercentageResolvedAgainstZero(size,maxSize)){// XXX bug 1463700: this doesn't handle calc() according to specminSize=0;fixedMinSize=&minSize;}// fall through - the caller will have to deal with "transferred size"}else{// min-[width|height]:auto with overflow != visible computes to zero.minSize=0;fixedMinSize=&minSize;}}elseif(GetAbsoluteCoord(size,minSize)){fixedMinSize=&minSize;}elseif(size.IsLengthPercentage()){MOZ_ASSERT(size.HasPercent());minSize=0;fixedMinSize=&minSize;}if(!fixedMinSize){// Let the caller deal with the "content size" cases.#ifdef DEBUG_INTRINSIC_WIDTHnsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s min-isize is indefinite.\n",aType==IntrinsicISizeType::MinISize?"min":"pref");#endifreturnNS_UNCONSTRAINEDSIZE;}// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);// The padding/margin percentage basis is the inline-size in the parent's// writing-mode.nscoordpmPercentageBasis=aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM)?aPercentageBasis.BSize(childWM):aPercentageBasis.ISize(childWM);nsIFrame::IntrinsicSizeOffsetDataoffsets=ourInlineAxis==aAxis?aFrame->IntrinsicISizeOffsets(pmPercentageBasis):aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);nscoordresult=0;nscoordmin=0;// Note: aInlineSizeFromAspectRatio is Nothing() here because we don't handle// "content size" cases here (i.e. |fixedMinSize| is false here).result=AddIntrinsicSizeOffset(aRC,aFrame,offsets,aType,stylePos->mBoxSizing,result,min,size,fixedMinSize,size,nullptr,maxSize,Nothing(),aFlags,aAxis);#ifdef DEBUG_INTRINSIC_WIDTHnsIFrame::IndentBy(stderr,gNoiseIndent);aFrame->ListTag(stderr);printf_stderr(" %s min-isize is %d twips.\n",aType==IntrinsicISizeType::MinISize?"min":"pref",result);#endifreturnresult;}/* static */nscoordnsLayoutUtils::ComputeBSizeDependentValue(nscoordaContainingBlockBSize,constLengthPercentageOrAuto&aCoord){// XXXldb Some callers explicitly check aContainingBlockBSize// against NS_UNCONSTRAINEDSIZE *and* unit against eStyleUnit_Percent or// calc()s containing percents before calling this function.// However, it would be much more likely to catch problems without// the unit conditions.// XXXldb Many callers pass a non-'auto' containing block height when// according to CSS2.1 they should be passing 'auto'.MOZ_ASSERT(NS_UNCONSTRAINEDSIZE!=aContainingBlockBSize||!aCoord.HasPercent(),"unexpected containing block block-size");if(aCoord.IsAuto()){return0;}returnaCoord.AsLengthPercentage().Resolve(aContainingBlockBSize);}/* static */voidnsLayoutUtils::MarkDescendantsDirty(nsIFrame*aSubtreeRoot){AutoTArray<nsIFrame*,4>subtrees;subtrees.AppendElement(aSubtreeRoot);// dirty descendants, iterating over subtrees that may include// additional subtrees associated with placeholdersdo{nsIFrame*subtreeRoot=subtrees.PopLastElement();// Mark all descendants dirty (using an nsTArray stack rather than// recursion).// Note that ReflowInput::InitResizeFlags has some similar// code; see comments there for how and why it differs.AutoTArray<nsIFrame*,32>stack;stack.AppendElement(subtreeRoot);do{nsIFrame*f=stack.PopLastElement();f->MarkIntrinsicISizesDirty();if(f->IsPlaceholderFrame()){nsIFrame*oof=nsPlaceholderFrame::GetRealFrameForPlaceholder(f);if(!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot,oof)){// We have another distinct subtree we need to mark.subtrees.AppendElement(oof);}}for(constauto&childList:f->ChildLists()){for(nsIFrame*kid:childList.mList){stack.AppendElement(kid);}}}while(stack.Length()!=0);}while(subtrees.Length()!=0);}/* static */voidnsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame*aFrame){AutoTArray<nsIFrame*,32>stack;stack.AppendElement(aFrame);do{nsIFrame*f=stack.PopLastElement();if(!f->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)){continue;}f->MarkIntrinsicISizesDirty();for(constauto&childList:f->ChildLists()){for(nsIFrame*kid:childList.mList){stack.AppendElement(kid);}}}while(stack.Length()!=0);}nsSizensLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoordminWidth,nscoordminHeight,nscoordmaxWidth,nscoordmaxHeight,nscoordtentWidth,nscoordtentHeight){// Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:if(minWidth>maxWidth)maxWidth=minWidth;if(minHeight>maxHeight)maxHeight=minHeight;nscoordheightAtMaxWidth,heightAtMinWidth,widthAtMaxHeight,widthAtMinHeight;if(tentWidth>0){heightAtMaxWidth=NSCoordMulDiv(maxWidth,tentHeight,tentWidth);if(heightAtMaxWidth<minHeight)heightAtMaxWidth=minHeight;heightAtMinWidth=NSCoordMulDiv(minWidth,tentHeight,tentWidth);if(heightAtMinWidth>maxHeight)heightAtMinWidth=maxHeight;}else{heightAtMaxWidth=heightAtMinWidth=NS_CSS_MINMAX(tentHeight,minHeight,maxHeight);}if(tentHeight>0){widthAtMaxHeight=NSCoordMulDiv(maxHeight,tentWidth,tentHeight);if(widthAtMaxHeight<minWidth)widthAtMaxHeight=minWidth;widthAtMinHeight=NSCoordMulDiv(minHeight,tentWidth,tentHeight);if(widthAtMinHeight>maxWidth)widthAtMinHeight=maxWidth;}else{widthAtMaxHeight=widthAtMinHeight=NS_CSS_MINMAX(tentWidth,minWidth,maxWidth);}// The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :nscoordwidth,height;if(tentWidth>maxWidth){if(tentHeight>maxHeight){if(int64_t(maxWidth)*int64_t(tentHeight)<=int64_t(maxHeight)*int64_t(tentWidth)){width=maxWidth;height=heightAtMaxWidth;}else{width=widthAtMaxHeight;height=maxHeight;}}else{// This also covers "(w > max-width) and (h < min-height)" since in// that case (max-width/w < 1), and with (h < min-height):// max(max-width * h/w, min-height) == min-heightwidth=maxWidth;height=heightAtMaxWidth;}}elseif(tentWidth<minWidth){if(tentHeight<minHeight){if(int64_t(minWidth)*int64_t(tentHeight)<=int64_t(minHeight)*int64_t(tentWidth)){width=widthAtMinHeight;height=minHeight;}else{width=minWidth;height=heightAtMinWidth;}}else{// This also covers "(w < min-width) and (h > max-height)" since in// that case (min-width/w > 1), and with (h > max-height):// min(min-width * h/w, max-height) == max-heightwidth=minWidth;height=heightAtMinWidth;}}else{if(tentHeight>maxHeight){width=widthAtMaxHeight;height=maxHeight;}elseif(tentHeight<minHeight){width=widthAtMinHeight;height=minHeight;}else{width=tentWidth;height=tentHeight;}}returnnsSize(width,height);}/* static */nscoordnsLayoutUtils::MinISizeFromInline(nsIFrame*aFrame,gfxContext*aRenderingContext){NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),"should not be container for font size inflation");nsIFrame::InlineMinISizeDatadata;DISPLAY_MIN_INLINE_SIZE(aFrame,data.mPrevLines);aFrame->AddInlineMinISize(aRenderingContext,&data);data.ForceBreak();returndata.mPrevLines;}/* static */nscoordnsLayoutUtils::PrefISizeFromInline(nsIFrame*aFrame,gfxContext*aRenderingContext){NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),"should not be container for font size inflation");nsIFrame::InlinePrefISizeDatadata;DISPLAY_PREF_INLINE_SIZE(aFrame,data.mPrevLines);aFrame->AddInlinePrefISize(aRenderingContext,&data);data.ForceBreak();returndata.mPrevLines;}staticnscolorDarkenColor(nscoloraColor){uint16_thue,sat,value;uint8_talpha;// convert the RBG to HSV so we can get the lightness (which is the v)NS_RGB2HSV(aColor,hue,sat,value,alpha);// The goal here is to send white to black while letting colored// stuff stay colored... So we adopt the following approach.// Something with sat = 0 should end up with value = 0. Something// with a high sat can end up with a high value and it's ok.... At// the same time, we don't want to make things lighter. Do// something simple, since it seems to work.if(value>sat){value=sat;// convert this color back into the RGB color space.NS_HSV2RGB(aColor,hue,sat,value,alpha);}returnaColor;}// Check whether we should darken text/decoration colors. We need to do this if// background images and colors are being suppressed, because that means// light text will not be visible against the (presumed light-colored)// background.staticboolShouldDarkenColors(nsIFrame*aFrame){nsPresContext*pc=aFrame->PresContext();if(pc->GetBackgroundColorDraw()||pc->GetBackgroundImageDraw()){returnfalse;}returnaFrame->StyleVisibility()->mColorAdjust!=StyleColorAdjust::Exact;}nscolornsLayoutUtils::DarkenColorIfNeeded(nsIFrame*aFrame,nscoloraColor){returnShouldDarkenColors(aFrame)?DarkenColor(aColor):aColor;}gfxFloatnsLayoutUtils::GetSnappedBaselineY(nsIFrame*aFrame,gfxContext*aContext,nscoordaY,nscoordaAscent){gfxFloatappUnitsPerDevUnit=aFrame->PresContext()->AppUnitsPerDevPixel();gfxFloatbaseline=gfxFloat(aY)+aAscent;gfxRectputativeRect(0,baseline/appUnitsPerDevUnit,1,1);if(!aContext->UserToDevicePixelSnapped(putativeRect,gfxContext::SnapOption::IgnoreScale)){returnbaseline;}returnaContext->DeviceToUser(putativeRect.TopLeft()).y*appUnitsPerDevUnit;}gfxFloatnsLayoutUtils::GetSnappedBaselineX(nsIFrame*aFrame,gfxContext*aContext,nscoordaX,nscoordaAscent){gfxFloatappUnitsPerDevUnit=aFrame->PresContext()->AppUnitsPerDevPixel();gfxFloatbaseline=gfxFloat(aX)+aAscent;gfxRectputativeRect(baseline/appUnitsPerDevUnit,0,1,1);if(!aContext->UserToDevicePixelSnapped(putativeRect,gfxContext::SnapOption::IgnoreScale)){returnbaseline;}returnaContext->DeviceToUser(putativeRect.TopLeft()).x*appUnitsPerDevUnit;}// Hard limit substring lengths to 8000 characters ... this lets us statically// size the cluster buffer array in FindSafeLength#define MAX_GFX_TEXT_BUF_SIZE 8000staticint32_tFindSafeLength(constchar16_t*aString,uint32_taLength,uint32_taMaxChunkLength){if(aLength<=aMaxChunkLength)returnaLength;int32_tlen=aMaxChunkLength;// Ensure that we don't break inside a surrogate pairwhile(len>0&&NS_IS_LOW_SURROGATE(aString[len])){len--;}if(len==0){// We don't want our caller to go into an infinite loop, so don't// return zero. It's hard to imagine how we could actually get here// unless there are languages that allow clusters of arbitrary size.// If there are and someone feeds us a 500+ character cluster, too// bad.returnaMaxChunkLength;}returnlen;}staticint32_tGetMaxChunkLength(nsFontMetrics&aFontMetrics){returnstd::min(aFontMetrics.GetMaxStringLength(),MAX_GFX_TEXT_BUF_SIZE);}nscoordnsLayoutUtils::AppUnitWidthOfString(constchar16_t*aString,uint32_taLength,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget){uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);nscoordwidth=0;while(aLength>0){int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);width+=aFontMetrics.GetWidth(aString,len,aDrawTarget);aLength-=len;aString+=len;}returnwidth;}nscoordnsLayoutUtils::AppUnitWidthOfStringBidi(constchar16_t*aString,uint32_taLength,constnsIFrame*aFrame,nsFontMetrics&aFontMetrics,gfxContext&aContext){nsPresContext*presContext=aFrame->PresContext();if(presContext->BidiEnabled()){nsBidiLevellevel=nsBidiPresUtils::BidiLevelFromStyle(aFrame->Style());returnnsBidiPresUtils::MeasureTextWidth(aString,aLength,level,presContext,aContext,aFontMetrics);}aFontMetrics.SetTextRunRTL(false);aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);returnnsLayoutUtils::AppUnitWidthOfString(aString,aLength,aFontMetrics,aContext.GetDrawTarget());}boolnsLayoutUtils::StringWidthIsGreaterThan(constnsString&aString,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget,nscoordaWidth){constchar16_t*string=aString.get();uint32_tlength=aString.Length();uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);nscoordwidth=0;while(length>0){int32_tlen=FindSafeLength(string,length,maxChunkLength);width+=aFontMetrics.GetWidth(string,len,aDrawTarget);if(width>aWidth){returntrue;}length-=len;string+=len;}returnfalse;}nsBoundingMetricsnsLayoutUtils::AppUnitBoundsOfString(constchar16_t*aString,uint32_taLength,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget){uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);// Assign directly in the first iteration. This ensures that// negative ascent/descent can be returned and the left bearing// is properly initialized.nsBoundingMetricstotalMetrics=aFontMetrics.GetBoundingMetrics(aString,len,aDrawTarget);aLength-=len;aString+=len;while(aLength>0){len=FindSafeLength(aString,aLength,maxChunkLength);nsBoundingMetricsmetrics=aFontMetrics.GetBoundingMetrics(aString,len,aDrawTarget);totalMetrics+=metrics;aLength-=len;aString+=len;}returntotalMetrics;}voidnsLayoutUtils::DrawString(constnsIFrame*aFrame,nsFontMetrics&aFontMetrics,gfxContext*aContext,constchar16_t*aString,int32_taLength,nsPointaPoint,ComputedStyle*aComputedStyle,DrawStringFlagsaFlags){nsresultrv=NS_ERROR_FAILURE;// If caller didn't pass a style, use the frame's.if(!aComputedStyle){aComputedStyle=aFrame->Style();}if(aFlags&DrawStringFlags::ForceHorizontal){aFontMetrics.SetVertical(false);}else{aFontMetrics.SetVertical(WritingMode(aComputedStyle).IsVertical());}aFontMetrics.SetTextOrientation(aComputedStyle->StyleVisibility()->mTextOrientation);nsPresContext*presContext=aFrame->PresContext();if(presContext->BidiEnabled()){nsBidiLevellevel=nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle);rv=nsBidiPresUtils::RenderText(aString,aLength,level,presContext,*aContext,aContext->GetDrawTarget(),aFontMetrics,aPoint.x,aPoint.y);}if(NS_FAILED(rv)){aFontMetrics.SetTextRunRTL(false);DrawUniDirString(aString,aLength,aPoint,aFontMetrics,*aContext);}}voidnsLayoutUtils::DrawUniDirString(constchar16_t*aString,uint32_taLength,constnsPoint&aPoint,nsFontMetrics&aFontMetrics,gfxContext&aContext){nscoordx=aPoint.x;nscoordy=aPoint.y;uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);if(aLength<=maxChunkLength){aFontMetrics.DrawString(aString,aLength,x,y,&aContext,aContext.GetDrawTarget());return;}boolisRTL=aFontMetrics.GetTextRunRTL();// If we're drawing right to left, we must start at the end.if(isRTL){x+=nsLayoutUtils::AppUnitWidthOfString(aString,aLength,aFontMetrics,aContext.GetDrawTarget());}while(aLength>0){int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);nscoordwidth=aFontMetrics.GetWidth(aString,len,aContext.GetDrawTarget());if(isRTL){x-=width;}aFontMetrics.DrawString(aString,len,x,y,&aContext,aContext.GetDrawTarget());if(!isRTL){x+=width;}aLength-=len;aString+=len;}}/* static */voidnsLayoutUtils::PaintTextShadow(constnsIFrame*aFrame,gfxContext*aContext,constnsRect&aTextRect,constnsRect&aDirtyRect,constnscolor&aForegroundColor,TextShadowCallbackaCallback,void*aCallbackData){constnsStyleText*textStyle=aFrame->StyleText();autoshadows=textStyle->mTextShadow.AsSpan();if(shadows.IsEmpty()){return;}// Text shadow happens with the last value being painted at the back,// ie. it is painted first.gfxContext*aDestCtx=aContext;for(auto&shadow:Reversed(shadows)){nsPointshadowOffset(shadow.horizontal.ToAppUnits(),shadow.vertical.ToAppUnits());nscoordblurRadius=std::max(shadow.blur.ToAppUnits(),0);nsRectshadowRect(aTextRect);shadowRect.MoveBy(shadowOffset);nsPresContext*presCtx=aFrame->PresContext();nsContextBoxBlurcontextBoxBlur;nscolorshadowColor=shadow.color.CalcColor(aForegroundColor);// Webrender just needs the shadow detailsif(auto*textDrawer=aContext->GetTextDrawer()){wr::ShadowwrShadow;wrShadow.offset={presCtx->AppUnitsToFloatDevPixels(shadow.horizontal.ToAppUnits()),presCtx->AppUnitsToFloatDevPixels(shadow.vertical.ToAppUnits())};wrShadow.blur_radius=presCtx->AppUnitsToFloatDevPixels(blurRadius);wrShadow.color=wr::ToColorF(ToDeviceColor(shadowColor));// Gecko already inflates the bounding rect of text shadows,// so tell WR not to inflate again.boolinflate=false;textDrawer->AppendShadow(wrShadow,inflate);continue;}gfxContext*shadowContext=contextBoxBlur.Init(shadowRect,0,blurRadius,presCtx->AppUnitsPerDevPixel(),aDestCtx,aDirtyRect,nullptr,nsContextBoxBlur::DISABLE_HARDWARE_ACCELERATION_BLUR);if(!shadowContext)continue;aDestCtx->Save();aDestCtx->NewPath();aDestCtx->SetColor(sRGBColor::FromABGR(shadowColor));// The callback will draw whatever we want to blur as a shadow.aCallback(shadowContext,shadowOffset,shadowColor,aCallbackData);contextBoxBlur.DoPaint();aDestCtx->Restore();}}/* static */nscoordnsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics*aFontMetrics,nscoordaLineHeight,boolaIsInverted){nscoordfontAscent=aIsInverted?aFontMetrics->MaxDescent():aFontMetrics->MaxAscent();nscoordfontHeight=aFontMetrics->MaxHeight();nscoordleading=aLineHeight-fontHeight;returnfontAscent+leading/2;}/* static */boolnsLayoutUtils::GetFirstLineBaseline(WritingModeaWritingMode,constnsIFrame*aFrame,nscoord*aResult){LinePositionposition;if(!GetFirstLinePosition(aWritingMode,aFrame,&position))returnfalse;*aResult=position.mBaseline;returntrue;}/* static */boolnsLayoutUtils::GetFirstLinePosition(WritingModeaWM,constnsIFrame*aFrame,LinePosition*aResult){if(aFrame->StyleDisplay()->IsContainLayout()){returnfalse;}constnsBlockFrame*block=do_QueryFrame(aFrame);if(!block){// For the first-line baseline we also have to check for a table, and if// so, use the baseline of its first row.LayoutFrameTypefType=aFrame->Type();if(fType==LayoutFrameType::TableWrapper||fType==LayoutFrameType::FlexContainer||fType==LayoutFrameType::GridContainer){if((fType==LayoutFrameType::GridContainer&&aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE))||(fType==LayoutFrameType::FlexContainer&&aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE))||(fType==LayoutFrameType::TableWrapper&&static_cast<constnsTableWrapperFrame*>(aFrame)->GetRowCount()==0)){// empty grid/flex/table containeraResult->mBStart=0;aResult->mBaseline=aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,BaselineSharingGroup::First);aResult->mBEnd=aFrame->BSize(aWM);returntrue;}aResult->mBStart=0;aResult->mBaseline=aFrame->GetLogicalBaseline(aWM);// This is what we want for the list bullet caller; not sure if// other future callers will want the same.aResult->mBEnd=aFrame->BSize(aWM);returntrue;}// For first-line baselines, we have to consider scroll frames.if(fType==LayoutFrameType::Scroll){nsIScrollableFrame*sFrame=do_QueryFrame(const_cast<nsIFrame*>(aFrame));if(!sFrame){MOZ_ASSERT_UNREACHABLE("not scroll frame");}LinePositionkidPosition;if(GetFirstLinePosition(aWM,sFrame->GetScrolledFrame(),&kidPosition)){// Consider only the border and padding that contributes to the// kid's position, not the scrolling, so we get the initial// position.*aResult=kidPosition+aFrame->GetLogicalUsedBorderAndPadding(aWM).BStart(aWM);returntrue;}returnfalse;}if(fType==LayoutFrameType::FieldSet||fType==LayoutFrameType::ColumnSet){LinePositionkidPosition;nsIFrame*kid=aFrame->PrincipalChildList().FirstChild();// If aFrame is fieldset, kid might be a legend frame here, but that's ok.if(kid&&GetFirstLinePosition(aWM,kid,&kidPosition)){*aResult=kidPosition+kid->GetLogicalNormalPosition(aWM,aFrame->GetSize()).B(aWM);returntrue;}returnfalse;}// No baseline.returnfalse;}for(constauto&line:block->Lines()){if(line.IsBlock()){constnsIFrame*kid=line.mFirstChild;LinePositionkidPosition;if(GetFirstLinePosition(aWM,kid,&kidPosition)){// XXX Not sure if this is the correct value to use for container// width here. It will only be used in vertical-rl layout,// which we don't have full support and testing for yet.constauto&containerSize=line.mContainerSize;*aResult=kidPosition+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}}else{// XXX Is this the right test? We have some bogus empty lines// floating around, but IsEmpty is perhaps too weak.if(0!=line.BSize()||!line.IsEmpty()){nscoordbStart=line.BStart();aResult->mBStart=bStart;aResult->mBaseline=bStart+line.GetLogicalAscent();aResult->mBEnd=bStart+line.BSize();returntrue;}}}returnfalse;}/* static */boolnsLayoutUtils::GetLastLineBaseline(WritingModeaWM,constnsIFrame*aFrame,nscoord*aResult){if(aFrame->StyleDisplay()->IsContainLayout()){returnfalse;}constnsBlockFrame*block=do_QueryFrame(aFrame);if(!block)// No baseline. (We intentionally don't descend into scroll frames.)returnfalse;for(nsBlockFrame::ConstReverseLineIteratorline=block->LinesRBegin(),line_end=block->LinesREnd();line!=line_end;++line){if(line->IsBlock()){nsIFrame*kid=line->mFirstChild;nscoordkidBaseline;constnsSize&containerSize=line->mContainerSize;if(GetLastLineBaseline(aWM,kid,&kidBaseline)){// Ignore relative positioning for baseline calculations*aResult=kidBaseline+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}elseif(kid->IsScrollFrame()){// Defer to nsIFrame::GetLogicalBaseline (which synthesizes a baseline// from the margin-box).kidBaseline=kid->GetLogicalBaseline(aWM);*aResult=kidBaseline+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}}else{// XXX Is this the right test? We have some bogus empty lines// floating around, but IsEmpty is perhaps too weak.if(line->BSize()!=0||!line->IsEmpty()){*aResult=line->BStart()+line->GetLogicalAscent();returntrue;}}}returnfalse;}staticnscoordCalculateBlockContentBEnd(WritingModeaWM,nsBlockFrame*aFrame){MOZ_ASSERT(aFrame,"null ptr");nscoordcontentBEnd=0;for(constauto&line:aFrame->Lines()){if(line.IsBlock()){nsIFrame*child=line.mFirstChild;constauto&containerSize=line.mContainerSize;nscoordoffset=child->GetLogicalNormalPosition(aWM,containerSize).B(aWM);contentBEnd=std::max(contentBEnd,nsLayoutUtils::CalculateContentBEnd(aWM,child)+offset);}else{contentBEnd=std::max(contentBEnd,line.BEnd());}}returncontentBEnd;}/* static */nscoordnsLayoutUtils::CalculateContentBEnd(WritingModeaWM,nsIFrame*aFrame){MOZ_ASSERT(aFrame,"null ptr");nscoordcontentBEnd=aFrame->BSize(aWM);// We want scrollable overflow rather than visual because this// calculation is intended to affect layout.LogicalSizeoverflowSize(aWM,aFrame->ScrollableOverflowRect().Size());if(overflowSize.BSize(aWM)>contentBEnd){nsIFrame::ChildListIDsskip={nsIFrame::kOverflowList,nsIFrame::kExcessOverflowContainersList,nsIFrame::kOverflowOutOfFlowList};nsBlockFrame*blockFrame=do_QueryFrame(aFrame);if(blockFrame){contentBEnd=std::max(contentBEnd,CalculateBlockContentBEnd(aWM,blockFrame));skip+=nsIFrame::kPrincipalList;}for(constauto&[list,listID]:aFrame->ChildLists()){if(!skip.contains(listID)){for(nsIFrame*child:list){nscoordoffset=child->GetLogicalNormalPosition(aWM,aFrame->GetSize()).B(aWM);contentBEnd=std::max(contentBEnd,CalculateContentBEnd(aWM,child)+offset);}}}}returncontentBEnd;}/* static */nsIFrame*nsLayoutUtils::GetClosestLayer(nsIFrame*aFrame){nsIFrame*layer;for(layer=aFrame;layer;layer=layer->GetParent()){if(layer->IsAbsPosContainingBlock()||(layer->GetParent()&&layer->GetParent()->IsScrollFrame()))break;}if(layer)returnlayer;returnaFrame->PresShell()->GetRootFrame();}SamplingFilternsLayoutUtils::GetSamplingFilterForFrame(nsIFrame*aForFrame){switch(aForFrame->UsedImageRendering()){caseStyleImageRendering::Smooth:caseStyleImageRendering::Optimizequality:returnSamplingFilter::LINEAR;caseStyleImageRendering::CrispEdges:caseStyleImageRendering::Optimizespeed:caseStyleImageRendering::Pixelated:returnSamplingFilter::POINT;caseStyleImageRendering::Auto:returnSamplingFilter::GOOD;}MOZ_ASSERT_UNREACHABLE("Unknown image-rendering value");returnSamplingFilter::GOOD;}/** * Given an image being drawn into an appunit coordinate system, and * a point in that coordinate system, map the point back into image * pixel space. * @param aSize the size of the image, in pixels * @param aDest the rectangle that the image is being mapped into * @param aPt a point in the same coordinate system as the rectangle */staticgfxPointMapToFloatImagePixels(constgfxSize&aSize,constgfxRect&aDest,constgfxPoint&aPt){returngfxPoint(((aPt.x-aDest.X())*aSize.width)/aDest.Width(),((aPt.y-aDest.Y())*aSize.height)/aDest.Height());}/** * Given an image being drawn into an pixel-based coordinate system, and * a point in image space, map the point into the pixel-based coordinate * system. * @param aSize the size of the image, in pixels * @param aDest the rectangle that the image is being mapped into * @param aPt a point in image space */staticgfxPointMapToFloatUserPixels(constgfxSize&aSize,constgfxRect&aDest,constgfxPoint&aPt){returngfxPoint(aPt.x*aDest.Width()/aSize.width+aDest.X(),aPt.y*aDest.Height()/aSize.height+aDest.Y());}/* static */gfxRectnsLayoutUtils::RectToGfxRect(constnsRect&aRect,int32_taAppUnitsPerDevPixel){returngfxRect(gfxFloat(aRect.x)/aAppUnitsPerDevPixel,gfxFloat(aRect.y)/aAppUnitsPerDevPixel,gfxFloat(aRect.width)/aAppUnitsPerDevPixel,gfxFloat(aRect.height)/aAppUnitsPerDevPixel);}structSnappedImageDrawingParameters{// A transform from image space to device space.gfxMatriximageSpaceToDeviceSpace;// The size at which the image should be drawn (which may not be its// intrinsic size due to, for example, HQ scaling).nsIntSizesize;// The region in tiled image space which will be drawn, with an associated// region to which sampling should be restricted.ImageRegionregion;// The default viewport size for SVG images, which we use unless a different// one has been explicitly specified. This is the same as |size| except that// it does not take into account any transformation on the gfxContext we're// drawing to - for example, CSS transforms are not taken into account.CSSIntSizesvgViewportSize;// Whether there's anything to draw at all.boolshouldDraw;SnappedImageDrawingParameters():region(ImageRegion::Empty()),shouldDraw(false){}SnappedImageDrawingParameters(constgfxMatrix&aImageSpaceToDeviceSpace,constnsIntSize&aSize,constImageRegion&aRegion,constCSSIntSize&aSVGViewportSize):imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace),size(aSize),region(aRegion),svgViewportSize(aSVGViewportSize),shouldDraw(true){}};/** * Given two axis-aligned rectangles, returns the transformation that maps the * first onto the second. * * @param aFrom The rect to be transformed. * @param aTo The rect that aFrom should be mapped onto by the transformation. */staticgfxMatrixTransformBetweenRects(constgfxRect&aFrom,constgfxRect&aTo){gfxSizescale(aTo.width/aFrom.width,aTo.height/aFrom.height);gfxPointtranslation(aTo.x-aFrom.x*scale.width,aTo.y-aFrom.y*scale.height);returngfxMatrix(scale.width,0,0,scale.height,translation.x,translation.y);}staticnsRectTileNearRect(constnsRect&aAnyTile,constnsRect&aTargetRect){nsPointdistance=aTargetRect.TopLeft()-aAnyTile.TopLeft();returnaAnyTile+nsPoint(distance.x/aAnyTile.width*aAnyTile.width,distance.y/aAnyTile.height*aAnyTile.height);}staticgfxFloatStableRound(gfxFloataValue){// Values slightly less than 0.5 should round up like 0.5 would; we're// assuming they were meant to be 0.5.returnfloor(aValue+0.5001);}staticgfxPointStableRound(constgfxPoint&aPoint){returngfxPoint(StableRound(aPoint.x),StableRound(aPoint.y));}/** * Given a set of input parameters, compute certain output parameters * for drawing an image with the image snapping algorithm. * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering * * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters */staticSnappedImageDrawingParametersComputeSnappedImageDrawingParameters(gfxContext*aCtx,int32_taAppUnitsPerDevPixel,constnsRectaDest,constnsRectaFill,constnsPointaAnchor,constnsRectaDirty,imgIContainer*aImage,constSamplingFilteraSamplingFilter,uint32_taImageFlags,ExtendModeaExtendMode){if(aDest.IsEmpty()||aFill.IsEmpty())returnSnappedImageDrawingParameters();// Avoid unnecessarily large offsets.booldoTile=!aDest.Contains(aFill);nsRectappUnitDest=doTile?TileNearRect(aDest,aFill.Intersect(aDirty)):aDest;nsPointanchor=aAnchor+(appUnitDest.TopLeft()-aDest.TopLeft());gfxRectdevPixelDest=nsLayoutUtils::RectToGfxRect(appUnitDest,aAppUnitsPerDevPixel);gfxRectdevPixelFill=nsLayoutUtils::RectToGfxRect(aFill,aAppUnitsPerDevPixel);gfxRectdevPixelDirty=nsLayoutUtils::RectToGfxRect(aDirty,aAppUnitsPerDevPixel);gfxMatrixcurrentMatrix=aCtx->CurrentMatrixDouble();gfxRectfill=devPixelFill;gfxRectdest=devPixelDest;booldidSnap;// Snap even if we have a scale in the context. But don't snap if// we have something that's not translation+scale, or if the scale flips in// the X or Y direction, because snapped image drawing can't handle that yet.if(!currentMatrix.HasNonAxisAlignedTransform()&¤tMatrix._11>0.0&¤tMatrix._22>0.0&&aCtx->UserToDevicePixelSnapped(fill,gfxContext::SnapOption::IgnoreScale)&&aCtx->UserToDevicePixelSnapped(dest,gfxContext::SnapOption::IgnoreScale)){// We snapped. On this code path, |fill| and |dest| take into account// currentMatrix's transform.didSnap=true;}else{// We didn't snap. On this code path, |fill| and |dest| do not take into// account currentMatrix's transform.didSnap=false;fill=devPixelFill;dest=devPixelDest;}// If we snapped above, |dest| already takes into account |currentMatrix|'s// scale and has integer coordinates. If not, we need these properties to// compute the optimal drawn image size, so compute |snappedDestSize| here.gfxSizesnappedDestSize=dest.Size();gfxSizescaleFactors=currentMatrix.ScaleFactors();if(!didSnap){snappedDestSize.Scale(scaleFactors.width,scaleFactors.height);snappedDestSize.width=NS_round(snappedDestSize.width);snappedDestSize.height=NS_round(snappedDestSize.height);}// We need to be sure that this is at least one pixel in width and height,// or we'll end up drawing nothing even if we have a nonempty fill.snappedDestSize.width=std::max(snappedDestSize.width,1.0);snappedDestSize.height=std::max(snappedDestSize.height,1.0);// Bail if we're not going to end up drawing anything.if(fill.IsEmpty()){returnSnappedImageDrawingParameters();}nsIntSizeintImageSize=aImage->OptimalImageSizeForDest(snappedDestSize,imgIContainer::FRAME_CURRENT,aSamplingFilter,aImageFlags);nsIntSizesvgViewportSize;if(scaleFactors.width==1.0&&scaleFactors.height==1.0){// intImageSize is scaled by currentMatrix. But since there are no scale// factors in currentMatrix, it is safe to assign intImageSize to// svgViewportSize directly.svgViewportSize=intImageSize;}else{// We should not take into account any transformation of currentMatrix// when computing svg viewport size. Since currentMatrix contains scale// factors, we need to recompute SVG viewport by unscaled devPixelDest.svgViewportSize=aImage->OptimalImageSizeForDest(devPixelDest.Size(),imgIContainer::FRAME_CURRENT,aSamplingFilter,aImageFlags);}gfxSizeimageSize(intImageSize.width,intImageSize.height);// Compute the set of pixels that would be sampled by an ideal renderinggfxPointsubimageTopLeft=MapToFloatImagePixels(imageSize,devPixelDest,devPixelFill.TopLeft());gfxPointsubimageBottomRight=MapToFloatImagePixels(imageSize,devPixelDest,devPixelFill.BottomRight());gfxRectsubimage;subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),NSToIntFloor(subimageTopLeft.y));subimage.SizeTo(NSToIntCeil(subimageBottomRight.x)-subimage.x,NSToIntCeil(subimageBottomRight.y)-subimage.y);if(subimage.IsEmpty()){// Bail if the subimage is empty (we're not going to be drawing anything).returnSnappedImageDrawingParameters();}gfxMatrixtransform;gfxMatrixinvTransform;boolanchorAtUpperLeft=anchor.x==appUnitDest.x&&anchor.y==appUnitDest.y;boolexactlyOneImageCopy=aFill.IsEqualEdges(appUnitDest);if(anchorAtUpperLeft&&exactlyOneImageCopy){// The simple case: we can ignore the anchor point and compute the// transformation from the sampled region (the subimage) to the fill rect.// This approach is preferable when it works since it tends to produce// less numerical error.transform=TransformBetweenRects(subimage,fill);invTransform=TransformBetweenRects(fill,subimage);}else{// The more complicated case: we compute the transformation from the// image rect positioned at the image space anchor point to the dest rect// positioned at the device space anchor point.// Compute the anchor point in both device space and image space. This// code assumes that pixel-based devices have one pixel per device unit!gfxPointanchorPoint(gfxFloat(anchor.x)/aAppUnitsPerDevPixel,gfxFloat(anchor.y)/aAppUnitsPerDevPixel);gfxPointimageSpaceAnchorPoint=MapToFloatImagePixels(imageSize,devPixelDest,anchorPoint);if(didSnap){imageSpaceAnchorPoint=StableRound(imageSpaceAnchorPoint);anchorPoint=imageSpaceAnchorPoint;anchorPoint=MapToFloatUserPixels(imageSize,devPixelDest,anchorPoint);anchorPoint=currentMatrix.TransformPoint(anchorPoint);anchorPoint=StableRound(anchorPoint);}// Compute an unsnapped version of the dest rect's size. We continue to// follow the pattern that we take |currentMatrix| into account only if// |didSnap| is true.gfxSizeunsnappedDestSize=didSnap?devPixelDest.Size()*currentMatrix.ScaleFactors():devPixelDest.Size();gfxRectanchoredDestRect(anchorPoint,unsnappedDestSize);gfxRectanchoredImageRect(imageSpaceAnchorPoint,imageSize);// Calculate anchoredDestRect with snapped fill rect when the devPixelFill// rect corresponds to just a single tile in that directionif(fill.Width()!=devPixelFill.Width()&&devPixelDest.x==devPixelFill.x&&devPixelDest.XMost()==devPixelFill.XMost()){anchoredDestRect.width=fill.width;}if(fill.Height()!=devPixelFill.Height()&&devPixelDest.y==devPixelFill.y&&devPixelDest.YMost()==devPixelFill.YMost()){anchoredDestRect.height=fill.height;}transform=TransformBetweenRects(anchoredImageRect,anchoredDestRect);invTransform=TransformBetweenRects(anchoredDestRect,anchoredImageRect);}// If the transform is not a straight translation by integers, then// filtering will occur, and restricting the fill rect to the dirty rect// would change the values computed for edge pixels, which we can't allow.// Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not// produce pixel-aligned coordinates, which would also break the values// computed for edge pixels.if(didSnap&&!invTransform.HasNonIntegerTranslation()){// This form of Transform is safe to call since non-axis-aligned// transforms wouldn't be snapped.devPixelDirty=currentMatrix.TransformRect(devPixelDirty);devPixelDirty.RoundOut();fill=fill.Intersect(devPixelDirty);}if(fill.IsEmpty())returnSnappedImageDrawingParameters();gfxRectimageSpaceFill(didSnap?invTransform.TransformRect(fill):invTransform.TransformBounds(fill));// If we didn't snap, we need to post-multiply the matrix on the context to// get the final matrix we'll draw with, because we didn't take it into// account when computing the matrices above.if(!didSnap){transform=transform*currentMatrix;}ExtendModeextendMode=(aImageFlags&imgIContainer::FLAG_CLAMP)?ExtendMode::CLAMP:aExtendMode;// We were passed in the default extend mode but need to tile.if(extendMode==ExtendMode::CLAMP&&doTile){MOZ_ASSERT(!(aImageFlags&imgIContainer::FLAG_CLAMP));extendMode=ExtendMode::REPEAT;}ImageRegionregion=ImageRegion::CreateWithSamplingRestriction(imageSpaceFill,subimage,extendMode);returnSnappedImageDrawingParameters(transform,intImageSize,region,CSSIntSize(svgViewportSize.width,svgViewportSize.height));}staticImgDrawResultDrawImageInternal(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty,constMaybe<SVGImageContext>&aSVGContext,uint32_taImageFlags,ExtendModeaExtendMode=ExtendMode::CLAMP,floataOpacity=1.0){ImgDrawResultresult=ImgDrawResult::SUCCESS;aImageFlags|=imgIContainer::FLAG_ASYNC_NOTIFY;if(aPresContext->Type()==nsPresContext::eContext_Print){// We want vector images to be passed on as vector commands, not a raster// image.aImageFlags|=imgIContainer::FLAG_BYPASS_SURFACE_CACHE;}if(aDest.Contains(aFill)){aImageFlags|=imgIContainer::FLAG_CLAMP;}int32_tappUnitsPerDevPixel=aPresContext->AppUnitsPerDevPixel();SnappedImageDrawingParametersparams=ComputeSnappedImageDrawingParameters(&aContext,appUnitsPerDevPixel,aDest,aFill,aAnchor,aDirty,aImage,aSamplingFilter,aImageFlags,aExtendMode);if(!params.shouldDraw){returnresult;}{gfxContextMatrixAutoSaveRestorecontextMatrixRestorer(&aContext);aContext.SetMatrixDouble(params.imageSpaceToDeviceSpace);Maybe<SVGImageContext>fallbackContext;if(!aSVGContext){// Use the default viewport.fallbackContext.emplace(Some(params.svgViewportSize));}result=aImage->Draw(&aContext,params.size,params.region,imgIContainer::FRAME_CURRENT,aSamplingFilter,aSVGContext?aSVGContext:fallbackContext,aImageFlags,aOpacity);}returnresult;}/* static */ImgDrawResultnsLayoutUtils::DrawSingleUnscaledImage(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsPoint&aDest,constnsRect*aDirty,constMaybe<SVGImageContext>&aSVGContext,uint32_taImageFlags,constnsRect*aSourceArea){CSSIntSizeimageSize;aImage->GetWidth(&imageSize.width);aImage->GetHeight(&imageSize.height);aImage->GetResolution().ApplyTo(imageSize.width,imageSize.height);if(imageSize.width<1||imageSize.height<1){NS_WARNING("Image width or height is non-positive");returnImgDrawResult::TEMPORARY_ERROR;}nsSizesize(CSSPixel::ToAppUnits(imageSize));nsRectsource;if(aSourceArea){source=*aSourceArea;}else{source.SizeTo(size);}nsRectdest(aDest-source.TopLeft(),size);nsRectfill(aDest,source.Size());// Ensure that only a single image tile is drawn. If aSourceArea extends// outside the image bounds, we want to honor the aSourceArea-to-aDest// translation but we don't want to actually tile the image.fill.IntersectRect(fill,dest);returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,dest,fill,aDest,aDirty?*aDirty:dest,aSVGContext,aImageFlags);}/* static */ImgDrawResultnsLayoutUtils::DrawSingleImage(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,SamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aDirty,constMaybe<SVGImageContext>&aSVGContext,uint32_taImageFlags,constnsPoint*aAnchorPoint,constnsRect*aSourceArea){nscoordappUnitsPerCSSPixel=AppUnitsPerCSSPixel();// NOTE(emilio): We can hardcode resolution to 1 here, since we're interested// in the actual image pixels, for snapping purposes, not on the adjusted// size.CSSIntSizepixelImageSize(ComputeSizeForDrawingWithFallback(aImage,ImageResolution(),aDest.Size()));if(pixelImageSize.width<1||pixelImageSize.height<1){NS_ASSERTION(pixelImageSize.width>=0&&pixelImageSize.height>=0,"Image width or height is negative");returnImgDrawResult::SUCCESS;// no point in drawing a zero size image}nsSizeimageSize(CSSPixel::ToAppUnits(pixelImageSize));nsRectsource;nsCOMPtr<imgIContainer>image;if(aSourceArea){source=*aSourceArea;nsIntRectsubRect(source.x,source.y,source.width,source.height);subRect.ScaleInverseRoundOut(appUnitsPerCSSPixel);image=ImageOps::Clip(aImage,subRect);nsRectimageRect;imageRect.SizeTo(imageSize);nsRectclippedSource=imageRect.Intersect(source);source-=clippedSource.TopLeft();imageSize=clippedSource.Size();}else{source.SizeTo(imageSize);image=aImage;}nsRectdest=GetWholeImageDestination(imageSize,source,aDest);// Ensure that only a single image tile is drawn. If aSourceArea extends// outside the image bounds, we want to honor the aSourceArea-to-aDest// transform but we don't want to actually tile the image.nsRectfill;fill.IntersectRect(aDest,dest);returnDrawImageInternal(aContext,aPresContext,image,aSamplingFilter,dest,fill,aAnchorPoint?*aAnchorPoint:fill.TopLeft(),aDirty,aSVGContext,aImageFlags);}/* static */voidnsLayoutUtils::ComputeSizeForDrawing(imgIContainer*aImage,constImageResolution&aResolution,/* outparam */CSSIntSize&aImageSize,/* outparam */AspectRatio&aIntrinsicRatio,/* outparam */bool&aGotWidth,/* outparam */bool&aGotHeight){aGotWidth=NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));aGotHeight=NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));Maybe<AspectRatio>intrinsicRatio=aImage->GetIntrinsicRatio();aIntrinsicRatio=intrinsicRatio.valueOr(AspectRatio());if(aGotWidth){aResolution.ApplyXTo(aImageSize.width);}if(aGotHeight){aResolution.ApplyYTo(aImageSize.height);}if(!(aGotWidth&&aGotHeight)&&intrinsicRatio.isNothing()){// We hit an error (say, because the image failed to load or couldn't be// decoded) and should return zero size.aGotWidth=aGotHeight=true;aImageSize=CSSIntSize(0,0);}}/* static */CSSIntSizensLayoutUtils::ComputeSizeForDrawingWithFallback(imgIContainer*aImage,constImageResolution&aResolution,constnsSize&aFallbackSize){CSSIntSizeimageSize;AspectRatioimageRatio;boolgotHeight,gotWidth;ComputeSizeForDrawing(aImage,aResolution,imageSize,imageRatio,gotWidth,gotHeight);// If we didn't get both width and height, try to compute them using the// intrinsic ratio of the image.if(gotWidth!=gotHeight){if(!gotWidth){if(imageRatio){imageSize.width=imageRatio.ApplyTo(imageSize.height);gotWidth=true;}}else{if(imageRatio){imageSize.height=imageRatio.Inverted().ApplyTo(imageSize.width);gotHeight=true;}}}// If we still don't have a width or height, just use the fallback size the// caller provided.if(!gotWidth){imageSize.width=nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.width);}if(!gotHeight){imageSize.height=nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.height);}returnimageSize;}/* static */LayerIntRectSnapRectForImage(constgfx::Matrix&aTransform,constgfx::Size&aScaleFactors,constLayoutDeviceRect&aRect){// Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.// Any changes to the algorithm here will need to be reflected there.boolsnapped=false;LayerIntRectsnapRect;if(!aTransform.HasNonAxisAlignedTransform()&&aTransform._11>0.0&&aTransform._22>0.0){gfxRectrect(gfxPoint(aRect.X(),aRect.Y()),gfxSize(aRect.Width(),aRect.Height()));gfxPointp1=ThebesPoint(aTransform.TransformPoint(ToPoint(rect.TopLeft())));gfxPointp2=ThebesPoint(aTransform.TransformPoint(ToPoint(rect.TopRight())));gfxPointp3=ThebesPoint(aTransform.TransformPoint(ToPoint(rect.BottomRight())));if(p2==gfxPoint(p1.x,p3.y)||p2==gfxPoint(p3.x,p1.y)){p1.Round();p3.Round();IntPointp1i(int32_t(p1.x),int32_t(p1.y));IntPointp3i(int32_t(p3.x),int32_t(p3.y));snapRect.MoveTo(std::min(p1i.x,p3i.x),std::min(p1i.y,p3i.y));snapRect.SizeTo(std::max(p1i.x,p3i.x)-snapRect.X(),std::max(p1i.y,p3i.y)-snapRect.Y());snapped=true;}}if(!snapped){// If we couldn't snap directly with the transform, we need to go best// effort in layer pixels.snapRect=RoundedToInt(LayerRect(aRect.X()*aScaleFactors.width,aRect.Y()*aScaleFactors.height,aRect.Width()*aScaleFactors.width,aRect.Height()*aScaleFactors.height));}// An empty size is unacceptable so we ensure our suggested size is at least// 1 pixel wide/tall.if(snapRect.Width()<1){snapRect.SetWidth(1);}if(snapRect.Height()<1){snapRect.SetHeight(1);}returnsnapRect;}/* static */IntSizensLayoutUtils::ComputeImageContainerDrawingParameters(imgIContainer*aImage,nsIFrame*aForFrame,constLayoutDeviceRect&aDestRect,constLayoutDeviceRect&aFillRect,constStackingContextHelper&aSc,uint32_taFlags,Maybe<SVGImageContext>&aSVGContext,Maybe<ImageIntRegion>&aRegion){MOZ_ASSERT(aImage);MOZ_ASSERT(aForFrame);gfx::SizescaleFactors=aSc.GetInheritedScale();SamplingFiltersamplingFilter=nsLayoutUtils::GetSamplingFilterForFrame(aForFrame);// Compute our SVG context parameters, if any. Don't replace the viewport// size if it was already set, prefer what the caller gave.SVGImageContext::MaybeStoreContextPaint(aSVGContext,aForFrame,aImage);if((scaleFactors.width!=1.0||scaleFactors.height!=1.0)&&aImage->GetType()==imgIContainer::TYPE_VECTOR&&(!aSVGContext||!aSVGContext->GetViewportSize())){gfxSizegfxDestSize(aDestRect.Width(),aDestRect.Height());IntSizeviewportSize=aImage->OptimalImageSizeForDest(gfxDestSize,imgIContainer::FRAME_CURRENT,samplingFilter,aFlags);CSSIntSizecssViewportSize(viewportSize.width,viewportSize.height);if(!aSVGContext){aSVGContext.emplace(Some(cssViewportSize));}else{aSVGContext->SetViewportSize(Some(cssViewportSize));}}constgfx::Matrix&itm=aSc.GetInheritedTransform();LayerIntRectdestRect=SnapRectForImage(itm,scaleFactors,aDestRect);// Since we always decode entire raster images, we only care about the// ImageIntRegion for vector images, for which we may only draw part of in// some cases.if(aImage->GetType()!=imgIContainer::TYPE_VECTOR){returnaImage->OptimalImageSizeForDest(gfxSize(destRect.Width(),destRect.Height()),imgIContainer::FRAME_CURRENT,samplingFilter,aFlags);}// If the dest rect contains the fill rect, then we are only displaying part// of the vector image. We need to calculate the restriction region to avoid// drawing more than we need, and sampling outside the desired bounds.LayerIntRectclipRect=SnapRectForImage(itm,scaleFactors,aFillRect);if(destRect.Contains(clipRect)){LayerIntRectrestrictRect=destRect.Intersect(clipRect);restrictRect.MoveBy(-destRect.TopLeft());if(restrictRect.Width()<1){restrictRect.SetWidth(1);}if(restrictRect.Height()<1){restrictRect.SetHeight(1);}if(restrictRect.X()!=0||restrictRect.Y()!=0||restrictRect.Size()!=destRect.Size()){IntRectsampleRect=restrictRect.ToUnknownRect();aRegion=Some(ImageIntRegion::CreateWithSamplingRestriction(sampleRect,sampleRect,ExtendMode::CLAMP));}}// VectorImage::OptimalImageSizeForDest will just round up, but we already// have an integer size.returndestRect.Size().ToUnknownSize();}/* static */nsPointnsLayoutUtils::GetBackgroundFirstTilePos(constnsPoint&aDest,constnsPoint&aFill,constnsSize&aRepeatSize){returnnsPoint(NSToIntFloor(float(aFill.x-aDest.x)/aRepeatSize.width)*aRepeatSize.width,NSToIntFloor(float(aFill.y-aDest.y)/aRepeatSize.height)*aRepeatSize.height)+aDest;}/* static */ImgDrawResultnsLayoutUtils::DrawBackgroundImage(gfxContext&aContext,nsIFrame*aForFrame,nsPresContext*aPresContext,imgIContainer*aImage,SamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsSize&aRepeatSize,constnsPoint&aAnchor,constnsRect&aDirty,uint32_taImageFlags,ExtendModeaExtendMode,floataOpacity){AUTO_PROFILER_LABEL("nsLayoutUtils::DrawBackgroundImage",GRAPHICS_Rasterization);CSSIntSizedestCSSSize{nsPresContext::AppUnitsToIntCSSPixels(aDest.width),nsPresContext::AppUnitsToIntCSSPixels(aDest.height)};Maybe<SVGImageContext>svgContext(Some(SVGImageContext(Some(destCSSSize))));SVGImageContext::MaybeStoreContextPaint(svgContext,aForFrame,aImage);/* Fast path when there is no need for image spacing */if(aRepeatSize.width==aDest.width&&aRepeatSize.height==aDest.height){returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,aDest,aFill,aAnchor,aDirty,svgContext,aImageFlags,aExtendMode,aOpacity);}constnsPointfirstTilePos=GetBackgroundFirstTilePos(aDest.TopLeft(),aFill.TopLeft(),aRepeatSize);constnscoordxMost=aFill.XMost();constnscoordrepeatWidth=aRepeatSize.width;constnscoordyMost=aFill.YMost();constnscoordrepeatHeight=aRepeatSize.height;nsRectdest(0,0,aDest.width,aDest.height);nsPointanchor=aAnchor;for(nscoordx=firstTilePos.x;x<xMost;x+=repeatWidth){for(nscoordy=firstTilePos.y;y<yMost;y+=repeatHeight){dest.x=x;dest.y=y;ImgDrawResultresult=DrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,dest,dest,anchor,aDirty,svgContext,aImageFlags,ExtendMode::CLAMP,aOpacity);anchor.y+=repeatHeight;if(result!=ImgDrawResult::SUCCESS){returnresult;}}anchor.x+=repeatWidth;anchor.y=aAnchor.y;}returnImgDrawResult::SUCCESS;}/* static */ImgDrawResultnsLayoutUtils::DrawImage(gfxContext&aContext,ComputedStyle*aComputedStyle,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty,uint32_taImageFlags,floataOpacity){Maybe<SVGImageContext>svgContext;SVGImageContext::MaybeStoreContextPaint(svgContext,aComputedStyle,aImage);returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,aDest,aFill,aAnchor,aDirty,svgContext,aImageFlags,ExtendMode::CLAMP,aOpacity);}/* static */nsRectnsLayoutUtils::GetWholeImageDestination(constnsSize&aWholeImageSize,constnsRect&aImageSourceArea,constnsRect&aDestArea){doublescaleX=double(aDestArea.width)/aImageSourceArea.width;doublescaleY=double(aDestArea.height)/aImageSourceArea.height;nscoorddestOffsetX=NSToCoordRound(aImageSourceArea.x*scaleX);nscoorddestOffsetY=NSToCoordRound(aImageSourceArea.y*scaleY);nscoordwholeSizeX=NSToCoordRound(aWholeImageSize.width*scaleX);nscoordwholeSizeY=NSToCoordRound(aWholeImageSize.height*scaleY);returnnsRect(aDestArea.TopLeft()-nsPoint(destOffsetX,destOffsetY),nsSize(wholeSizeX,wholeSizeY));}/* static */already_AddRefed<imgIContainer>nsLayoutUtils::OrientImage(imgIContainer*aContainer,constStyleImageOrientation&aOrientation){MOZ_ASSERT(aContainer,"Should have an image container");nsCOMPtr<imgIContainer>img(aContainer);switch(aOrientation){caseStyleImageOrientation::FromImage:break;caseStyleImageOrientation::None:img=ImageOps::Unorient(img);break;}returnimg.forget();}staticboolNonZeroCorner(constLengthPercentage&aLength){// Since negative results are clamped to 0, check > 0.returnaLength.Resolve(nscoord_MAX)>0||aLength.Resolve(0)>0;}/* static */boolnsLayoutUtils::HasNonZeroCorner(constBorderRadius&aCorners){for(constautocorner:mozilla::AllPhysicalHalfCorners()){if(NonZeroCorner(aCorners.Get(corner)))returntrue;}returnfalse;}// aCorner is a "full corner" value, i.e. eCornerTopLeft etc.staticboolIsCornerAdjacentToSide(uint8_taCorner,SideaSide){static_assert((int)eSideTop==eCornerTopLeft,"Check for Full Corner");static_assert((int)eSideRight==eCornerTopRight,"Check for Full Corner");static_assert((int)eSideBottom==eCornerBottomRight,"Check for Full Corner");static_assert((int)eSideLeft==eCornerBottomLeft,"Check for Full Corner");static_assert((int)eSideTop==((eCornerTopRight-1)&3),"Check for Full Corner");static_assert((int)eSideRight==((eCornerBottomRight-1)&3),"Check for Full Corner");static_assert((int)eSideBottom==((eCornerBottomLeft-1)&3),"Check for Full Corner");static_assert((int)eSideLeft==((eCornerTopLeft-1)&3),"Check for Full Corner");returnaSide==aCorner||aSide==((aCorner-1)&3);}/* static */boolnsLayoutUtils::HasNonZeroCornerOnSide(constBorderRadius&aCorners,SideaSide){static_assert(eCornerTopLeftX/2==eCornerTopLeft,"Check for Non Zero on side");static_assert(eCornerTopLeftY/2==eCornerTopLeft,"Check for Non Zero on side");static_assert(eCornerTopRightX/2==eCornerTopRight,"Check for Non Zero on side");static_assert(eCornerTopRightY/2==eCornerTopRight,"Check for Non Zero on side");static_assert(eCornerBottomRightX/2==eCornerBottomRight,"Check for Non Zero on side");static_assert(eCornerBottomRightY/2==eCornerBottomRight,"Check for Non Zero on side");static_assert(eCornerBottomLeftX/2==eCornerBottomLeft,"Check for Non Zero on side");static_assert(eCornerBottomLeftY/2==eCornerBottomLeft,"Check for Non Zero on side");for(constautocorner:mozilla::AllPhysicalHalfCorners()){// corner is a "half corner" value, so dividing by two gives us a// "full corner" value.if(NonZeroCorner(aCorners.Get(corner))&&IsCornerAdjacentToSide(corner/2,aSide))returntrue;}returnfalse;}/* static */LayoutDeviceIntSizensLayoutUtils::GetBorderRadiusForMenuDropShadow(constnsIFrame*aFrame){if(aFrame->StyleUIReset()->mWindowShadow==StyleWindowShadow::Cliprounded){constauto&corners=aFrame->StyleBorder()->mBorderRadius;// Get the width and height of the top-left corner.constLengthPercentage&cornerX=corners.Get(eCornerTopLeftX);constLengthPercentage&cornerY=corners.Get(eCornerTopLeftY);nscoordlengthX=(cornerX.IsLength()?cornerX.ToLength():0);nscoordlengthY=(cornerY.IsLength()?cornerY.ToLength():0);if(lengthX||lengthY){constnsPresContext*presContext=aFrame->PresContext();returnLayoutDeviceIntSize(presContext->AppUnitsToDevPixels(lengthX),presContext->AppUnitsToDevPixels(lengthY));}}returnLayoutDeviceIntSize();}/* static */nsTransparencyModensLayoutUtils::GetFrameTransparency(nsIFrame*aBackgroundFrame,nsIFrame*aCSSRootFrame){if(aCSSRootFrame->StyleEffects()->mOpacity<1.0f)returneTransparencyTransparent;if(HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius))returneTransparencyTransparent;StyleAppearanceappearance=aCSSRootFrame->StyleDisplay()->EffectiveAppearance();if(appearance==StyleAppearance::MozWinGlass)returneTransparencyGlass;if(appearance==StyleAppearance::MozWinBorderlessGlass)returneTransparencyBorderlessGlass;nsITheme::Transparencytransparency;if(aCSSRootFrame->IsThemed(&transparency))returntransparency==nsITheme::eTransparent?eTransparencyTransparent:eTransparencyOpaque;// We need an uninitialized window to be treated as opaque because// doing otherwise breaks window display effects on some platforms,// specifically Vista. (bug 450322)if(aBackgroundFrame->IsViewportFrame()&&!aBackgroundFrame->PrincipalChildList().FirstChild()){returneTransparencyOpaque;}ComputedStyle*bgSC;if(!nsCSSRendering::FindBackground(aBackgroundFrame,&bgSC)){returneTransparencyTransparent;}constnsStyleBackground*bg=bgSC->StyleBackground();if(NS_GET_A(bg->BackgroundColor(bgSC))<255||// bottom layer's clip is used for the colorbg->BottomLayer().mClip!=StyleGeometryBox::BorderBox)returneTransparencyTransparent;returneTransparencyOpaque;}staticboolIsPopupFrame(constnsIFrame*aFrame){// aFrame is a popup it's the list control frame dropdown for a combobox.LayoutFrameTypeframeType=aFrame->Type();if(frameType==LayoutFrameType::ListControl){constnsListControlFrame*lcf=static_cast<constnsListControlFrame*>(aFrame);returnlcf->IsInDropDownMode();}// ... or if it's a XUL menupopup frame.returnframeType==LayoutFrameType::MenuPopup;}/* static */boolnsLayoutUtils::IsPopup(constnsIFrame*aFrame){// Optimization: the frame can't possibly be a popup if it has no view.if(!aFrame->HasView()){NS_ASSERTION(!IsPopupFrame(aFrame),"popup frame must have a view");returnfalse;}returnIsPopupFrame(aFrame);}/* static */nsIFrame*nsLayoutUtils::GetDisplayRootFrame(nsIFrame*aFrame){returnconst_cast<nsIFrame*>(nsLayoutUtils::GetDisplayRootFrame(const_cast<constnsIFrame*>(aFrame)));}/* static */constnsIFrame*nsLayoutUtils::GetDisplayRootFrame(constnsIFrame*aFrame){// We could use GetRootPresContext() here if the// NS_FRAME_IN_POPUP frame bit is set.constnsIFrame*f=aFrame;for(;;){if(!f->HasAnyStateBits(NS_FRAME_IN_POPUP)){f=f->PresShell()->GetRootFrame();if(!f){returnaFrame;}}elseif(IsPopup(f)){returnf;}nsIFrame*parent=GetCrossDocParentFrameInProcess(f);if(!parent)returnf;f=parent;}}/* static */nsIFrame*nsLayoutUtils::GetReferenceFrame(nsIFrame*aFrame){nsIFrame*f=aFrame;for(;;){if(f->IsTransformed()||f->IsPreserve3DLeaf()||IsPopup(f)){returnf;}nsIFrame*parent=GetCrossDocParentFrameInProcess(f);if(!parent){returnf;}f=parent;}}/* static */gfx::ShapedTextFlagsnsLayoutUtils::GetTextRunFlagsForStyle(ComputedStyle*aComputedStyle,nsPresContext*aPresContext,constnsStyleFont*aStyleFont,constnsStyleText*aStyleText,nscoordaLetterSpacing){gfx::ShapedTextFlagsresult=gfx::ShapedTextFlags();if(aLetterSpacing!=0||aStyleText->mTextJustify==StyleTextJustify::InterCharacter){result|=gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;}if(aStyleText->mMozControlCharacterVisibility==StyleMozControlCharacterVisibility::Hidden){result|=gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS;}switch(aComputedStyle->StyleText()->mTextRendering){caseStyleTextRendering::Optimizespeed:result|=gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;break;caseStyleTextRendering::Auto:if(aStyleFont->mFont.size.ToCSSPixels()<aPresContext->GetAutoQualityMinFontSize()){result|=gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;}break;default:break;}returnresult|GetTextRunOrientFlagsForStyle(aComputedStyle);}/* static */gfx::ShapedTextFlagsnsLayoutUtils::GetTextRunOrientFlagsForStyle(ComputedStyle*aComputedStyle){autowritingMode=aComputedStyle->StyleVisibility()->mWritingMode;switch(writingMode){caseStyleWritingModeProperty::HorizontalTb:returngfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL;caseStyleWritingModeProperty::VerticalLr:caseStyleWritingModeProperty::VerticalRl:switch(aComputedStyle->StyleVisibility()->mTextOrientation){caseStyleTextOrientation::Mixed:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;caseStyleTextOrientation::Upright:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;caseStyleTextOrientation::Sideways:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;default:MOZ_ASSERT_UNREACHABLE("unknown text-orientation");returngfx::ShapedTextFlags();}caseStyleWritingModeProperty::SidewaysLr:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;caseStyleWritingModeProperty::SidewaysRl:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;default:MOZ_ASSERT_UNREACHABLE("unknown writing-mode");returngfx::ShapedTextFlags();}}/* static */voidnsLayoutUtils::GetRectDifferenceStrips(constnsRect&aR1,constnsRect&aR2,nsRect*aHStrip,nsRect*aVStrip){NS_ASSERTION(aR1.TopLeft()==aR2.TopLeft(),"expected rects at the same position");nsRectunionRect(aR1.x,aR1.y,std::max(aR1.width,aR2.width),std::max(aR1.height,aR2.height));nscoordVStripStart=std::min(aR1.width,aR2.width);nscoordHStripStart=std::min(aR1.height,aR2.height);*aVStrip=unionRect;aVStrip->x+=VStripStart;aVStrip->width-=VStripStart;*aHStrip=unionRect;aHStrip->y+=HStripStart;aHStrip->height-=HStripStart;}nsDeviceContext*nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindowOuter*aWindow){if(!aWindow){returnnullptr;}nsCOMPtr<nsIDocShell>docShell=aWindow->GetDocShell();while(docShell){// Now make sure our size is up to date. That will mean that the device// context does the right thing on multi-monitor systems when we return it// to the caller. It will also make sure that our prescontext has been// created, if we're supposed to have one.nsCOMPtr<nsPIDOMWindowOuter>win=docShell->GetWindow();if(!win){// No reason to go onreturnnullptr;}win->EnsureSizeAndPositionUpToDate();RefPtr<nsPresContext>presContext=docShell->GetPresContext();if(presContext){nsDeviceContext*context=presContext->DeviceContext();if(context){returncontext;}}nsCOMPtr<nsIDocShellTreeItem>parentItem;docShell->GetInProcessParent(getter_AddRefs(parentItem));docShell=do_QueryInterface(parentItem);}returnnullptr;}/* static */boolnsLayoutUtils::IsReallyFixedPos(constnsIFrame*aFrame){MOZ_ASSERT(aFrame->StyleDisplay()->mPosition==StylePositionProperty::Fixed,"IsReallyFixedPos called on non-'position:fixed' frame");returnMayBeReallyFixedPos(aFrame);}/* static */boolnsLayoutUtils::MayBeReallyFixedPos(constnsIFrame*aFrame){MOZ_ASSERT(aFrame->GetParent(),"MayBeReallyFixedPos called on frame not in tree");LayoutFrameTypeparentType=aFrame->GetParent()->Type();returnparentType==LayoutFrameType::Viewport||parentType==LayoutFrameType::PageContent;}/* static */boolnsLayoutUtils::IsInPositionFixedSubtree(constnsIFrame*aFrame){for(constnsIFrame*f=aFrame;f;f=f->GetParent()){if(f->StyleDisplay()->mPosition==StylePositionProperty::Fixed&&nsLayoutUtils::IsReallyFixedPos(f)){returntrue;}}returnfalse;}SurfaceFromElementResultnsLayoutUtils::SurfaceFromOffscreenCanvas(OffscreenCanvas*aOffscreenCanvas,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;IntSizesize=aOffscreenCanvas->GetWidthHeight();result.mSourceSurface=aOffscreenCanvas->GetSurfaceSnapshot(&result.mAlphaType);if(!result.mSourceSurface){// If the element doesn't have a context then we won't get a snapshot. The// canvas spec wants us to not error and just draw nothing, so return an// empty surface.result.mAlphaType=gfxAlphaType::Opaque;RefPtr<DrawTarget>ref=aTarget?aTarget:gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();if(ref->CanCreateSimilarDrawTarget(size,SurfaceFormat::B8G8R8A8)){RefPtr<DrawTarget>dt=ref->CreateSimilarDrawTarget(size,SurfaceFormat::B8G8R8A8);if(dt){result.mSourceSurface=dt->Snapshot();}}}elseif(aTarget){RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}result.mHasSize=true;result.mSize=size;result.mIntrinsicSize=size;result.mIsWriteOnly=aOffscreenCanvas->IsWriteOnly();returnresult;}staticRefPtr<SourceSurface>ScaleSourceSurface(SourceSurface&aSurface,constIntSize&aTargetSize){constIntSizesurfaceSize=aSurface.GetSize();MOZ_ASSERT(surfaceSize!=aTargetSize);MOZ_ASSERT(!surfaceSize.IsEmpty());MOZ_ASSERT(!aTargetSize.IsEmpty());RefPtr<DrawTarget>dt=Factory::CreateDrawTarget(gfxVars::ContentBackend(),aTargetSize,aSurface.GetFormat());if(!dt||!dt->IsValid()){returnnullptr;}RefPtr<gfxContext>context=gfxContext::CreateOrNull(dt);MOZ_ASSERT(context);dt->DrawSurface(&aSurface,Rect(Point(),Size(aTargetSize)),Rect(Point(),Size(surfaceSize)));returndt->GetBackingSurface();}SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;nsresultrv;nsCOMPtr<imgIRequest>imgRequest;rv=aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,getter_AddRefs(imgRequest));if(NS_FAILED(rv)){returnresult;}if(!imgRequest){// There's no image request. This is either because a request for// a non-empty URI failed, or the URI is the empty string.nsCOMPtr<nsIURI>currentURI;aElement->GetCurrentURI(getter_AddRefs(currentURI));if(!currentURI){// Treat the empty URI as available instead of broken state.result.mHasSize=true;}returnresult;}uint32_tstatus;imgRequest->GetImageStatus(&status);result.mHasSize=status&imgIRequest::STATUS_SIZE_AVAILABLE;if((status&imgIRequest::STATUS_LOAD_COMPLETE)==0){// Spec says to use GetComplete, but that only works on// HTMLImageElement, and we support all sorts of other stuff// here. Do this for now pending spec clarification.result.mIsStillLoading=(status&imgIRequest::STATUS_ERROR)==0;returnresult;}nsCOMPtr<nsIPrincipal>principal;rv=imgRequest->GetImagePrincipal(getter_AddRefs(principal));if(NS_FAILED(rv)){returnresult;}nsCOMPtr<imgIContainer>imgContainer;rv=imgRequest->GetImage(getter_AddRefs(imgContainer));if(NS_FAILED(rv)){returnresult;}nsCOMPtr<nsIContent>content=do_QueryInterface(aElement);// Ensure that the image is oriented the same way as it's displayed.autoorientation=StyleImageOrientation::FromImage;if(nsIFrame*f=content->GetPrimaryFrame()){orientation=f->StyleVisibility()->mImageOrientation;}imgContainer=OrientImage(imgContainer,orientation);constboolnoRasterize=aSurfaceFlags&SFE_NO_RASTERIZING_VECTORS;uint32_twhichFrame=aSurfaceFlags&SFE_WANT_FIRST_FRAME_IF_IMAGE?(uint32_t)imgIContainer::FRAME_FIRST:(uint32_t)imgIContainer::FRAME_CURRENT;constboolexactSize=aSurfaceFlags&SFE_EXACT_SIZE_SURFACE;uint32_tframeFlags=imgIContainer::FLAG_SYNC_DECODE|imgIContainer::FLAG_ASYNC_NOTIFY;if(aSurfaceFlags&SFE_NO_COLORSPACE_CONVERSION)frameFlags|=imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;if(aSurfaceFlags&SFE_ALLOW_NON_PREMULT){frameFlags|=imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;}int32_timgWidth,imgHeight;HTMLImageElement*element=HTMLImageElement::FromNodeOrNull(content);if(aSurfaceFlags&SFE_USE_ELEMENT_SIZE_IF_VECTOR&&element&&imgContainer->GetType()==imgIContainer::TYPE_VECTOR){// We're holding a strong ref to "element" via "content".imgWidth=MOZ_KnownLive(element)->Width();imgHeight=MOZ_KnownLive(element)->Height();}else{rv=imgContainer->GetWidth(&imgWidth);nsresultrv2=imgContainer->GetHeight(&imgHeight);if(NS_FAILED(rv)||NS_FAILED(rv2))returnresult;imgContainer->GetResolution().ApplyTo(imgWidth,imgHeight);}result.mSize=result.mIntrinsicSize=IntSize(imgWidth,imgHeight);if(!noRasterize||imgContainer->GetType()==imgIContainer::TYPE_RASTER){result.mSourceSurface=imgContainer->GetFrameAtSize(result.mSize,whichFrame,frameFlags);if(!result.mSourceSurface){returnresult;}if(exactSize&&result.mSourceSurface->GetSize()!=result.mSize){result.mSourceSurface=ScaleSourceSurface(*result.mSourceSurface,result.mSize);if(!result.mSourceSurface){returnresult;}}// The surface we return is likely to be cached. We don't want to have to// convert to a surface that's compatible with aTarget each time it's used// (that would result in terrible performance), so we convert once here// upfront if aTarget is specified.if(aTarget){RefPtr<SourceSurface>optSurface=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(optSurface){result.mSourceSurface=optSurface;}}constauto&format=result.mSourceSurface->GetFormat();if(IsOpaque(format)){result.mAlphaType=gfxAlphaType::Opaque;}elseif(frameFlags&imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA){result.mAlphaType=gfxAlphaType::NonPremult;}else{result.mAlphaType=gfxAlphaType::Premult;}}else{result.mDrawInfo.mImgContainer=imgContainer;result.mDrawInfo.mWhichFrame=whichFrame;result.mDrawInfo.mDrawingFlags=frameFlags;}int32_tcorsmode;if(NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))){result.mCORSUsed=corsmode!=CORS_NONE;}boolhadCrossOriginRedirects=true;imgRequest->GetHadCrossOriginRedirects(&hadCrossOriginRedirects);result.mPrincipal=std::move(principal);result.mHadCrossOriginRedirects=hadCrossOriginRedirects;result.mImageRequest=std::move(imgRequest);result.mIsWriteOnly=CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed,result.mPrincipal,result.mHadCrossOriginRedirects);returnresult;}SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLImageElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){returnSurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),aSurfaceFlags,aTarget);}SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLCanvasElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;IntSizesize=aElement->GetSize();autopAlphaType=&result.mAlphaType;if(!(aSurfaceFlags&SFE_ALLOW_NON_PREMULT)){pAlphaType=nullptr;// Coersce GetSurfaceSnapshot to give us Opaque/Premult only.}result.mSourceSurface=aElement->GetSurfaceSnapshot(pAlphaType);if(!result.mSourceSurface){// If the element doesn't have a context then we won't get a snapshot. The// canvas spec wants us to not error and just draw nothing, so return an// empty surface.result.mAlphaType=gfxAlphaType::Opaque;RefPtr<DrawTarget>ref=aTarget?aTarget:gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();if(ref->CanCreateSimilarDrawTarget(size,SurfaceFormat::B8G8R8A8)){RefPtr<DrawTarget>dt=ref->CreateSimilarDrawTarget(size,SurfaceFormat::B8G8R8A8);if(dt){result.mSourceSurface=dt->Snapshot();}}}elseif(aTarget){RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}// Ensure that any future changes to the canvas trigger proper invalidation,// in case this is being used by -moz-element()aElement->MarkContextClean();result.mHasSize=true;result.mSize=size;result.mIntrinsicSize=size;result.mPrincipal=aElement->NodePrincipal();result.mHadCrossOriginRedirects=false;result.mIsWriteOnly=aElement->IsWriteOnly();returnresult;}SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLVideoElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;result.mAlphaType=gfxAlphaType::Opaque;// Assume opaque.if(aElement->ContainsRestrictedContent()){returnresult;}uint16_treadyState=aElement->ReadyState();if(readyState==HAVE_NOTHING||readyState==HAVE_METADATA){result.mIsStillLoading=true;returnresult;}// If it doesn't have a principal, just bailnsCOMPtr<nsIPrincipal>principal=aElement->GetCurrentVideoPrincipal();if(!principal){returnresult;}result.mLayersImage=aElement->GetCurrentImage();if(!result.mLayersImage){returnresult;}result.mCORSUsed=aElement->GetCORSMode()!=CORS_NONE;result.mHasSize=true;result.mSize=result.mLayersImage->GetSize();result.mIntrinsicSize=gfx::IntSize(aElement->VideoWidth(),aElement->VideoHeight());result.mPrincipal=std::move(principal);result.mHadCrossOriginRedirects=aElement->HadCrossOriginRedirects();result.mIsWriteOnly=CanvasUtils::CheckWriteOnlySecurity(result.mCORSUsed,result.mPrincipal,result.mHadCrossOriginRedirects);if(aTarget){// They gave us a DrawTarget to optimize for, so even though we have a// layers::Image, we should unconditionally try to grab a SourceSurface and// try to optimize it.if((result.mSourceSurface=result.mLayersImage->GetAsSourceSurface())){RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}}returnresult;}SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(dom::Element*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){// If it's a <canvas>, we may be able to just grab its internal surfaceif(HTMLCanvasElement*canvas=HTMLCanvasElement::FromNodeOrNull(aElement)){returnSurfaceFromElement(canvas,aSurfaceFlags,aTarget);}// Maybe it's <video>?if(HTMLVideoElement*video=HTMLVideoElement::FromNodeOrNull(aElement)){returnSurfaceFromElement(video,aSurfaceFlags,aTarget);}// Finally, check if it's a normal imagensCOMPtr<nsIImageLoadingContent>imageLoader=do_QueryInterface(aElement);if(!imageLoader){returnSurfaceFromElementResult();}returnSurfaceFromElement(imageLoader,aSurfaceFlags,aTarget);}/* static */Element*nsLayoutUtils::GetEditableRootContentByContentEditable(Document*aDocument){// If the document is in designMode we should return nullptr.if(!aDocument||aDocument->HasFlag(NODE_IS_EDITABLE)){returnnullptr;}// contenteditable only works with HTML document.// XXXbz should this test IsHTMLOrXHTML(), or just IsHTML()?if(!aDocument->IsHTMLOrXHTML()){returnnullptr;}Element*rootElement=aDocument->GetRootElement();if(rootElement&&rootElement->IsEditable()){returnrootElement;}// If there is no editable root element, check its <body> element.// Note that the body element could be <frameset> element.Element*bodyElement=aDocument->GetBody();if(bodyElement&&bodyElement->IsEditable()){returnbodyElement;}returnnullptr;}#ifdef DEBUG/* static */voidnsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame*aContainer,constnsFrameList&aFrameList){for(nsIFrame*f:aFrameList){// Check only later continuations of f; we deal with checking the// earlier continuations when we hit those earlier continuations in// the frame list.for(nsIFrame*c=f;(c=c->GetNextInFlow());){NS_ASSERTION(c->GetParent()!=aContainer||!aFrameList.ContainsFrame(c),"Two continuations of the same frame in the same ""frame list");}}}// Is one of aFrame's ancestors a letter frame?staticboolIsInLetterFrame(nsIFrame*aFrame){for(nsIFrame*f=aFrame->GetParent();f;f=f->GetParent()){if(f->IsLetterFrame()){returntrue;}}returnfalse;}/* static */voidnsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame*aSubtreeRoot){NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),"frame tree not empty, but caller reported complete status");// Also assert that text frames map no text.auto[start,end]=aSubtreeRoot->GetOffsets();// In some cases involving :first-letter, we'll partially unlink a// continuation in the middle of a continuation chain from its// previous and next continuations before destroying it, presumably so// that we don't also destroy the later continuations. Once we've// done this, GetOffsets returns incorrect values.// For examples, see list of tests in// https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29NS_ASSERTION(start==end||IsInLetterFrame(aSubtreeRoot),"frame tree not empty, but caller reported complete status");for(constauto&childList:aSubtreeRoot->ChildLists()){for(nsIFrame*child:childList.mList){nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(child);}}}#endifstaticvoidGetFontFacesForFramesInner(nsIFrame*aFrame,nsLayoutUtils::UsedFontFaceList&aResult,nsLayoutUtils::UsedFontFaceTable&aFontFaces,uint32_taMaxRanges,boolaSkipCollapsedWhitespace){MOZ_ASSERT(aFrame,"NULL frame pointer");if(aFrame->IsTextFrame()){if(!aFrame->GetPrevContinuation()){nsLayoutUtils::GetFontFacesForText(aFrame,0,INT32_MAX,true,aResult,aFontFaces,aMaxRanges,aSkipCollapsedWhitespace);}return;}nsIFrame::ChildListIDchildLists[]={nsIFrame::kPrincipalList,nsIFrame::kPopupList};for(size_ti=0;i<ArrayLength(childLists);++i){nsFrameListchildren(aFrame->GetChildList(childLists[i]));for(nsFrameList::Enumeratore(children);!e.AtEnd();e.Next()){nsIFrame*child=e.get();child=nsPlaceholderFrame::GetRealFrameFor(child);GetFontFacesForFramesInner(child,aResult,aFontFaces,aMaxRanges,aSkipCollapsedWhitespace);}}}/* static */nsresultnsLayoutUtils::GetFontFacesForFrames(nsIFrame*aFrame,UsedFontFaceList&aResult,UsedFontFaceTable&aFontFaces,uint32_taMaxRanges,boolaSkipCollapsedWhitespace){MOZ_ASSERT(aFrame,"NULL frame pointer");while(aFrame){GetFontFacesForFramesInner(aFrame,aResult,aFontFaces,aMaxRanges,aSkipCollapsedWhitespace);aFrame=GetNextContinuationOrIBSplitSibling(aFrame);}returnNS_OK;}staticvoidAddFontsFromTextRun(gfxTextRun*aTextRun,nsTextFrame*aFrame,gfxSkipCharsIterator&aSkipIter,constgfxTextRun::Range&aRange,nsLayoutUtils::UsedFontFaceList&aResult,nsLayoutUtils::UsedFontFaceTable&aFontFaces,uint32_taMaxRanges){gfxTextRun::GlyphRunIteratorglyphRuns(aTextRun,aRange);nsIContent*content=aFrame->GetContent();int32_tcontentLimit=aFrame->GetContentOffset()+aFrame->GetInFlowContentLength();while(glyphRuns.NextRun()){gfxFontEntry*fe=glyphRuns.GetGlyphRun()->mFont->GetFontEntry();// if we have already listed this face, just make sure the match type is// recordedInspectorFontFace*fontFace=aFontFaces.Get(fe);if(fontFace){fontFace->AddMatchType(glyphRuns.GetGlyphRun()->mMatchType);}else{// A new font entry we haven't seen beforefontFace=newInspectorFontFace(fe,aTextRun->GetFontGroup(),glyphRuns.GetGlyphRun()->mMatchType);aFontFaces.InsertOrUpdate(fe,fontFace);aResult.AppendElement(fontFace);}// Add this glyph run to the fontFace's list of ranges, unless we have// already collected as many as wanted.if(fontFace->RangeCount()<aMaxRanges){int32_tstart=aSkipIter.ConvertSkippedToOriginal(glyphRuns.GetStringStart());int32_tend=aSkipIter.ConvertSkippedToOriginal(glyphRuns.GetStringEnd());// Mapping back from textrun offsets ("skipped" offsets that reflect the// text after whitespace collapsing, etc) to DOM content offsets in the// original text is ambiguous, because many original characters can// map to a single skipped offset. aSkipIter.ConvertSkippedToOriginal()// will return an "original" offset that corresponds to the *end* of// a collapsed run of characters in this case; but that might extend// beyond the current content node if the textrun mapped multiple nodes.// So we clamp the end offset to keep it valid for the content node// that corresponds to the current textframe.end=std::min(end,contentLimit);if(end>start){RefPtr<nsRange>range=nsRange::Create(content,start,content,end,IgnoreErrors());NS_WARNING_ASSERTION(range,"nsRange::Create() failed to create valid range");if(range){fontFace->AddRange(range);}}}}}/* static */voidnsLayoutUtils::GetFontFacesForText(nsIFrame*aFrame,int32_taStartOffset,int32_taEndOffset,boolaFollowContinuations,UsedFontFaceList&aResult,UsedFontFaceTable&aFontFaces,uint32_taMaxRanges,boolaSkipCollapsedWhitespace){MOZ_ASSERT(aFrame,"NULL frame pointer");if(!aFrame->IsTextFrame()){return;}if(!aFrame->StyleVisibility()->IsVisible()){return;}nsTextFrame*curr=static_cast<nsTextFrame*>(aFrame);do{int32_tfstart=std::max(curr->GetContentOffset(),aStartOffset);int32_tfend=std::min(curr->GetContentEnd(),aEndOffset);if(fstart>=fend){curr=static_cast<nsTextFrame*>(curr->GetNextContinuation());continue;}// curr is overlapping with the offset we wantgfxSkipCharsIteratoriter=curr->EnsureTextRun(nsTextFrame::eInflated);gfxTextRun*textRun=curr->GetTextRun(nsTextFrame::eInflated);if(!textRun){NS_WARNING("failed to get textRun, low memory?");return;}// include continuations in the range that share the same textrunnsTextFrame*next=nullptr;if(aFollowContinuations&&fend<aEndOffset){next=static_cast<nsTextFrame*>(curr->GetNextContinuation());while(next&&next->GetTextRun(nsTextFrame::eInflated)==textRun){fend=std::min(next->GetContentEnd(),aEndOffset);next=fend<aEndOffset?static_cast<nsTextFrame*>(next->GetNextContinuation()):nullptr;}}if(!aSkipCollapsedWhitespace||(curr->HasAnyNoncollapsedCharacters()&&curr->HasNonSuppressedText())){gfxTextRun::Rangerange(iter.ConvertOriginalToSkipped(fstart),iter.ConvertOriginalToSkipped(fend));AddFontsFromTextRun(textRun,curr,iter,range,aResult,aFontFaces,aMaxRanges);}curr=next;}while(aFollowContinuations&&curr);}/* static */size_tnsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame*aFrame,MallocSizeOfaMallocSizeOf,boolclear){MOZ_ASSERT(aFrame,"NULL frame pointer");size_ttotal=0;if(aFrame->IsTextFrame()){nsTextFrame*textFrame=static_cast<nsTextFrame*>(aFrame);for(uint32_ti=0;i<2;++i){gfxTextRun*run=textFrame->GetTextRun((i!=0)?nsTextFrame::eInflated:nsTextFrame::eNotInflated);if(run){if(clear){run->ResetSizeOfAccountingFlags();}else{total+=run->MaybeSizeOfIncludingThis(aMallocSizeOf);}}}returntotal;}for(constauto&childList:aFrame->ChildLists()){for(nsIFrame*f:childList.mList){total+=SizeOfTextRunsForFrames(f,aMallocSizeOf,clear);}}returntotal;}/* static */voidnsLayoutUtils::Initialize(){nsComputedDOMStyle::RegisterPrefChangeCallbacks();}/* static */voidnsLayoutUtils::Shutdown(){if(sContentMap){deletesContentMap;sContentMap=nullptr;}nsComputedDOMStyle::UnregisterPrefChangeCallbacks();}/* static */voidnsLayoutUtils::RegisterImageRequest(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}if(aRequestRegistered&&*aRequestRegistered){// Our request is already registered with the refresh driver, so// no need to register it again.return;}if(aRequest){if(!aPresContext->RefreshDriver()->AddImageRequest(aRequest)){NS_WARNING("Unable to add image request");return;}if(aRequestRegistered){*aRequestRegistered=true;}}}/* static */voidnsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}if(aRequestRegistered&&*aRequestRegistered){// Our request is already registered with the refresh driver, so// no need to register it again.return;}if(aRequest){nsCOMPtr<imgIContainer>image;if(NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))){// Check to verify that the image is animated. If so, then add it to the// list of images tracked by the refresh driver.boolisAnimated=false;nsresultrv=image->GetAnimated(&isAnimated);if(NS_SUCCEEDED(rv)&&isAnimated){if(!aPresContext->RefreshDriver()->AddImageRequest(aRequest)){NS_WARNING("Unable to add image request");return;}if(aRequestRegistered){*aRequestRegistered=true;}}}}}/* static */voidnsLayoutUtils::DeregisterImageRequest(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}// Deregister our imgIRequest with the refresh driver to// complete tear-down, but only if it has been registeredif(aRequestRegistered&&!*aRequestRegistered){return;}if(aRequest){nsCOMPtr<imgIContainer>image;if(NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))){aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);if(aRequestRegistered){*aRequestRegistered=false;}}}}/* static */voidnsLayoutUtils::PostRestyleEvent(Element*aElement,RestyleHintaRestyleHint,nsChangeHintaMinChangeHint){if(Document*doc=aElement->GetComposedDoc()){if(nsPresContext*presContext=doc->GetPresContext()){presContext->RestyleManager()->PostRestyleEvent(aElement,aRestyleHint,aMinChangeHint);}}}nsSetAttrRunnable::nsSetAttrRunnable(Element*aElement,nsAtom*aAttrName,constnsAString&aValue):mozilla::Runnable("nsSetAttrRunnable"),mElement(aElement),mAttrName(aAttrName),mValue(aValue){NS_ASSERTION(aElement&&aAttrName,"Missing stuff, prepare to crash");}nsSetAttrRunnable::nsSetAttrRunnable(Element*aElement,nsAtom*aAttrName,int32_taValue):mozilla::Runnable("nsSetAttrRunnable"),mElement(aElement),mAttrName(aAttrName){NS_ASSERTION(aElement&&aAttrName,"Missing stuff, prepare to crash");mValue.AppendInt(aValue);}NS_IMETHODIMPnsSetAttrRunnable::Run(){returnmElement->SetAttr(kNameSpaceID_None,mAttrName,mValue,true);}nsUnsetAttrRunnable::nsUnsetAttrRunnable(Element*aElement,nsAtom*aAttrName):mozilla::Runnable("nsUnsetAttrRunnable"),mElement(aElement),mAttrName(aAttrName){NS_ASSERTION(aElement&&aAttrName,"Missing stuff, prepare to crash");}NS_IMETHODIMPnsUnsetAttrRunnable::Run(){returnmElement->UnsetAttr(kNameSpaceID_None,mAttrName,true);}/** * Compute the minimum font size inside of a container with the given * width, such that **when the user zooms the container to fill the full * width of the device**, the fonts satisfy our minima. */staticnscoordMinimumFontSizeFor(nsPresContext*aPresContext,WritingModeaWritingMode,nscoordaContainerISize){PresShell*presShell=aPresContext->PresShell();uint32_temPerLine=presShell->FontSizeInflationEmPerLine();uint32_tminTwips=presShell->FontSizeInflationMinTwips();if(emPerLine==0&&minTwips==0){return0;}nscoordbyLine=0,byInch=0;if(emPerLine!=0){byLine=aContainerISize/emPerLine;}if(minTwips!=0){// REVIEW: Is this giving us app units and sizes *not* counting// viewport scaling?gfxSizescreenSize=aPresContext->ScreenSizeInchesForFontInflation();floatdeviceISizeInches=aWritingMode.IsVertical()?screenSize.height:screenSize.width;byInch=NSToCoordRound(aContainerISize/(deviceISizeInches*1440/minTwips));}returnstd::max(byLine,byInch);}/* static */floatnsLayoutUtils::FontSizeInflationInner(constnsIFrame*aFrame,nscoordaMinFontSize){// Note that line heights should be inflated by the same ratio as the// font size of the same text; thus we operate only on the font size// even when we're scaling a line height.nscoordstyleFontSize=aFrame->StyleFont()->mFont.size.ToAppUnits();if(styleFontSize<=0){// Never scale zero font size.return1.0;}if(aMinFontSize<=0){// No need to scale.return1.0;}// If between this current frame and its font inflation container there is a// non-inline element with fixed width or height, then we should not inflate// fonts for this frame.for(constnsIFrame*f=aFrame;f&&!f->IsContainerForFontSizeInflation();f=f->GetParent()){nsIContent*content=f->GetContent();LayoutFrameTypefType=f->Type();nsIFrame*parent=f->GetParent();// Also, if there is more than one frame corresponding to a single// content node, we want the outermost one.if(!(parent&&parent->GetContent()==content)&&// ignore width/height on inlines since they don't applyfType!=LayoutFrameType::Inline&&// ignore width on radios and checkboxes since we enlarge them and// they have width/height in ua.cssfType!=LayoutFrameType::CheckboxRadio){// ruby annotations should have the same inflation as its// grandparent, which is the ruby frame contains the annotation.if(fType==LayoutFrameType::RubyText){MOZ_ASSERT(parent&&parent->IsRubyTextContainerFrame());nsIFrame*grandparent=parent->GetParent();MOZ_ASSERT(grandparent&&grandparent->IsRubyFrame());returnFontSizeInflationFor(grandparent);}WritingModewm=f->GetWritingMode();constauto&stylePosISize=f->StylePosition()->ISize(wm);constauto&stylePosBSize=f->StylePosition()->BSize(wm);if(!stylePosISize.IsAuto()||!stylePosBSize.BehavesLikeInitialValueOnBlockAxis()){return1.0;}}}int32_tinterceptParam=StaticPrefs::font_size_inflation_mappingIntercept();floatmaxRatio=(float)StaticPrefs::font_size_inflation_maxRatio()/100.0f;floatratio=float(styleFontSize)/float(aMinFontSize);floatinflationRatio;// Given a minimum inflated font size m, a specified font size s, we want to// find the inflated font size i and then return the ratio of i to s (i/s).if(interceptParam>=0){// Since the mapping intercept parameter P is greater than zero, we use it// to determine the point where our mapping function intersects the i=s// line. This means that we have an equation of the form://// i = m + s*(P/2)/(1 + P/2), if s <= (1 + P/2)*m// i = s, if s >= (1 + P/2)*mfloatintercept=1+float(interceptParam)/2.0f;if(ratio>=intercept){// If we're already at 1+P/2 or more times the minimum, don't scale.return1.0;}// The point (intercept, intercept) is where the part of the i vs. s graph// that's not slope 1 meets the i=s line. (This part of the// graph is a line from (0, m), to that point). We calculate the// intersection point to be ((1+P/2)m, (1+P/2)m), where P is the// intercept parameter above. We then need to return i/s.inflationRatio=(1.0f+(ratio*(intercept-1)/intercept))/ratio;}else{// This is the case where P is negative. We essentially want to implement// the case for P=infinity here, so we make i = s + m, which means that// i/s = s/s + m/s = 1 + 1/ratioinflationRatio=1+1.0f/ratio;}if(maxRatio>1.0&&inflationRatio>maxRatio){returnmaxRatio;}else{returninflationRatio;}}staticboolShouldInflateFontsForContainer(constnsIFrame*aFrame){// We only want to inflate fonts for text that is in a place// with room to expand. The question is what the best heuristic for// that is...// For now, we're going to use NS_FRAME_IN_CONSTRAINED_BSIZE, which// indicates whether the frame is inside something with a constrained// block-size (propagating down the tree), but the propagation stops when// we hit overflow-y [or -x, for vertical mode]: scroll or auto.constnsStyleText*styleText=aFrame->StyleText();returnstyleText->mTextSizeAdjust!=StyleTextSizeAdjust::None&&!aFrame->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE)&&// We also want to disable font inflation for containers that have// preformatted text.// MathML cells need special treatment. See bug 1002526 comment 56.(styleText->WhiteSpaceCanWrap(aFrame)||aFrame->IsFrameOfType(nsIFrame::eMathML));}nscoordnsLayoutUtils::InflationMinFontSizeFor(constnsIFrame*aFrame){nsPresContext*presContext=aFrame->PresContext();if(!FontSizeInflationEnabled(presContext)||presContext->mInflationDisabledForShrinkWrap){return0;}for(constnsIFrame*f=aFrame;f;f=f->GetParent()){if(f->IsContainerForFontSizeInflation()){if(!ShouldInflateFontsForContainer(f)){return0;}nsFontInflationData*data=nsFontInflationData::FindFontInflationDataFor(aFrame);// FIXME: The need to null-check here is sort of a bug, and might// lead to incorrect results.if(!data||!data->InflationEnabled()){return0;}returnMinimumFontSizeFor(aFrame->PresContext(),aFrame->GetWritingMode(),data->UsableISize());}}MOZ_ASSERT(false,"root should always be container");return0;}floatnsLayoutUtils::FontSizeInflationFor(constnsIFrame*aFrame){if(SVGUtils::IsInSVGTextSubtree(aFrame)){constnsIFrame*container=aFrame;while(!container->IsSVGTextFrame()){container=container->GetParent();}NS_ASSERTION(container,"expected to find an ancestor SVGTextFrame");returnstatic_cast<constSVGTextFrame*>(container)->GetFontSizeScaleFactor();}if(!FontSizeInflationEnabled(aFrame->PresContext())){return1.0f;}returnFontSizeInflationInner(aFrame,InflationMinFontSizeFor(aFrame));}/* static */boolnsLayoutUtils::FontSizeInflationEnabled(nsPresContext*aPresContext){PresShell*presShell=aPresContext->GetPresShell();if(!presShell){returnfalse;}returnpresShell->FontSizeInflationEnabled();}/* static */nsRectnsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame*aFrame,constnsSize&aFrameSize){autoboxShadows=aFrame->StyleEffects()->mBoxShadow.AsSpan();if(boxShadows.IsEmpty()){returnnsRect();}nsRectinputRect(nsPoint(0,0),aFrameSize);// According to the CSS spec, box-shadow should be based on the border box.// However, that looks broken when the background extends outside the border// box, as can be the case with native theming. To fix that we expand the// area that we shadow to include the bounds of any native theme drawing.constnsStyleDisplay*styleDisplay=aFrame->StyleDisplay();nsITheme::Transparencytransparency;if(aFrame->IsThemed(styleDisplay,&transparency)){// For opaque (rectangular) theme widgets we can take the generic// border-box path with border-radius disabled.if(transparency!=nsITheme::eOpaque){nsPresContext*presContext=aFrame->PresContext();presContext->Theme()->GetWidgetOverflow(presContext->DeviceContext(),aFrame,styleDisplay->EffectiveAppearance(),&inputRect);}}nsRectshadows;int32_tA2D=aFrame->PresContext()->AppUnitsPerDevPixel();for(auto&shadow:boxShadows){nsRecttmpRect=inputRect;// inset shadows are never painted outside the frameif(shadow.inset){continue;}tmpRect.MoveBy(nsPoint(shadow.base.horizontal.ToAppUnits(),shadow.base.vertical.ToAppUnits()));tmpRect.Inflate(shadow.spread.ToAppUnits());tmpRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(shadow.base.blur.ToAppUnits(),A2D));shadows.UnionRect(shadows,tmpRect);}returnshadows;}/* static */boolnsLayoutUtils::GetContentViewerSize(constnsPresContext*aPresContext,LayoutDeviceIntSize&aOutSize,SubtractDynamicToolbaraSubtractDynamicToolbar){nsCOMPtr<nsIDocShell>docShell=aPresContext->GetDocShell();if(!docShell){returnfalse;}nsCOMPtr<nsIContentViewer>cv;docShell->GetContentViewer(getter_AddRefs(cv));if(!cv){returnfalse;}nsIntRectbounds;cv->GetBounds(bounds);if(aPresContext->IsRootContentDocumentCrossProcess()&&aSubtractDynamicToolbar==SubtractDynamicToolbar::Yes&&aPresContext->HasDynamicToolbar()&&!bounds.IsEmpty()){MOZ_ASSERT(aPresContext->IsRootContentDocumentCrossProcess());bounds.height-=aPresContext->GetDynamicToolbarMaxHeight();// Collapse the size in the case the dynamic toolbar max height is greater// than the content bound height so that hopefully embedders of GeckoView// may notice they set wrong dynamic toolbar max height.if(bounds.height<0){bounds.height=0;}}aOutSize=LayoutDeviceIntRect::FromUnknownRect(bounds).Size();returntrue;}boolnsLayoutUtils::UpdateCompositionBoundsForRCDRSF(ParentLayerRect&aCompBounds,constnsPresContext*aPresContext){SubtractDynamicToolbarshouldSubtractDynamicToolbar=SubtractDynamicToolbar::Yes;if(RefPtr<MobileViewportManager>MVM=aPresContext->PresShell()->GetMobileViewportManager()){CSSSizeintrinsicCompositionSize=MVM->GetIntrinsicCompositionSize();if(nsIScrollableFrame*rootScrollableFrame=aPresContext->PresShell()->GetRootScrollFrameAsScrollable()){// Expand the composition size to include the area initially covered by// the dynamic toolbar only if the content is taller than the intrinsic// composition size (i.e. the dynamic toolbar should be able to move only// if the content is vertically scrollable).if(intrinsicCompositionSize.height<CSSPixel::FromAppUnits(CalculateScrollableRectForFrame(rootScrollableFrame,nullptr).Height())){shouldSubtractDynamicToolbar=SubtractDynamicToolbar::No;}}}LayoutDeviceIntSizecontentSize;if(!GetContentViewerSize(aPresContext,contentSize,shouldSubtractDynamicToolbar)){returnfalse;}aCompBounds.SizeTo(ViewAs<ParentLayerPixel>(LayoutDeviceSize(contentSize),PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF));returntrue;}/* static */nsMarginnsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(constnsIFrame*aScrollFrame){if(!aScrollFrame||!aScrollFrame->GetScrollTargetFrame()){returnnsMargin();}nsPresContext*presContext=aScrollFrame->PresContext();PresShell*presShell=presContext->GetPresShell();if(!presShell){returnnsMargin();}boolisRootScrollFrame=aScrollFrame==presShell->GetRootScrollFrame();boolisRootContentDocRootScrollFrame=isRootScrollFrame&&presContext->IsRootContentDocumentCrossProcess();if(!isRootContentDocRootScrollFrame){returnnsMargin();}if(LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars)){returnnsMargin();}nsIScrollableFrame*scrollableFrame=aScrollFrame->GetScrollTargetFrame();if(!scrollableFrame){returnnsMargin();}returnscrollableFrame->GetActualScrollbarSizes(nsIScrollableFrame::ScrollbarSizesOptions::INCLUDE_VISUAL_VIEWPORT_SCROLLBARS);}/* static */nsSizensLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame*aFrame,boolaSubtractScrollbars,constnsSize*aOverrideScrollPortSize){// If we have a scrollable frame, restrict the composition bounds to its// scroll port. The scroll port excludes the frame borders and the scroll// bars, which we don't want to be part of the composition bounds.nsIScrollableFrame*scrollableFrame=aFrame->GetScrollTargetFrame();nsRectrect=scrollableFrame?scrollableFrame->GetScrollPortRect():aFrame->GetRect();nsSizesize=aOverrideScrollPortSize?*aOverrideScrollPortSize:rect.Size();nsPresContext*presContext=aFrame->PresContext();PresShell*presShell=presContext->PresShell();boolisRootContentDocRootScrollFrame=presContext->IsRootContentDocumentCrossProcess()&&aFrame==presShell->GetRootScrollFrame();if(isRootContentDocRootScrollFrame){ParentLayerRectcompBounds;if(UpdateCompositionBoundsForRCDRSF(compBounds,presContext)){int32_tauPerDevPixel=presContext->AppUnitsPerDevPixel();size=nsSize(compBounds.width*auPerDevPixel,compBounds.height*auPerDevPixel);}}if(aSubtractScrollbars){nsMarginmargins=ScrollbarAreaToExcludeFromCompositionBoundsFor(aFrame);size.width-=margins.LeftRight();size.height-=margins.TopBottom();}returnsize;}/* static */CSSSizensLayoutUtils::CalculateBoundingCompositionSize(constnsIFrame*aFrame,boolaIsRootContentDocRootScrollFrame,constFrameMetrics&aMetrics){if(aIsRootContentDocRootScrollFrame){returnViewAs<LayerPixel>(aMetrics.GetCompositionBounds().Size(),PixelCastJustification::ParentLayerToLayerForRootComposition)*LayerToScreenScale(1.0f)/aMetrics.DisplayportPixelsPerCSSPixel();}nsPresContext*presContext=aFrame->PresContext();ScreenSizerootCompositionSize;nsPresContext*rootPresContext=presContext->GetInProcessRootContentDocumentPresContext();if(!rootPresContext){rootPresContext=presContext->GetRootPresContext();}PresShell*rootPresShell=nullptr;if(rootPresContext){rootPresShell=rootPresContext->PresShell();if(nsIFrame*rootFrame=rootPresShell->GetRootFrame()){LayoutDeviceToLayerScale2DcumulativeResolution(rootPresShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(rootFrame));ParentLayerRectcompBounds;if(UpdateCompositionBoundsForRCDRSF(compBounds,rootPresContext)){rootCompositionSize=ViewAs<ScreenPixel>(compBounds.Size(),PixelCastJustification::ScreenIsParentLayerForRoot);}else{int32_trootAUPerDevPixel=rootPresContext->AppUnitsPerDevPixel();LayerSizeframeSize=(LayoutDeviceRect::FromAppUnits(rootFrame->GetRect(),rootAUPerDevPixel)*cumulativeResolution).Size();rootCompositionSize=frameSize*LayerToScreenScale(1.0f);}}}else{nsIWidget*widget=aFrame->GetNearestWidget();LayoutDeviceIntRectwidgetBounds=widget->GetBounds();rootCompositionSize=ScreenSize(ViewAs<ScreenPixel>(widgetBounds.Size(),PixelCastJustification::LayoutDeviceIsScreenForBounds));}// Adjust composition size for the size of scroll bars.nsIFrame*rootRootScrollFrame=rootPresShell?rootPresShell->GetRootScrollFrame():nullptr;nsMarginscrollbarMargins=ScrollbarAreaToExcludeFromCompositionBoundsFor(rootRootScrollFrame);LayoutDeviceMarginmargins=LayoutDeviceMargin::FromAppUnits(scrollbarMargins,rootPresContext->AppUnitsPerDevPixel());// Scrollbars are not subject to resolution scaling, so LD pixels = layer// pixels for them.rootCompositionSize.width-=margins.LeftRight();rootCompositionSize.height-=margins.TopBottom();CSSSizeresult=rootCompositionSize/aMetrics.DisplayportPixelsPerCSSPixel();// If this is a nested content process, the in-process root content document's// composition size may still be arbitrarily large, so bound it further by// how much of the in-process RCD is visible in the top-level (cross-process// RCD) viewport.if(rootPresShell){if(BrowserChild*bc=BrowserChild::GetFrom(rootPresShell)){if(constauto&visibleRect=bc->GetTopLevelViewportVisibleRectInSelfCoords()){CSSSizecssVisibleRect=visibleRect->Size()/rootPresContext->CSSToDevPixelScale();result=Min(result,cssVisibleRect);}}}returnresult;}/* static */nsRectnsLayoutUtils::CalculateScrollableRectForFrame(constnsIScrollableFrame*aScrollableFrame,constnsIFrame*aRootFrame){nsRectcontentBounds;if(aScrollableFrame){contentBounds=aScrollableFrame->GetScrollRange();nsPointscrollPosition=aScrollableFrame->GetScrollPosition();if(aScrollableFrame->GetScrollStyles().mVertical==StyleOverflow::Hidden){contentBounds.y=scrollPosition.y;contentBounds.height=0;}if(aScrollableFrame->GetScrollStyles().mHorizontal==StyleOverflow::Hidden){contentBounds.x=scrollPosition.x;contentBounds.width=0;}contentBounds.width+=aScrollableFrame->GetScrollPortRect().width;contentBounds.height+=aScrollableFrame->GetScrollPortRect().height;}else{contentBounds=aRootFrame->GetRect();// Clamp to (0, 0) if there is no corresponding scrollable frame for the// given |aRootFrame|.contentBounds.MoveTo(0,0);}returncontentBounds;}/* static */nsRectnsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame*aFrame){nsRectscrollableRect=CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(),aFrame->PresShell()->GetRootFrame());nsSizecompSize=CalculateCompositionSizeForFrame(aFrame);if(aFrame==aFrame->PresShell()->GetRootScrollFrame()){// the composition size for the root scroll frame does not include the// local resolution, so we adjust.floatres=aFrame->PresShell()->GetResolution();compSize.width=NSToCoordRound(compSize.width/res);compSize.height=NSToCoordRound(compSize.height/res);}if(scrollableRect.width<compSize.width){scrollableRect.x=std::max(0,scrollableRect.x-(compSize.width-scrollableRect.width));scrollableRect.width=compSize.width;}if(scrollableRect.height<compSize.height){scrollableRect.y=std::max(0,scrollableRect.y-(compSize.height-scrollableRect.height));scrollableRect.height=compSize.height;}returnscrollableRect;}/* static */voidnsLayoutUtils::DoLogTestDataForPaint(WebRenderLayerManager*aManager,ViewIDaScrollId,conststd::string&aKey,conststd::string&aValue){MOZ_ASSERT(nsLayoutUtils::IsAPZTestLoggingEnabled(),"don't call me");aManager->LogTestDataForCurrentPaint(aScrollId,aKey,aValue);}voidnsLayoutUtils::LogAdditionalTestData(nsDisplayListBuilder*aBuilder,conststd::string&aKey,conststd::string&aValue){WebRenderLayerManager*manager=aBuilder->GetWidgetLayerManager(nullptr);if(!manager){return;}manager->LogAdditionalTestData(aKey,aValue);}/* static */boolnsLayoutUtils::IsAPZTestLoggingEnabled(){returnStaticPrefs::apz_test_logging_enabled();}////////////////////////////////////////// SurfaceFromElementResultSurfaceFromElementResult::SurfaceFromElementResult()// Use safe default values here:mHadCrossOriginRedirects(false),mIsWriteOnly(true),mIsStillLoading(false),mHasSize(false),mCORSUsed(false),mAlphaType(gfxAlphaType::Opaque){}constRefPtr<mozilla::gfx::SourceSurface>&SurfaceFromElementResult::GetSourceSurface(){if(!mSourceSurface&&mLayersImage){mSourceSurface=mLayersImage->GetAsSourceSurface();}returnmSourceSurface;}////////////////////////////////////////boolnsLayoutUtils::IsNonWrapperBlock(nsIFrame*aFrame){MOZ_ASSERT(aFrame);returnaFrame->IsBlockFrameOrSubclass()&&!aFrame->IsBlockWrapper();}boolnsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext*aPresContext){returnaPresContext->IsRootPaginatedDocument()&&(aPresContext->Type()==nsPresContext::eContext_PrintPreview||aPresContext->Type()==nsPresContext::eContext_PageLayout);}AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame*aFrame){// FIXME: Now that inflation calculations are based on the flow// root's NCA's (nearest common ancestor of its inflatable// descendants) width, we could probably disable inflation in// fewer cases than we currently do.// MathML cells need special treatment. See bug 1002526 comment 56.if(aFrame->IsContainerForFontSizeInflation()&&!aFrame->IsFrameOfType(nsIFrame::eMathML)){mPresContext=aFrame->PresContext();mOldValue=mPresContext->mInflationDisabledForShrinkWrap;mPresContext->mInflationDisabledForShrinkWrap=true;}else{// indicate we have nothing to restoremPresContext=nullptr;mOldValue=false;}}AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation(){if(mPresContext){mPresContext->mInflationDisabledForShrinkWrap=mOldValue;}}namespacemozilla{RectNSRectToRect(constnsRect&aRect,doubleaAppUnitsPerPixel){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.returnRect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));}RectNSRectToSnappedRect(constnsRect&aRect,doubleaAppUnitsPerPixel,constgfx::DrawTarget&aSnapDT){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.Rectrect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));MaybeSnapToDevicePixels(rect,aSnapDT,true);returnrect;}// Similar to a snapped rect, except an axis is left unsnapped if the snapping// process results in a length of 0.RectNSRectToNonEmptySnappedRect(constnsRect&aRect,doubleaAppUnitsPerPixel,constgfx::DrawTarget&aSnapDT){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.Rectrect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));MaybeSnapToDevicePixels(rect,aSnapDT,true,false);returnrect;}voidStrokeLineWithSnapping(constnsPoint&aP1,constnsPoint&aP2,int32_taAppUnitsPerDevPixel,DrawTarget&aDrawTarget,constPattern&aPattern,constStrokeOptions&aStrokeOptions,constDrawOptions&aDrawOptions){Pointp1=NSPointToPoint(aP1,aAppUnitsPerDevPixel);Pointp2=NSPointToPoint(aP2,aAppUnitsPerDevPixel);SnapLineToDevicePixelsForStroking(p1,p2,aDrawTarget,aStrokeOptions.mLineWidth);aDrawTarget.StrokeLine(p1,p2,aPattern,aStrokeOptions,aDrawOptions);}}// namespace mozilla/* static */voidnsLayoutUtils::SetBSizeFromFontMetrics(constnsIFrame*aFrame,ReflowOutput&aMetrics,constLogicalMargin&aFramePadding,WritingModeaLineWM,WritingModeaFrameWM){RefPtr<nsFontMetrics>fm=nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);if(fm){// Compute final height of the frame.//// Do things the standard css2 way -- though it's hard to find it// in the css2 spec! It's actually found in the css1 spec section// 4.4 (you will have to read between the lines to really see// it).//// The height of our box is the sum of our font size plus the top// and bottom border and padding. The height of children do not// affect our height.aMetrics.SetBlockStartAscent(aLineWM.IsLineInverted()?fm->MaxDescent():fm->MaxAscent());aMetrics.BSize(aLineWM)=fm->MaxHeight();}else{NS_WARNING("Cannot get font metrics - defaulting sizes to 0");aMetrics.SetBlockStartAscent(aMetrics.BSize(aLineWM)=0);}aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent()+aFramePadding.BStart(aFrameWM));aMetrics.BSize(aLineWM)+=aFramePadding.BStartEnd(aFrameWM);}/* static */boolnsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(PresShell*aPresShell){if(Document*doc=aPresShell->GetDocument()){WidgetEventevent(true,eVoidEvent);nsTArray<EventTarget*>targets;nsresultrv=EventDispatcher::Dispatch(ToSupports(doc),nullptr,&event,nullptr,nullptr,nullptr,&targets);NS_ENSURE_SUCCESS(rv,false);for(size_ti=0;i<targets.Length();i++){if(targets[i]->IsApzAware()){returntrue;}}}returnfalse;}/* static */boolnsLayoutUtils::CanScrollOriginClobberApz(ScrollOriginaScrollOrigin){switch(aScrollOrigin){caseScrollOrigin::None:caseScrollOrigin::NotSpecified:caseScrollOrigin::Apz:caseScrollOrigin::Restore:returnfalse;default:returntrue;}}/* static */ScrollMetadatansLayoutUtils::ComputeScrollMetadata(constnsIFrame*aForFrame,constnsIFrame*aScrollFrame,nsIContent*aContent,constnsIFrame*aReferenceFrame,WebRenderLayerManager*aLayerManager,ViewIDaScrollParentId,constnsSize&aScrollPortSize,constMaybe<nsRect>&aClipRect,boolaIsRootContent){constnsPresContext*presContext=aForFrame->PresContext();int32_tauPerDevPixel=presContext->AppUnitsPerDevPixel();PresShell*presShell=presContext->GetPresShell();ScrollMetadatametadata;FrameMetrics&metrics=metadata.GetMetrics();metrics.SetLayoutViewport(CSSRect(CSSPoint(),CSSSize::FromAppUnits(aScrollPortSize)));nsIDocShell*docShell=presContext->GetDocShell();constBrowsingContext*bc=docShell?docShell->GetBrowsingContext():nullptr;boolisTouchEventsEnabled=bc&&bc->TouchEventsOverride()==mozilla::dom::TouchEventsOverride::Enabled;if(bc&&bc->InRDMPane()&&isTouchEventsEnabled){metadata.SetIsRDMTouchSimulationActive(true);}ViewIDscrollId=ScrollableLayerGuid::NULL_SCROLL_ID;if(aContent){if(void*paintRequestTime=aContent->GetProperty(nsGkAtoms::paintRequestTime)){metrics.SetPaintRequestTime(*static_cast<TimeStamp*>(paintRequestTime));aContent->RemoveProperty(nsGkAtoms::paintRequestTime);}scrollId=nsLayoutUtils::FindOrCreateIDFor(aContent);nsRectdp;if(DisplayPortUtils::GetDisplayPort(aContent,&dp)){metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));DisplayPortUtils::MarkDisplayPortAsPainted(aContent);}if(DisplayPortUtils::GetCriticalDisplayPort(aContent,&dp)){metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));}metrics.SetHasNonZeroDisplayPortMargins(false);if(DisplayPortMarginsPropertyData*currentData=static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins))){if(currentData->mMargins.mMargins!=ScreenMargin()){metrics.SetHasNonZeroDisplayPortMargins(true);}}// Log the high-resolution display port (which is either the displayport// or the critical displayport) for test purposes.if(IsAPZTestLoggingEnabled()){LogTestDataForPaint(aLayerManager,scrollId,"displayport",StaticPrefs::layers_low_precision_buffer()?metrics.GetCriticalDisplayPort():metrics.GetDisplayPort());}metrics.SetMinimalDisplayPort(aContent->GetProperty(nsGkAtoms::MinimalDisplayPort));}constnsIScrollableFrame*scrollableFrame=nullptr;if(aScrollFrame)scrollableFrame=aScrollFrame->GetScrollTargetFrame();metrics.SetScrollableRect(CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame,aForFrame)));if(scrollableFrame){CSSPointlayoutScrollOffset=CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());CSSPointvisualScrollOffset=aIsRootContent&&presShell->IsVisualViewportOffsetSet()?CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset()):layoutScrollOffset;metrics.SetVisualScrollOffset(visualScrollOffset);// APZ sometimes reads this even if we haven't set a visual scroll// update type (specifically, in the isFirstPaint case), so always// set it.metrics.SetVisualDestination(visualScrollOffset);if(aIsRootContent){if(aLayerManager->GetIsFirstPaint()&&presShell->IsVisualViewportOffsetSet()){// Restore the visual viewport offset to the copy stored on the// main thread.presShell->ScrollToVisual(presShell->GetVisualViewportOffset(),FrameMetrics::eRestore,ScrollMode::Instant);}}if(scrollableFrame->IsRootScrollFrameOfDocument()){if(constMaybe<PresShell::VisualScrollUpdate>&visualUpdate=presShell->GetPendingVisualScrollUpdate()){metrics.SetVisualDestination(CSSPoint::FromAppUnits(visualUpdate->mVisualScrollOffset));metrics.SetVisualScrollUpdateType(visualUpdate->mUpdateType);presShell->AcknowledgePendingVisualScrollUpdate();}}if(aIsRootContent){// Expand the layout viewport to the size including the area covered by// the dynamic toolbar in the case where the dynamic toolbar is being// used, otherwise when the dynamic toolbar transitions on the compositor,// the layout viewport will be smaller than the visual viewport on the// compositor, thus the layout viewport offset will be forced to be moved// in FrameMetrics::KeepLayoutViewportEnclosingVisualViewport.if(presContext->HasDynamicToolbar()){CSSRectviewport=metrics.GetLayoutViewport();viewport.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(presContext,viewport.Size()));metrics.SetLayoutViewport(viewport);// We need to set 'fixed margins' to adjust 'fixed margins' value on the// composiutor in the case where the dynamic toolbar is completely// hidden because the margin value on the compositor is offset from the// position where the dynamic toolbar is completely VISIBLE but now the// toolbar is completely HIDDEN we need to adjust the difference on the// compositor.if(presContext->GetDynamicToolbarState()==DynamicToolbarState::Collapsed){metrics.SetFixedLayerMargins(ScreenMargin(0,0,presContext->GetDynamicToolbarHeight()-presContext->GetDynamicToolbarMaxHeight(),0));}}}metrics.SetScrollGeneration(scrollableFrame->CurrentScrollGeneration());CSSRectviewport=metrics.GetLayoutViewport();viewport.MoveTo(layoutScrollOffset);metrics.SetLayoutViewport(viewport);nsSizelineScrollAmount=scrollableFrame->GetLineScrollAmount();LayoutDeviceIntSizelineScrollAmountInDevPixels=LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount,presContext->AppUnitsPerDevPixel());metadata.SetLineScrollAmount(lineScrollAmountInDevPixels);nsSizepageScrollAmount=scrollableFrame->GetPageScrollAmount();LayoutDeviceIntSizepageScrollAmountInDevPixels=LayoutDeviceIntSize::FromAppUnitsRounded(pageScrollAmount,presContext->AppUnitsPerDevPixel());metadata.SetPageScrollAmount(pageScrollAmountInDevPixels);if(aScrollFrame->GetParent()){metadata.SetDisregardedDirection(WheelHandlingUtils::GetDisregardedWheelScrollDirection(aScrollFrame->GetParent()));}metadata.SetSnapInfo(scrollableFrame->GetScrollSnapInfo());metadata.SetOverscrollBehavior(scrollableFrame->GetOverscrollBehaviorInfo());metadata.SetScrollUpdates(scrollableFrame->GetScrollUpdates());}// If we have the scrollparent being the same as the scroll id, the// compositor-side code could get into an infinite loop while building the// overscroll handoff chain.MOZ_ASSERT(aScrollParentId==ScrollableLayerGuid::NULL_SCROLL_ID||scrollId!=aScrollParentId);metrics.SetScrollId(scrollId);metrics.SetIsRootContent(aIsRootContent);metadata.SetScrollParentId(aScrollParentId);constnsIFrame*rootScrollFrame=presShell->GetRootScrollFrame();boolisRootScrollFrame=aScrollFrame==rootScrollFrame;Document*document=presShell->GetDocument();if(scrollId!=ScrollableLayerGuid::NULL_SCROLL_ID&&!presContext->GetParentPresContext()){if((aScrollFrame&&isRootScrollFrame)){metadata.SetIsLayersIdRoot(true);}else{MOZ_ASSERT(document,"A non-root-scroll frame must be in a document");if(aContent==document->GetDocumentElement()){metadata.SetIsLayersIdRoot(true);}}}// Get whether the root content is RTL(E.g. it's true either if// "writing-mode: vertical-rl", or if// "writing-mode: horizontal-tb; direction: rtl;" in CSS).// For the concept of this and the reason why we need to get this kind of// information, see the definition of |mIsAutoDirRootContentRTL| in struct// |ScrollMetadata|.constElement*bodyElement=document?document->GetBodyElement():nullptr;constnsIFrame*primaryFrame=bodyElement?bodyElement->GetPrimaryFrame():rootScrollFrame;if(!primaryFrame){primaryFrame=rootScrollFrame;}if(primaryFrame){WritingModewritingModeOfRootScrollFrame=primaryFrame->GetWritingMode();WritingMode::BlockDirblockDirOfRootScrollFrame=writingModeOfRootScrollFrame.GetBlockDir();WritingMode::InlineDirinlineDirOfRootScrollFrame=writingModeOfRootScrollFrame.GetInlineDir();if(blockDirOfRootScrollFrame==WritingMode::BlockDir::eBlockRL||(blockDirOfRootScrollFrame==WritingMode::BlockDir::eBlockTB&&inlineDirOfRootScrollFrame==WritingMode::InlineDir::eInlineRTL)){metadata.SetIsAutoDirRootContentRTL(true);}}// Only the root scrollable frame for a given presShell should pick up// the presShell's resolution. All the other frames are 1.0.if(isRootScrollFrame){metrics.SetPresShellResolution(presShell->GetResolution());}else{metrics.SetPresShellResolution(1.0f);}if(presShell->IsResolutionUpdated()){metadata.SetResolutionUpdated(true);}// The cumulative resolution is the resolution at which the scroll frame's// content is actually rendered. It includes the pres shell resolutions of// all the pres shells from here up to the root, as well as any css-driven// resolution. We don't need to compute it as it's already stored in the// container parameters... except if we're in WebRender in which case we// don't have a aContainerParameters. In that case we're also not rasterizing// in Gecko anyway, so the only resolution we care about here is the presShell// resolution which we need to propagate to WebRender.metrics.SetCumulativeResolution(LayoutDeviceToLayerScale2D(LayoutDeviceToLayerScale(presShell->GetCumulativeResolution())));LayoutDeviceToScreenScale2DresolutionToScreen(presShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(aScrollFrame?aScrollFrame:aForFrame));metrics.SetExtraResolution(metrics.GetCumulativeResolution()/resolutionToScreen);metrics.SetDevPixelsPerCSSPixel(presContext->CSSToDevPixelScale());// Initially, AsyncPanZoomController should render the content to the screen// at the painted resolution.constLayerToParentLayerScalelayerToParentLayerScale(1.0f);metrics.SetZoom(metrics.GetCumulativeResolution()*metrics.GetDevPixelsPerCSSPixel()*layerToParentLayerScale);// Calculate the composition bounds as the size of the scroll frame and// its origin relative to the reference frame.// If aScrollFrame is null, we are in a document without a root scroll frame,// so it's a xul document. In this case, use the size of the viewport frame.constnsIFrame*frameForCompositionBoundsCalculation=aScrollFrame?aScrollFrame:aForFrame;nsRectcompositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),frameForCompositionBoundsCalculation->GetSize());if(scrollableFrame){// If we have a scrollable frame, restrict the composition bounds to its// scroll port. The scroll port excludes the frame borders and the scroll// bars, which we don't want to be part of the composition bounds.nsRectscrollPort=scrollableFrame->GetScrollPortRect();compositionBounds=nsRect(compositionBounds.TopLeft()+scrollPort.TopLeft(),scrollPort.Size());}ParentLayerRectframeBounds=LayoutDeviceRect::FromAppUnits(compositionBounds,auPerDevPixel)*metrics.GetCumulativeResolution()*layerToParentLayerScale;if(aClipRect){ParentLayerRectrect=LayoutDeviceRect::FromAppUnits(*aClipRect,auPerDevPixel)*metrics.GetCumulativeResolution()*layerToParentLayerScale;metadata.SetScrollClip(Some(LayerClip(RoundedToInt(rect))));}// For the root scroll frame of the root content document (RCD-RSF), the above// calculation will yield the size of the viewport frame as the composition// bounds, which doesn't actually correspond to what is visible when// nsIDOMWindowUtils::setCSSViewport has been called to modify the visible// area of the prescontext that the viewport frame is reflowed into. In that// case if our document has a widget then the widget's bounds will correspond// to what is visible. If we don't have a widget the root view's bounds// correspond to what would be visible because they don't get modified by// setCSSViewport.boolisRootContentDocRootScrollFrame=isRootScrollFrame&&presContext->IsRootContentDocumentCrossProcess();if(isRootContentDocRootScrollFrame){UpdateCompositionBoundsForRCDRSF(frameBounds,presContext);if(RefPtr<MobileViewportManager>MVM=presContext->PresShell()->GetMobileViewportManager()){metrics.SetCompositionSizeWithoutDynamicToolbar(MVM->GetCompositionSizeWithoutDynamicToolbar());}}metrics.SetCompositionBoundsWidthIgnoringScrollbars(frameBounds.width);nsMarginsizes=ScrollbarAreaToExcludeFromCompositionBoundsFor(aScrollFrame);// Scrollbars are not subject to resolution scaling, so LD pixels = layer// pixels for them.ParentLayerMarginboundMargins=LayoutDeviceMargin::FromAppUnits(sizes,auPerDevPixel)*LayoutDeviceToParentLayerScale(1.0f);frameBounds.Deflate(boundMargins);metrics.SetCompositionBounds(frameBounds);metrics.SetBoundingCompositionSize(nsLayoutUtils::CalculateBoundingCompositionSize(aScrollFrame?aScrollFrame:aForFrame,isRootContentDocRootScrollFrame,metrics));if(StaticPrefs::apz_printtree()||StaticPrefs::apz_test_logging_enabled()){if(constnsIContent*content=frameForCompositionBoundsCalculation->GetContent()){nsAutoStringcontentDescription;if(content->IsElement()){content->AsElement()->Describe(contentDescription);}else{contentDescription.AssignLiteral("(not an element)");}metadata.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription));if(IsAPZTestLoggingEnabled()){LogTestDataForPaint(aLayerManager,scrollId,"contentDescription",metadata.GetContentDescription().get());}}}metrics.SetPresShellId(presShell->GetPresShellId());// If the scroll frame's content is marked 'scrollgrab', record this// in the FrameMetrics so APZ knows to provide the scroll grabbing// behaviour.if(aScrollFrame&&nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())){metadata.SetHasScrollgrab(true);}// Also compute and set the background color.// This is needed for APZ overscrolling support.if(aScrollFrame){if(isRootScrollFrame){metadata.SetBackgroundColor(sRGBColor::FromABGR(presShell->GetCanvasBackground()));}else{ComputedStyle*backgroundStyle;if(nsCSSRendering::FindBackground(aScrollFrame,&backgroundStyle)){nscolorbackgroundColor=backgroundStyle->StyleBackground()->BackgroundColor(backgroundStyle);metadata.SetBackgroundColor(sRGBColor::FromABGR(backgroundColor));}}}if(ShouldDisableApzForElement(aContent)){metadata.SetForceDisableApz(true);}metadata.SetPrefersReducedMotion(Gecko_MediaFeatures_PrefersReducedMotion(document));returnmetadata;}/*static*/Maybe<ScrollMetadata>nsLayoutUtils::GetRootMetadata(nsDisplayListBuilder*aBuilder,WebRenderLayerManager*aLayerManager,conststd::function<bool(ViewID&aScrollId)>&aCallback){nsIFrame*frame=aBuilder->RootReferenceFrame();nsPresContext*presContext=frame->PresContext();PresShell*presShell=presContext->PresShell();Document*document=presShell->GetDocument();// There is one case where we want the root container layer to have metrics.// If the parent process is using XUL windows, there is no root scrollframe,// and without explicitly creating metrics there will be no guaranteed// top-level APZC.booladdMetrics=XRE_IsParentProcess()&&!presShell->GetRootScrollFrame();// Add metrics if there are none in the layer tree with the id (create an id// if there isn't one already) of the root scroll frame/root content.boolensureMetricsForRootId=nsLayoutUtils::AsyncPanZoomEnabled(frame)&&aBuilder->IsPaintingToWindow()&&!presContext->GetParentPresContext();nsIContent*content=nullptr;nsIFrame*rootScrollFrame=presShell->GetRootScrollFrame();if(rootScrollFrame){content=rootScrollFrame->GetContent();}else{// If there is no root scroll frame, pick the document element instead.// The only case we don't want to do this is in non-APZ fennec, where// we want the root xul document to get a null scroll id so that the root// content document gets the first non-null scroll id.content=document->GetDocumentElement();}if(ensureMetricsForRootId&&content){ViewIDscrollId=nsLayoutUtils::FindOrCreateIDFor(content);if(aCallback(scrollId)){ensureMetricsForRootId=false;}}if(addMetrics||ensureMetricsForRootId){boolisRootContent=presContext->IsRootContentDocumentCrossProcess();nsSizescrollPortSize=frame->GetSize();if(isRootContent&&rootScrollFrame){nsIScrollableFrame*scrollableFrame=rootScrollFrame->GetScrollTargetFrame();scrollPortSize=scrollableFrame->GetScrollPortRect().Size();}returnSome(nsLayoutUtils::ComputeScrollMetadata(frame,rootScrollFrame,content,aBuilder->FindReferenceFrameFor(frame),aLayerManager,ScrollableLayerGuid::NULL_SCROLL_ID,scrollPortSize,Nothing(),isRootContent));}returnNothing();}/* static */boolnsLayoutUtils::ContainsMetricsWithId(constLayer*aLayer,constViewID&aScrollId){for(uint32_ti=aLayer->GetScrollMetadataCount();i>0;i--){if(aLayer->GetFrameMetrics(i-1).GetScrollId()==aScrollId){returntrue;}}for(Layer*child=aLayer->GetFirstChild();child;child=child->GetNextSibling()){if(ContainsMetricsWithId(child,aScrollId)){returntrue;}}returnfalse;}/* static */StyleTouchActionnsLayoutUtils::GetTouchActionFromFrame(nsIFrame*aFrame){if(!aFrame){returnStyleTouchAction::AUTO;}// The touch-action CSS property applies to: all elements except:// non-replaced inline elements, table rows, row groups, table columns, and// column groupsboolisNonReplacedInlineElement=aFrame->IsFrameOfType(nsIFrame::eLineParticipant);if(isNonReplacedInlineElement){returnStyleTouchAction::AUTO;}constnsStyleDisplay*disp=aFrame->StyleDisplay();boolisTableElement=disp->IsInternalTableStyleExceptCell();if(isTableElement){returnStyleTouchAction::AUTO;}returndisp->mTouchAction;}/* static */voidnsLayoutUtils::TransformToAncestorAndCombineRegions(constnsRegion&aRegion,nsIFrame*aFrame,constnsIFrame*aAncestorFrame,nsRegion*aPreciseTargetDest,nsRegion*aImpreciseTargetDest,Maybe<Matrix4x4Flagged>*aMatrixCache,constDisplayItemClip*aClip){if(aRegion.IsEmpty()){return;}boolisPrecise;RegionBuilder<nsRegion>transformedRegion;for(nsRegion::RectIteratorit=aRegion.RectIter();!it.Done();it.Next()){nsRecttransformed=TransformFrameRectToAncestor(aFrame,it.Get(),aAncestorFrame,&isPrecise,aMatrixCache);if(aClip){transformed=aClip->ApplyNonRoundedIntersection(transformed);if(aClip->GetRoundedRectCount()>0){isPrecise=false;}}transformedRegion.OrWith(transformed);}nsRegion*dest=isPrecise?aPreciseTargetDest:aImpreciseTargetDest;dest->OrWith(transformedRegion.ToRegion());// If the region becomes too complex this has a large performance impact.// We limit its complexity here.if(dest->GetNumRects()>12){dest->SimplifyOutward(6);if(isPrecise){aPreciseTargetDest->OrWith(*aImpreciseTargetDest);*aImpreciseTargetDest=std::move(*aPreciseTargetDest);aImpreciseTargetDest->SimplifyOutward(6);*aPreciseTargetDest=nsRegion();}}}/* static */boolnsLayoutUtils::ShouldUseNoScriptSheet(Document*aDocument){// also handle the case where print is done from print preview// see bug #342439 for more detailsif(aDocument->IsStaticDocument()){aDocument=aDocument->GetOriginalDocument();}returnaDocument->IsScriptEnabled();}/* static */boolnsLayoutUtils::ShouldUseNoFramesSheet(Document*aDocument){boolallowSubframes=true;nsIDocShell*docShell=aDocument->GetDocShell();if(docShell){docShell->GetAllowSubframes(&allowSubframes);}return!allowSubframes;}/* static */voidnsLayoutUtils::GetFrameTextContent(nsIFrame*aFrame,nsAString&aResult){aResult.Truncate();AppendFrameTextContent(aFrame,aResult);}/* static */voidnsLayoutUtils::AppendFrameTextContent(nsIFrame*aFrame,nsAString&aResult){if(aFrame->IsTextFrame()){autotextFrame=static_cast<nsTextFrame*>(aFrame);autooffset=textFrame->GetContentOffset();autolength=textFrame->GetContentLength();textFrame->TextFragment()->AppendTo(aResult,offset,length);}else{for(nsIFrame*child:aFrame->PrincipalChildList()){AppendFrameTextContent(child,aResult);}}}/* static */nsRectnsLayoutUtils::GetSelectionBoundingRect(constSelection*aSel){nsRectres;// Bounding client rect may be empty after calling GetBoundingClientRect// when range is collapsed. So we get caret's rect when range is// collapsed.if(aSel->IsCollapsed()){nsIFrame*frame=nsCaret::GetGeometry(aSel,&res);if(frame){nsIFrame*relativeTo=GetContainingBlockForClientRect(frame);res=TransformFrameRectToAncestor(frame,res,relativeTo);}}else{int32_trangeCount=aSel->RangeCount();RectAccumulatoraccumulator;for(int32_tidx=0;idx<rangeCount;++idx){nsRange*range=aSel->GetRangeAt(idx);nsRange::CollectClientRectsAndText(&accumulator,nullptr,range,range->GetStartContainer(),range->StartOffset(),range->GetEndContainer(),range->EndOffset(),true,false);}res=accumulator.mResultRect.IsEmpty()?accumulator.mFirstRect:accumulator.mResultRect;}returnres;}/* static */nsBlockFrame*nsLayoutUtils::GetFloatContainingBlock(nsIFrame*aFrame){nsIFrame*ancestor=aFrame->GetParent();while(ancestor&&!ancestor->IsFloatContainingBlock()){ancestor=ancestor->GetParent();}MOZ_ASSERT(!ancestor||ancestor->IsBlockFrameOrSubclass(),"Float containing block can only be block frame");returnstatic_cast<nsBlockFrame*>(ancestor);}// The implementations of this calculation are adapted from// Element::GetBoundingClientRect()./* static */CSSRectnsLayoutUtils::GetBoundingContentRect(constnsIContent*aContent,constnsIScrollableFrame*aRootScrollFrame,Maybe<CSSRect>*aOutNearestScrollClip){if(nsIFrame*frame=aContent->GetPrimaryFrame()){returnGetBoundingFrameRect(frame,aRootScrollFrame,aOutNearestScrollClip);}returnCSSRect();}/* static */CSSRectnsLayoutUtils::GetBoundingFrameRect(nsIFrame*aFrame,constnsIScrollableFrame*aRootScrollFrame,Maybe<CSSRect>*aOutNearestScrollClip){CSSRectresult;nsIFrame*relativeTo=aRootScrollFrame->GetScrolledFrame();result=CSSRect::FromAppUnits(nsLayoutUtils::GetAllInFlowRectsUnion(aFrame,relativeTo,nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS));// If the element is contained in a scrollable frame that is not// the root scroll frame, make sure to clip the result so that it is// not larger than the containing scrollable frame's bounds.nsIScrollableFrame*scrollFrame=nsLayoutUtils::GetNearestScrollableFrame(aFrame,SCROLLABLE_INCLUDE_HIDDEN|SCROLLABLE_FIXEDPOS_FINDS_ROOT);if(scrollFrame&&scrollFrame!=aRootScrollFrame){nsIFrame*subFrame=do_QueryFrame(scrollFrame);MOZ_ASSERT(subFrame);// Get the bounds of the scroll frame in the same coordinate space// as |result|.nsRectsubFrameRect=subFrame->GetRectRelativeToSelf();TransformResultres=nsLayoutUtils::TransformRect(subFrame,relativeTo,subFrameRect);MOZ_ASSERT(res==TRANSFORM_SUCCEEDED||res==NONINVERTIBLE_TRANSFORM);if(res==TRANSFORM_SUCCEEDED){CSSRectsubFrameRectCSS=CSSRect::FromAppUnits(subFrameRect);if(aOutNearestScrollClip){*aOutNearestScrollClip=Some(subFrameRectCSS);}result=subFrameRectCSS.Intersect(result);}}returnresult;}/* static */boolnsLayoutUtils::IsTransformed(nsIFrame*aForFrame,nsIFrame*aTopFrame){for(nsIFrame*f=aForFrame;f!=aTopFrame;f=f->GetParent()){if(f->IsTransformed()){returntrue;}}returnfalse;}/*static*/CSSPointnsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame*aFrame){CSSPointdelta;if(!aFrame){returndelta;}nsIFrame*frame=aFrame;nsCOMPtr<nsIContent>lastContent;boolseenRcdRsf=false;// Helper lambda to apply the callback transform for a single frame.autoapplyCallbackTransformForFrame=[&](nsIFrame*frame){if(frame){nsCOMPtr<nsIContent>content=frame->GetContent();if(content&&(content!=lastContent)){void*property=content->GetProperty(nsGkAtoms::apzCallbackTransform);if(property){delta+=*static_cast<CSSPoint*>(property);}}lastContent=content;}};while(frame){// Apply the callback transform for the current frame.applyCallbackTransformForFrame(frame);// Keep track of whether we've encountered the RCD-RSF's content element.nsPresContext*pc=frame->PresContext();if(pc->IsRootContentDocumentCrossProcess()){if(PresShell*shell=pc->GetPresShell()){if(nsIFrame*rsf=shell->GetRootScrollFrame()){if(frame->GetContent()==rsf->GetContent()){seenRcdRsf=true;}}}}// If we reach the RCD's viewport frame, but have not encountered// the RCD-RSF, we were inside fixed content in the RCD.// We still want to apply the RCD-RSF's callback transform because// it contains the offset between the visual and layout viewports// which applies to fixed content as well.ViewportFrame*viewportFrame=do_QueryFrame(frame);if(viewportFrame){if(pc->IsRootContentDocumentCrossProcess()&&!seenRcdRsf){applyCallbackTransformForFrame(pc->PresShell()->GetRootScrollFrame());}}// Proceed to the parent frame.frame=GetCrossDocParentFrameInProcess(frame);}returndelta;}staticnsSizeComputeMaxSizeForPartialPrerender(nsIFrame*aFrame,nsSizeaMaxSize){Matrix4x4Flaggedtransform=nsLayoutUtils::GetTransformToAncestor(RelativeTo{aFrame},RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});Matrixtransform2D;if(!transform.Is2D(&transform2D)){returnaMaxSize;}gfx::Rectresult(0,0,aMaxSize.width,aMaxSize.height);gfx::Sizescale=transform2D.ScaleFactors();if(scale.width!=0&&scale.height!=0){result.width/=scale.width;result.height/=scale.height;}// Don't apply translate.transform2D._31=0.0f;transform2D._32=0.0f;// Don't apply scale.if(scale.width!=0&&scale.height!=0){transform2D._11/=scale.width;transform2D._12/=scale.width;transform2D._21/=scale.height;transform2D._22/=scale.height;}// Theoretically we should use transform2D.Inverse() here but in this case// |transform2D| is a pure rotation matrix, no scaling, no translate at all,// so that the result bound's width and height would be pretty much same// as the one rotated by the inverse matrix.result=transform2D.TransformBounds(result);returnnsSize(result.width,result.height);}/* static */nsRectnsLayoutUtils::ComputePartialPrerenderArea(nsIFrame*aFrame,constnsRect&aDirtyRect,constnsRect&aOverflow,constnsSize&aPrerenderSize){nsSizemaxSizeForPartialPrerender=ComputeMaxSizeForPartialPrerender(aFrame,aPrerenderSize);// Simple calculation for now: center the pre-render area on the dirty rect,// and clamp to the overflow area. Later we can do more advanced things like// redistributing from one axis to another, or from one side to another.nscoordxExcess=std::max(maxSizeForPartialPrerender.width-aDirtyRect.width,0);nscoordyExcess=std::max(maxSizeForPartialPrerender.height-aDirtyRect.height,0);nsRectresult=aDirtyRect;result.Inflate(xExcess/2,yExcess/2);returnresult.MoveInsideAndClamp(aOverflow);}staticboolLineHasNonEmptyContentWorker(nsIFrame*aFrame){// Look for non-empty frames, but ignore inline and br frames.// For inline frames, descend into the children, if any.if(aFrame->IsInlineFrame()){for(nsIFrame*child:aFrame->PrincipalChildList()){if(LineHasNonEmptyContentWorker(child)){returntrue;}}}else{if(!aFrame->IsBrFrame()&&!aFrame->IsEmpty()){returntrue;}}returnfalse;}staticboolLineHasNonEmptyContent(nsLineBox*aLine){int32_tcount=aLine->GetChildCount();for(nsIFrame*frame=aLine->mFirstChild;count>0;--count,frame=frame->GetNextSibling()){if(LineHasNonEmptyContentWorker(frame)){returntrue;}}returnfalse;}/* static */boolnsLayoutUtils::IsInvisibleBreak(nsINode*aNode,nsIFrame**aNextLineFrame){if(aNextLineFrame){*aNextLineFrame=nullptr;}if(!aNode->IsElement()||!aNode->IsEditable()){returnfalse;}nsIFrame*frame=aNode->AsElement()->GetPrimaryFrame();if(!frame||!frame->IsBrFrame()){returnfalse;}nsContainerFrame*f=frame->GetParent();while(f&&f->IsFrameOfType(nsIFrame::eLineParticipant)){f=f->GetParent();}nsBlockFrame*blockAncestor=do_QueryFrame(f);if(!blockAncestor){// The container frame doesn't support line breaking.returnfalse;}boolvalid=false;nsBlockInFlowLineIteratoriter(blockAncestor,frame,&valid);if(!valid){returnfalse;}boollineNonEmpty=LineHasNonEmptyContent(iter.GetLine());if(!lineNonEmpty){returnfalse;}while(iter.Next()){autocurrentLine=iter.GetLine();// Completely skip empty lines.if(!currentLine->IsEmpty()){// If we come across an inline line, the BR has caused a visible line// break.if(currentLine->IsInline()){if(aNextLineFrame){*aNextLineFrame=currentLine->mFirstChild;}returnfalse;}break;}}returnlineNonEmpty;}staticnsRectComputeSVGReferenceRect(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){MOZ_ASSERT(aFrame->GetContent()->IsSVGElement());nsRectr;// For SVG elements without associated CSS layout box, the used value for// content-box, padding-box, border-box and margin-box is fill-box.switch(aGeometryBox){caseStyleGeometryBox::StrokeBox:{// XXX Bug 1299876// The size of stroke-box is not correct if this graphic element has// specific stroke-linejoin or stroke-linecap.gfxRectbbox=SVGUtils::GetBBox(aFrame,SVGUtils::eBBoxIncludeFillGeometry|SVGUtils::eBBoxIncludeStroke);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,AppUnitsPerCSSPixel());break;}caseStyleGeometryBox::ViewBox:{nsIContent*content=aFrame->GetContent();SVGElement*element=static_cast<SVGElement*>(content);SVGViewportElement*svgElement=element->GetCtx();MOZ_ASSERT(svgElement);if(svgElement&&svgElement->HasViewBox()){// If a `viewBox` attribute is specified for the SVG viewport creating// element:// 1. The reference box is positioned at the origin of the coordinate// system established by the `viewBox` attribute.// 2. The dimension of the reference box is set to the width and height// values of the `viewBox` attribute.constSVGViewBox&value=svgElement->GetAnimatedViewBox()->GetAnimValue();r=nsRect(nsPresContext::CSSPixelsToAppUnits(value.x),nsPresContext::CSSPixelsToAppUnits(value.y),nsPresContext::CSSPixelsToAppUnits(value.width),nsPresContext::CSSPixelsToAppUnits(value.height));}else{// No viewBox is specified, uses the nearest SVG viewport as reference// box.svgFloatSizeviewportSize=svgElement->GetViewportSize();r=nsRect(0,0,nsPresContext::CSSPixelsToAppUnits(viewportSize.width),nsPresContext::CSSPixelsToAppUnits(viewportSize.height));}break;}caseStyleGeometryBox::NoBox:caseStyleGeometryBox::BorderBox:caseStyleGeometryBox::ContentBox:caseStyleGeometryBox::PaddingBox:caseStyleGeometryBox::MarginBox:caseStyleGeometryBox::FillBox:{gfxRectbbox=SVGUtils::GetBBox(aFrame,SVGUtils::eBBoxIncludeFillGeometry);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,AppUnitsPerCSSPixel());break;}default:{MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");gfxRectbbox=SVGUtils::GetBBox(aFrame,SVGUtils::eBBoxIncludeFillGeometry);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,AppUnitsPerCSSPixel());break;}}returnr;}staticnsRectComputeHTMLReferenceRect(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){nsRectr;// For elements with associated CSS layout box, the used value for fill-box,// stroke-box and view-box is border-box.switch(aGeometryBox){caseStyleGeometryBox::ContentBox:r=aFrame->GetContentRectRelativeToSelf();break;caseStyleGeometryBox::PaddingBox:r=aFrame->GetPaddingRectRelativeToSelf();break;caseStyleGeometryBox::MarginBox:r=aFrame->GetMarginRectRelativeToSelf();break;caseStyleGeometryBox::NoBox:caseStyleGeometryBox::BorderBox:caseStyleGeometryBox::FillBox:caseStyleGeometryBox::StrokeBox:caseStyleGeometryBox::ViewBox:r=aFrame->GetRectRelativeToSelf();break;default:MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");r=aFrame->GetRectRelativeToSelf();break;}returnr;}staticStyleGeometryBoxShapeBoxToGeometryBox(constStyleShapeBox&aBox){switch(aBox){caseStyleShapeBox::BorderBox:returnStyleGeometryBox::BorderBox;caseStyleShapeBox::ContentBox:returnStyleGeometryBox::ContentBox;caseStyleShapeBox::MarginBox:returnStyleGeometryBox::MarginBox;caseStyleShapeBox::PaddingBox:returnStyleGeometryBox::PaddingBox;}MOZ_ASSERT_UNREACHABLE("Unknown shape box type");returnStyleGeometryBox::MarginBox;}staticStyleGeometryBoxClipPathBoxToGeometryBox(constStyleShapeGeometryBox&aBox){usingTag=StyleShapeGeometryBox::Tag;switch(aBox.tag){caseTag::ShapeBox:returnShapeBoxToGeometryBox(aBox.AsShapeBox());caseTag::ElementDependent:returnStyleGeometryBox::NoBox;caseTag::FillBox:returnStyleGeometryBox::FillBox;caseTag::StrokeBox:returnStyleGeometryBox::StrokeBox;caseTag::ViewBox:returnStyleGeometryBox::ViewBox;}MOZ_ASSERT_UNREACHABLE("Unknown shape box type");returnStyleGeometryBox::NoBox;}/* static */nsRectnsLayoutUtils::ComputeGeometryBox(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){// We use ComputeSVGReferenceRect for all SVG elements, except <svg>// element, which does have an associated CSS layout box. In this case we// should still use ComputeHTMLReferenceRect for region computing.returnaFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)?ComputeSVGReferenceRect(aFrame,aGeometryBox):ComputeHTMLReferenceRect(aFrame,aGeometryBox);}nsRectnsLayoutUtils::ComputeGeometryBox(nsIFrame*aFrame,constStyleShapeBox&aBox){returnComputeGeometryBox(aFrame,ShapeBoxToGeometryBox(aBox));}nsRectnsLayoutUtils::ComputeGeometryBox(nsIFrame*aFrame,constStyleShapeGeometryBox&aBox){returnComputeGeometryBox(aFrame,ClipPathBoxToGeometryBox(aBox));}/* static */nsPointnsLayoutUtils::ComputeOffsetToUserSpace(nsDisplayListBuilder*aBuilder,nsIFrame*aFrame){nsPointoffsetToBoundingBox=aBuilder->ToReferenceFrame(aFrame)-SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);if(!aFrame->IsFrameOfType(nsIFrame::eSVG)){// Snap the offset if the reference frame is not a SVG frame, since other// frames will be snapped to pixel when rendering.offsetToBoundingBox=nsPoint(aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));}// During SVG painting, the offset computed here is applied to the gfxContext// "ctx" used to paint the mask. After applying only "offsetToBoundingBox",// "ctx" would have its origin at the top left corner of frame's bounding box// (over all continuations).// However, SVG painting needs the origin to be located at the origin of the// SVG frame's "user space", i.e. the space in which, for example, the// frame's BBox lives.// SVG geometry frames and foreignObject frames apply their own offsets, so// their position is relative to their user space. So for these frame types,// if we want "ctx" to be in user space, we first need to subtract the// frame's position so that SVG painting can later add it again and the// frame is painted in the right place.gfxPointtoUserSpaceGfx=SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);nsPointtoUserSpace=nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));return(offsetToBoundingBox-toUserSpace);}/* static */already_AddRefed<nsFontMetrics>nsLayoutUtils::GetMetricsFor(nsPresContext*aPresContext,boolaIsVertical,constnsStyleFont*aStyleFont,LengthaFontSize,boolaUseUserFontSet){nsFontfont=aStyleFont->mFont;font.size=aFontSize;gfxFont::Orientationorientation=aIsVertical?nsFontMetrics::eVertical:nsFontMetrics::eHorizontal;nsFontMetrics::Paramsparams;params.language=aStyleFont->mLanguage;params.explicitLanguage=aStyleFont->mExplicitLanguage;params.orientation=orientation;params.userFontSet=aUseUserFontSet?aPresContext->GetUserFontSet():nullptr;params.textPerf=aPresContext->GetTextPerfMetrics();params.featureValueLookup=aPresContext->GetFontFeatureValuesLookup();returnaPresContext->GetMetricsFor(font,params);}/* static */voidnsLayoutUtils::ComputeSystemFont(nsFont*aSystemFont,LookAndFeel::FontIDaFontID,constnsFont*aDefaultVariableFont,constDocument*aDocument){gfxFontStylefontStyle;nsAutoStringsystemFontName;if(!LookAndFeel::GetFont(aFontID,systemFontName,fontStyle)){return;}systemFontName.Trim("\"'");NS_ConvertUTF16toUTF8nameu8(systemFontName);Servo_FontFamily_ForSystemFont(&nameu8,&aSystemFont->family);aSystemFont->style=fontStyle.style;aSystemFont->family.is_system_font=fontStyle.systemFont;aSystemFont->weight=fontStyle.weight;aSystemFont->stretch=fontStyle.stretch;aSystemFont->size=Length::FromPixels(fontStyle.size);// aSystemFont->langGroup = fontStyle.langGroup;switch(StyleFontSizeAdjust::Tag(fontStyle.sizeAdjustBasis)){caseStyleFontSizeAdjust::Tag::None:aSystemFont->sizeAdjust=StyleFontSizeAdjust::None();break;caseStyleFontSizeAdjust::Tag::ExHeight:aSystemFont->sizeAdjust=StyleFontSizeAdjust::ExHeight(fontStyle.sizeAdjust);break;caseStyleFontSizeAdjust::Tag::CapHeight:aSystemFont->sizeAdjust=StyleFontSizeAdjust::CapHeight(fontStyle.sizeAdjust);break;caseStyleFontSizeAdjust::Tag::ChWidth:aSystemFont->sizeAdjust=StyleFontSizeAdjust::ChWidth(fontStyle.sizeAdjust);break;caseStyleFontSizeAdjust::Tag::IcWidth:aSystemFont->sizeAdjust=StyleFontSizeAdjust::IcWidth(fontStyle.sizeAdjust);break;caseStyleFontSizeAdjust::Tag::IcHeight:aSystemFont->sizeAdjust=StyleFontSizeAdjust::IcHeight(fontStyle.sizeAdjust);break;}if(aFontID==LookAndFeel::FontID::MozField||aFontID==LookAndFeel::FontID::MozButton||aFontID==LookAndFeel::FontID::MozList){constboolisWindowsOrNonNativeTheme=#ifdef XP_WINtrue||#endifaDocument->ShouldAvoidNativeTheme();if(isWindowsOrNonNativeTheme){// For textfields, buttons and selects, we use whatever font is defined by// the system. Which it appears (and the assumption is) it is always a// proportional font. Then we always use 2 points smaller than what the// browser has defined as the default proportional font.//// This matches historical Windows behavior and other browsers.autonewSize=aDefaultVariableFont->size.ToCSSPixels()-CSSPixel::FromPoints(2.0f);aSystemFont->size=Length::FromPixels(std::max(float(newSize),0.0f));}}}/* static */boolnsLayoutUtils::ShouldHandleMetaViewport(constDocument*aDocument){autometaViewportOverride=nsIDocShell::META_VIEWPORT_OVERRIDE_NONE;if(aDocument){if(nsIDocShell*docShell=aDocument->GetDocShell()){metaViewportOverride=docShell->GetMetaViewportOverride();}}switch(metaViewportOverride){casensIDocShell::META_VIEWPORT_OVERRIDE_ENABLED:returntrue;casensIDocShell::META_VIEWPORT_OVERRIDE_DISABLED:returnfalse;default:MOZ_ASSERT(metaViewportOverride==nsIDocShell::META_VIEWPORT_OVERRIDE_NONE);// The META_VIEWPORT_OVERRIDE_NONE case means that there is no override// and we rely solely on the StaticPrefs.returnStaticPrefs::dom_meta_viewport_enabled();}}/* static */ComputedStyle*nsLayoutUtils::StyleForScrollbar(nsIFrame*aScrollbarPart){// Get the closest content node which is not an anonymous scrollbar// part. It should be the originating element of the scrollbar part.nsIContent*content=aScrollbarPart->GetContent();// Note that the content may be a normal element with scrollbar part// value specified for its -moz-appearance, so don't rely on it being// a native anonymous. Also note that we have to check the node name// because anonymous element like generated content may originate a// scrollbar.MOZ_ASSERT(content,"No content for the scrollbar part?");while(content&&content->IsInNativeAnonymousSubtree()&&content->IsAnyOfXULElements(nsGkAtoms::scrollbar,nsGkAtoms::scrollbarbutton,nsGkAtoms::scrollcorner,nsGkAtoms::slider,nsGkAtoms::thumb)){content=content->GetParent();}MOZ_ASSERT(content,"Native anonymous element with no originating node?");// Use the style from the primary frame of the content.// Note: it is important to use the primary frame rather than an// ancestor frame of the scrollbar part for the correct handling of// viewport scrollbar. The content of the scroll frame of the viewport// is the root element, but its style inherits from the viewport.// Since we need to use the style of root element for the viewport// scrollbar, we have to get the style from the primary frame.if(nsIFrame*primaryFrame=content->GetPrimaryFrame()){returnprimaryFrame->Style();}// If the element doesn't have primary frame, get the computed style// from the element directly. This can happen on viewport, because// the scrollbar of viewport may be shown when the root element has// > display: none; overflow: scroll;MOZ_ASSERT(content==aScrollbarPart->PresContext()->Document()->GetRootElement(),"Root element is the only case for this fallback ""path to be triggered");RefPtr<ComputedStyle>style=ServoStyleSet::ResolveServoStyle(*content->AsElement());// Dropping the strong reference is fine because the style should be// held strongly by the element.returnstyle.get();}// NOTE: Returns Nothing() if |aFrame| is not in out-of-process or if we haven't// received enough information from APZ.staticMaybe<ScreenRect>GetFrameVisibleRectOnScreen(constnsIFrame*aFrame){// We actually want the in-process top prescontext here.nsPresContext*topContextInProcess=aFrame->PresContext()->GetInProcessRootContentDocumentPresContext();if(!topContextInProcess){// We are in chrome process.returnNothing();}if(topContextInProcess->Document()->IsTopLevelContentDocument()){// We are in the top of content document.returnNothing();}nsIDocShell*docShell=topContextInProcess->GetDocShell();BrowserChild*browserChild=BrowserChild::GetFrom(docShell);if(!browserChild){// We are not in out-of-process iframe.returnNothing();}if(!browserChild->GetEffectsInfo().IsVisible()){// There is no visible rect on this iframe at all.returnSome(ScreenRect());}Maybe<ScreenRect>visibleRect=browserChild->GetTopLevelViewportVisibleRectInBrowserCoords();if(!visibleRect){// We are unsure if we haven't received the transformed rectangle of the// iframe's visible area.returnNothing();}nsIFrame*rootFrame=topContextInProcess->PresShell()->GetRootFrame();nsRecttransformedToIFrame=nsLayoutUtils::TransformFrameRectToAncestor(aFrame,aFrame->GetRectRelativeToSelf(),rootFrame);LayoutDeviceRectrectInLayoutDevicePixel=LayoutDeviceRect::FromAppUnits(transformedToIFrame,topContextInProcess->AppUnitsPerDevPixel());ScreenRecttransformedToRoot=ViewAs<ScreenPixel>(browserChild->GetChildToParentConversionMatrix().TransformBounds(rectInLayoutDevicePixel),PixelCastJustification::ContentProcessIsLayerInUiProcess);returnSome(visibleRect->Intersect(transformedToRoot));}// staticboolnsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(constnsIFrame*aFrame){Maybe<ScreenRect>visibleRect=GetFrameVisibleRectOnScreen(aFrame);if(visibleRect.isNothing()){returnfalse;}returnvisibleRect->IsEmpty();}// staticboolnsLayoutUtils::FrameIsMostlyScrolledOutOfViewInCrossProcess(constnsIFrame*aFrame,nscoordaMargin){Maybe<ScreenRect>visibleRect=GetFrameVisibleRectOnScreen(aFrame);if(visibleRect.isNothing()){returnfalse;}nsPresContext*topContextInProcess=aFrame->PresContext()->GetInProcessRootContentDocumentPresContext();MOZ_ASSERT(topContextInProcess);nsIDocShell*docShell=topContextInProcess->GetDocShell();BrowserChild*browserChild=BrowserChild::GetFrom(docShell);MOZ_ASSERT(browserChild);Sizescale=browserChild->GetChildToParentConversionMatrix().As2D().ScaleFactors();ScreenSizemargin(scale.width*CSSPixel::FromAppUnits(aMargin),scale.height*CSSPixel::FromAppUnits(aMargin));returnvisibleRect->width<margin.width||visibleRect->height<margin.height;}// staticnsSizensLayoutUtils::ExpandHeightForViewportUnits(nsPresContext*aPresContext,constnsSize&aSize){nsSizesizeForViewportUnits=aPresContext->GetSizeForViewportUnits();// |aSize| might be the size expanded to the minimum-scale size whereas the// size for viewport units is not scaled so that we need to expand the |aSize|// height by multiplying by the ratio of the viewport units height to the// visible area height.floatvhExpansionRatio=(float)sizeForViewportUnits.height/aPresContext->GetVisibleArea().height;MOZ_ASSERT(aSize.height<=NSCoordSaturatingNonnegativeMultiply(aSize.height,vhExpansionRatio));returnnsSize(aSize.width,NSCoordSaturatingNonnegativeMultiply(aSize.height,vhExpansionRatio));}template<typenameSizeType>/* static */SizeTypeExpandHeightForDynamicToolbarImpl(constnsPresContext*aPresContext,constSizeType&aSize){MOZ_ASSERT(aPresContext);LayoutDeviceIntSizedisplaySize;if(RefPtr<MobileViewportManager>MVM=aPresContext->PresShell()->GetMobileViewportManager()){displaySize=MVM->DisplaySize();}elseif(!nsLayoutUtils::GetContentViewerSize(aPresContext,displaySize)){returnaSize;}floattoolbarHeightRatio=mozilla::ScreenCoord(aPresContext->GetDynamicToolbarMaxHeight())/mozilla::ViewAs<mozilla::ScreenPixel>(displaySize,mozilla::PixelCastJustification::LayoutDeviceIsScreenForBounds).height;returnSizeType(aSize.width,NSCoordSaturatingAdd(aSize.height,aSize.height*toolbarHeightRatio));}CSSSizensLayoutUtils::ExpandHeightForDynamicToolbar(constnsPresContext*aPresContext,constCSSSize&aSize){returnExpandHeightForDynamicToolbarImpl(aPresContext,aSize);}nsSizensLayoutUtils::ExpandHeightForDynamicToolbar(constnsPresContext*aPresContext,constnsSize&aSize){returnExpandHeightForDynamicToolbarImpl(aPresContext,aSize);}