ViewportFrame.cpp (21978B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * rendering object that is the root of the frame tree, which contains 9 * the document's scrollbars and contains fixed-positioned elements 10 */ 11 12 #include "mozilla/ViewportFrame.h" 13 14 #include "MobileViewportManager.h" 15 #include "mozilla/AbsoluteContainingBlock.h" 16 #include "mozilla/ComputedStyleInlines.h" 17 #include "mozilla/PresShell.h" 18 #include "mozilla/ProfilerLabels.h" 19 #include "mozilla/RestyleManager.h" 20 #include "mozilla/ScrollContainerFrame.h" 21 #include "mozilla/dom/ViewTransition.h" 22 #include "nsCanvasFrame.h" 23 #include "nsGkAtoms.h" 24 #include "nsLayoutUtils.h" 25 #include "nsPlaceholderFrame.h" 26 #include "nsSubDocumentFrame.h" 27 28 using namespace mozilla; 29 30 // ScrollContainerFrame can create two other wrap lists for scrollbars and such. 31 static constexpr uint16_t kFirstTopLayerIndex = 2; 32 enum class TopLayerIndex : uint16_t { 33 // The content-accessible top layer (fullscreen, <dialog>, popover). 34 Content = kFirstTopLayerIndex, 35 // The view transitions and anonymous content top layer. View transitions need 36 // to be separate from the content top layer, because the former needs to be 37 // potentially captured by a view transition, but the later can't be 38 // (otherwise it'd be cyclic). 39 // The native anonymous content are for things like the one for DevTools 40 // highlighters and other non-web-visible UI. 41 ViewTransitionsAndAnonymousContent, 42 }; 43 44 ViewportFrame* NS_NewViewportFrame(PresShell* aPresShell, 45 ComputedStyle* aStyle) { 46 return new (aPresShell) ViewportFrame(aStyle, aPresShell->GetPresContext()); 47 } 48 49 NS_IMPL_FRAMEARENA_HELPERS(ViewportFrame) 50 NS_QUERYFRAME_HEAD(ViewportFrame) 51 NS_QUERYFRAME_ENTRY(ViewportFrame) 52 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 53 54 void ViewportFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 55 nsIFrame* aPrevInFlow) { 56 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 57 // No need to call CreateView() here - the frame ctor will call SetView() 58 // with the ViewManager's root view, so we'll assign it in SetViewInternal(). 59 60 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(this); 61 if (parent) { 62 nsFrameState state = parent->GetStateBits(); 63 64 AddStateBits(state & (NS_FRAME_IN_POPUP)); 65 } 66 } 67 68 void ViewportFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 69 const nsDisplayListSet& aLists) { 70 AUTO_PROFILER_LABEL("ViewportFrame::BuildDisplayList", 71 GRAPHICS_DisplayListBuilding); 72 73 nsIFrame* kid = mFrames.FirstChild(); 74 if (!kid) { 75 return; 76 } 77 78 nsDisplayListCollection set(aBuilder); 79 BuildDisplayListForChild(aBuilder, kid, set); 80 81 // If we have a scrollframe then it takes care of creating the display list 82 // for the top layer, but otherwise we need to do it here. 83 if (!kid->IsScrollContainerFrame()) { 84 bool isOpaque = false; 85 if (auto* list = BuildDisplayListForContentTopLayer(aBuilder, &isOpaque)) { 86 if (isOpaque) { 87 set.DeleteAll(aBuilder); 88 } 89 set.PositionedDescendants()->AppendToTop(list); 90 } 91 if (auto* list = 92 BuildDisplayListForViewTransitionsAndNACTopLayer(aBuilder)) { 93 set.PositionedDescendants()->AppendToTop(list); 94 } 95 } 96 97 set.MoveTo(aLists); 98 } 99 100 #ifdef DEBUG 101 // Returns whether we are going to put an element in the top layer for 102 // fullscreen. This function should matches the CSS rules in ua.css and xul.css. 103 static bool ShouldInTopLayerForFullscreen(dom::Element* aElement) { 104 return !aElement->IsRootElement() && 105 !aElement->IsXULElement(nsGkAtoms::browser); 106 } 107 #endif // DEBUG 108 109 static void BuildDisplayListForTopLayerFrame(nsDisplayListBuilder* aBuilder, 110 nsIFrame* aFrame, 111 nsDisplayList* aList) { 112 nsRect visible; 113 nsRect dirty; 114 DisplayListClipState::AutoSaveRestore clipState(aBuilder); 115 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder); 116 if (auto* savedOutOfFlowData = 117 nsDisplayListBuilder::GetOutOfFlowData(aFrame)) { 118 visible = 119 savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, aFrame, &dirty); 120 // If we are in the top layer, our containing block is the viewport, which 121 // can't be captured by a view transition on the same document itself. 122 // Also, the top layer is painted from the root scrollframe, so that 123 // already takes care of clearing the ASR / clip when captured. 124 // TODO(emilio): We might need to clear the ASR / clip when coming from the 125 // viewport (for chrome / XUL docs). 126 if (!aBuilder->IsInViewTransitionCapture()) { 127 // This function is called after we've finished building display items for 128 // the root scroll frame. That means that the content clip from the root 129 // scroll frame is no longer on aBuilder. However, we need to make sure 130 // that the display items we build in this function have finite clipped 131 // bounds with respect to the root ASR, so we restore the *combined clip* 132 // that we saved earlier. The combined clip will include the clip from the 133 // root scroll frame. 134 clipState.SetClipChainForContainingBlockDescendants( 135 savedOutOfFlowData->mCombinedClipChain); 136 asrSetter.SetCurrentActiveScrolledRoot( 137 savedOutOfFlowData->mContainingBlockActiveScrolledRoot); 138 asrSetter.SetCurrentScrollParentId(savedOutOfFlowData->mScrollParentId); 139 } 140 } 141 142 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( 143 aBuilder, aFrame, visible, dirty); 144 145 nsDisplayList list(aBuilder); 146 aFrame->BuildDisplayListForStackingContext(aBuilder, &list); 147 aList->AppendToTop(&list); 148 } 149 150 static bool BackdropListIsOpaque(ViewportFrame* aFrame, 151 nsDisplayListBuilder* aBuilder, 152 nsDisplayList* aList) { 153 // The common case for ::backdrop elements on the top layer is a single 154 // fixed position container, holding an opaque background color covering 155 // the whole viewport. 156 if (aList->Length() != 1 || 157 aList->GetTop()->GetType() != DisplayItemType::TYPE_FIXED_POSITION) { 158 return false; 159 } 160 161 // Make sure the fixed position container isn't clipped or scrollable. 162 nsDisplayFixedPosition* fixed = 163 static_cast<nsDisplayFixedPosition*>(aList->GetTop()); 164 if (fixed->GetActiveScrolledRoot() || fixed->GetClipChain()) { 165 return false; 166 } 167 168 nsDisplayList* children = fixed->GetChildren(); 169 if (!children->GetTop() || 170 children->GetTop()->GetType() != DisplayItemType::TYPE_BACKGROUND_COLOR) { 171 return false; 172 } 173 174 nsDisplayBackgroundColor* child = 175 static_cast<nsDisplayBackgroundColor*>(children->GetTop()); 176 if (child->GetActiveScrolledRoot() || child->GetClipChain()) { 177 return false; 178 } 179 180 // Check that the background color is both opaque, and covering the 181 // whole viewport. 182 bool dummy; 183 nsRegion opaque = child->GetOpaqueRegion(aBuilder, &dummy); 184 return opaque.Contains(aFrame->GetRect()); 185 } 186 187 nsDisplayWrapList* ViewportFrame::MaybeWrapTopLayerList( 188 nsDisplayListBuilder* aBuilder, uint16_t aIndex, 189 nsDisplayList& aTopLayerList) { 190 if (aTopLayerList.IsEmpty()) { 191 return nullptr; 192 } 193 nsPoint offset = aBuilder->GetCurrentFrame()->GetOffsetTo(this); 194 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList( 195 aBuilder, this, aBuilder->GetVisibleRect() + offset, 196 aBuilder->GetDirtyRect() + offset); 197 // Wrap the whole top layer in a single item with maximum z-index, 198 // and append it at the very end, so that it stays at the topmost. 199 nsDisplayWrapList* wrapList = MakeDisplayItemWithIndex<nsDisplayWrapper>( 200 aBuilder, this, aIndex, &aTopLayerList, false); 201 if (!wrapList) { 202 return nullptr; 203 } 204 wrapList->SetOverrideZIndex( 205 std::numeric_limits<decltype(wrapList->ZIndex())>::max()); 206 return wrapList; 207 } 208 209 nsDisplayWrapList* ViewportFrame::BuildDisplayListForContentTopLayer( 210 nsDisplayListBuilder* aBuilder, bool* aIsOpaque) { 211 if (aBuilder->AvoidBuildingDuplicateOofs()) { 212 return nullptr; 213 } 214 215 nsDisplayList topLayerList(aBuilder); 216 auto* doc = PresContext()->Document(); 217 218 nsTArray<dom::Element*> topLayer = doc->GetTopLayer(); 219 for (dom::Element* elem : topLayer) { 220 nsIFrame* frame = elem->GetPrimaryFrame(); 221 if (!frame) { 222 continue; 223 } 224 if (frame->GetContent() != elem->AsContent()) { 225 // area elements in image maps point to the image frame as their primary 226 // frame but we should treat them like they don't have their own frame 227 // here. See also bug 135040. 228 continue; 229 } 230 231 if (frame->IsHiddenByContentVisibilityOnAnyAncestor( 232 nsIFrame::IncludeContentVisibility::Hidden)) { 233 continue; 234 } 235 236 // There are two cases where an element in fullscreen is not in 237 // the top layer: 238 // 1. When building display list for purpose other than painting, 239 // it is possible that there is inconsistency between the style 240 // info and the content tree. 241 // 2. This is an element which we are not going to put in the top 242 // layer for fullscreen. See ShouldInTopLayerForFullscreen(). 243 // In both cases, we want to skip the frame here and paint it in 244 // the normal path. 245 if (frame->StyleDisplay()->mTopLayer == StyleTopLayer::None) { 246 MOZ_ASSERT(!aBuilder->IsForPainting() || 247 !elem->State().HasState(dom::ElementState::FULLSCREEN) || 248 !ShouldInTopLayerForFullscreen(elem)); 249 continue; 250 } 251 MOZ_ASSERT_IF(elem->State().HasState(dom::ElementState::FULLSCREEN), 252 ShouldInTopLayerForFullscreen(elem)); 253 // Inner SVG, MathML elements, as well as children of some XUL 254 // elements are not allowed to be out-of-flow. They should not 255 // be handled as top layer element here. 256 if (!frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 257 MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(), 258 "HTML element should always be out-of-flow if in the top " 259 "layer"); 260 continue; 261 } 262 if (auto* backdropFrame = nsLayoutUtils::GetBackdropFrame(elem)) { 263 BuildDisplayListForTopLayerFrame(aBuilder, backdropFrame, &topLayerList); 264 if (aIsOpaque) { 265 *aIsOpaque = BackdropListIsOpaque(this, aBuilder, &topLayerList); 266 } 267 } 268 BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList); 269 } 270 271 return MaybeWrapTopLayerList(aBuilder, uint16_t(TopLayerIndex::Content), 272 topLayerList); 273 } 274 275 nsDisplayWrapList* 276 ViewportFrame::BuildDisplayListForViewTransitionsAndNACTopLayer( 277 nsDisplayListBuilder* aBuilder) { 278 if (aBuilder->AvoidBuildingDuplicateOofs()) { 279 return nullptr; 280 } 281 282 nsDisplayList topLayerList(aBuilder); 283 auto* doc = PresContext()->Document(); 284 if (dom::ViewTransition* vt = doc->GetActiveViewTransition()) { 285 if (dom::Element* root = vt->GetSnapshotContainingBlock()) { 286 if (nsIFrame* frame = root->GetPrimaryFrame()) { 287 MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != StyleTopLayer::None, 288 "the snapshot containing block should ensure this"); 289 MOZ_ASSERT(frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); 290 BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList); 291 } 292 } 293 } 294 295 if (dom::Element* container = doc->GetCustomContentContainer()) { 296 if (nsIFrame* frame = container->GetPrimaryFrame()) { 297 MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != StyleTopLayer::None, 298 "ua.css should ensure this"); 299 MOZ_ASSERT(frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); 300 BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList); 301 } 302 } 303 304 return MaybeWrapTopLayerList( 305 aBuilder, uint16_t(TopLayerIndex::ViewTransitionsAndAnonymousContent), 306 topLayerList); 307 } 308 309 #ifdef DEBUG 310 void ViewportFrame::AppendFrames(ChildListID aListID, 311 nsFrameList&& aFrameList) { 312 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list"); 313 NS_ASSERTION(GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); 314 nsContainerFrame::AppendFrames(aListID, std::move(aFrameList)); 315 } 316 317 void ViewportFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, 318 const nsLineList::iterator* aPrevFrameLine, 319 nsFrameList&& aFrameList) { 320 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list"); 321 NS_ASSERTION(GetChildList(aListID).IsEmpty(), "Shouldn't have any kids!"); 322 nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine, 323 std::move(aFrameList)); 324 } 325 326 void ViewportFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID, 327 nsIFrame* aOldFrame) { 328 NS_ASSERTION(aListID == FrameChildListID::Principal, "unexpected child list"); 329 nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); 330 } 331 #endif 332 333 void ViewportFrame::Destroy(DestroyContext& aContext) { 334 if (PresShell()->IsDestroying()) { 335 PresShell::ClearMouseCapture(this); 336 } 337 nsContainerFrame::Destroy(aContext); 338 } 339 340 nscoord ViewportFrame::IntrinsicISize(const IntrinsicSizeInput& aInput, 341 IntrinsicISizeType aType) { 342 return mFrames.IsEmpty() 343 ? 0 344 : mFrames.FirstChild()->IntrinsicISize(aInput, aType); 345 } 346 347 nsRect ViewportFrame::GetContainingBlockAdjustedForScrollbars( 348 const ReflowInput& aReflowInput) const { 349 const WritingMode wm = aReflowInput.GetWritingMode(); 350 351 LogicalSize computedSize = aReflowInput.ComputedSize(); 352 const nsPoint& origin = [&]() { 353 // Get our prinicpal child frame and see if we're scrollable 354 nsIFrame* kidFrame = mFrames.FirstChild(); 355 if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(kidFrame)) { 356 // Note: In ReflowInput::CalculateHypotheticalPosition(), we exclude the 357 // scrollbar or scrollbar-gutter area when computing the offset to 358 // ViewportFrame. Ensure the code there remains in sync with the logic 359 // here. 360 LogicalMargin scrollbars(wm, 361 scrollContainerFrame->GetActualScrollbarSizes()); 362 computedSize.ISize(wm) = 363 std::max(0, aReflowInput.ComputedISize() - scrollbars.IStartEnd(wm)); 364 computedSize.BSize(wm) = 365 std::max(0, aReflowInput.ComputedBSize() - scrollbars.BStartEnd(wm)); 366 return nsPoint(scrollbars.Left(wm), scrollbars.Top(wm)); 367 } 368 return nsPoint(0, 0); 369 }(); 370 371 nsRect rect(origin, computedSize.GetPhysicalSize(wm)); 372 rect.SizeTo(AdjustViewportSizeForFixedPosition(rect)); 373 374 return rect; 375 } 376 377 void ViewportFrame::Reflow(nsPresContext* aPresContext, 378 ReflowOutput& aDesiredSize, 379 const ReflowInput& aReflowInput, 380 nsReflowStatus& aStatus) { 381 MarkInReflow(); 382 DO_GLOBAL_REFLOW_COUNT("ViewportFrame"); 383 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 384 NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow"); 385 386 // Because |Reflow| sets ComputedBSize() on the child to our 387 // ComputedBSize(). 388 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); 389 390 // Set our size up front, since some parts of reflow depend on it 391 // being already set. Note that the computed height may be 392 // unconstrained; that's ok. Consumers should watch out for that. 393 SetSize(aReflowInput.ComputedPhysicalSize()); 394 395 // Reflow the main content first so that the placeholders of the 396 // fixed-position frames will be in the right places on an initial 397 // reflow. 398 nscoord kidBSize = 0; 399 WritingMode wm = aReflowInput.GetWritingMode(); 400 401 if (mFrames.NotEmpty()) { 402 // Deal with a non-incremental reflow or an incremental reflow 403 // targeted at our one-and-only principal child frame. 404 if (aReflowInput.ShouldReflowAllKids() || 405 mFrames.FirstChild()->IsSubtreeDirty()) { 406 // Reflow our one-and-only principal child frame 407 nsIFrame* kidFrame = mFrames.FirstChild(); 408 ReflowOutput kidDesiredSize(aReflowInput); 409 const WritingMode kidWM = kidFrame->GetWritingMode(); 410 LogicalSize availableSpace = aReflowInput.AvailableSize(kidWM); 411 ReflowInput kidReflowInput(aPresContext, aReflowInput, kidFrame, 412 availableSpace); 413 414 // Reflow the frame 415 kidReflowInput.SetComputedBSize(aReflowInput.ComputedBSize()); 416 if (aReflowInput.IsBResizeForWM(kidWM)) { 417 kidReflowInput.SetBResize(true); 418 } 419 if (aReflowInput.IsBResizeForPercentagesForWM(kidWM)) { 420 kidReflowInput.SetBResizeForPercentages(true); 421 } 422 ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowInput, 0, 0, 423 ReflowChildFlags::Default, aStatus); 424 kidBSize = kidDesiredSize.BSize(wm); 425 426 FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowInput, 427 0, 0, ReflowChildFlags::Default); 428 } else { 429 kidBSize = LogicalSize(wm, mFrames.FirstChild()->GetSize()).BSize(wm); 430 } 431 } 432 433 NS_ASSERTION(aReflowInput.AvailableISize() != NS_UNCONSTRAINEDSIZE, 434 "shouldn't happen anymore"); 435 436 // Return the max size as our desired size 437 LogicalSize maxSize(wm, aReflowInput.AvailableISize(), 438 // Being flowed initially at an unconstrained block size 439 // means we should return our child's intrinsic size. 440 aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE 441 ? aReflowInput.ComputedBSize() 442 : kidBSize); 443 aDesiredSize.SetSize(wm, maxSize); 444 aDesiredSize.SetOverflowAreasToDesiredBounds(); 445 446 if (HasAbsolutelyPositionedChildren()) { 447 // Make a copy of the reflow input and change the computed block size to 448 // reflect the available space for the fixed items 449 ReflowInput reflowInput(aReflowInput); 450 451 if (reflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE) { 452 // We have an intrinsic-block-size document with abs-pos/fixed-pos 453 // children. Set the available block-size and computed block-size to our 454 // chosen block-size. 455 reflowInput.SetAvailableBSize(maxSize.BSize(wm)); 456 // Not having border/padding simplifies things 457 NS_ASSERTION(reflowInput.ComputedPhysicalBorderPadding() == nsMargin(), 458 "Viewports can't have border/padding"); 459 reflowInput.SetComputedBSize(maxSize.BSize(wm)); 460 } 461 462 // The containing block for children. We intentionally not take scrollbar 463 // size and dynamic toolbar into account because 464 // ::-moz-snapshot-containing-block should include those areas. 465 // 466 // We will take them into account in AbsoluteContainingBlock::Reflow(), 467 // for kid frames other than ::-moz-snapshot-containing-block. 468 const nsRect cb(nsPoint(), reflowInput.ComputedPhysicalSize()); 469 // XXX: To optimize the performance, set the flags only when the CB width or 470 // height actually changes. 471 AbsPosReflowFlags flags{AbsPosReflowFlag::CBWidthChanged, 472 AbsPosReflowFlag::CBHeightChanged}; 473 GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowInput, 474 aStatus, cb, flags, 475 /* aOverflowAreas = */ nullptr); 476 } 477 478 if (mFrames.NotEmpty()) { 479 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild()); 480 } 481 482 // If we were dirty then do a repaint 483 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { 484 InvalidateFrame(); 485 } 486 487 // Clipping is handled by the document container (e.g., nsSubDocumentFrame), 488 // so we don't need to change our overflow areas. 489 FinishAndStoreOverflow(&aDesiredSize); 490 491 NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus); 492 } 493 494 void ViewportFrame::UpdateStyle(ServoRestyleState& aRestyleState) { 495 RefPtr<ComputedStyle> newStyle = 496 aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle( 497 Style()->GetPseudoType(), nullptr); 498 499 MOZ_ASSERT(!GetNextContinuation(), "Viewport has continuations?"); 500 SetComputedStyle(newStyle); 501 502 UpdateStyleOfOwnedAnonBoxes(aRestyleState); 503 } 504 505 void ViewportFrame::AppendDirectlyOwnedAnonBoxes( 506 nsTArray<OwnedAnonBox>& aResult) { 507 if (mFrames.NotEmpty()) { 508 aResult.AppendElement(mFrames.FirstChild()); 509 } 510 } 511 512 nsSize ViewportFrame::AdjustViewportSizeForFixedPosition( 513 const nsRect& aViewportRect) const { 514 nsSize result = aViewportRect.Size(); 515 516 mozilla::PresShell* presShell = PresShell(); 517 // Layout fixed position elements to the visual viewport size if and only if 518 // it has been set and it is larger than the computed size, otherwise use the 519 // computed size. 520 if (presShell->IsVisualViewportSizeSet()) { 521 if (presShell->GetDynamicToolbarState() == DynamicToolbarState::Collapsed && 522 result < presShell->GetVisualViewportSizeUpdatedByDynamicToolbar()) { 523 // We need to use the viewport size updated by the dynamic toolbar in the 524 // case where the dynamic toolbar is completely hidden. 525 result = presShell->GetVisualViewportSizeUpdatedByDynamicToolbar(); 526 } else if (result < presShell->GetVisualViewportSize()) { 527 result = presShell->GetVisualViewportSize(); 528 } 529 } 530 // Expand the size to the layout viewport size if necessary. 531 const nsSize layoutViewportSize = presShell->GetLayoutViewportSize(); 532 if (result < layoutViewportSize) { 533 result = layoutViewportSize; 534 } 535 536 return result; 537 } 538 539 #ifdef DEBUG_FRAME_DUMP 540 nsresult ViewportFrame::GetFrameName(nsAString& aResult) const { 541 return MakeFrameName(u"Viewport"_ns, aResult); 542 } 543 #endif