| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. |
| * All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| |
| #include <inttypes.h> |
| |
| #include "build/build_config.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_screen_info.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/element.h" |
| #include "third_party/blink/renderer/core/editing/frame_selection.h" |
| #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/html/html_iframe_element.h" |
| #include "third_party/blink/renderer/core/input/event_handler.h" |
| #include "third_party/blink/renderer/core/layout/hit_test_result.h" |
| #include "third_party/blink/renderer/core/layout/layout_counter.h" |
| #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" |
| #include "third_party/blink/renderer/core/layout/layout_geometry_map.h" |
| #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h" |
| #include "third_party/blink/renderer/core/layout/view_fragmentation_context.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" |
| #include "third_party/blink/renderer/core/paint/view_painter.h" |
| #include "third_party/blink/renderer/core/svg/svg_document_extensions.h" |
| #include "third_party/blink/renderer/platform/geometry/float_quad.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" |
| #include "third_party/blink/renderer/platform/histogram.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| #include "third_party/blink/renderer/platform/transforms/transform_state.h" |
| |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) |
| #include "third_party/blink/renderer/platform/fonts/font_cache.h" |
| #endif |
| |
| namespace blink { |
| |
| namespace { |
| |
| class HitTestLatencyRecorder { |
| public: |
| HitTestLatencyRecorder(bool allows_child_frame_content) |
| : start_(CurrentTimeTicks()), |
| allows_child_frame_content_(allows_child_frame_content) {} |
| |
| ~HitTestLatencyRecorder() { |
| TimeDelta duration = CurrentTimeTicks() - start_; |
| if (allows_child_frame_content_) { |
| DEFINE_STATIC_LOCAL(CustomCountHistogram, recursive_latency_histogram, |
| ("Event.Latency.HitTestRecursive", 0, 10000000, 100)); |
| recursive_latency_histogram.Count(duration.InMicroseconds()); |
| } else { |
| DEFINE_STATIC_LOCAL(CustomCountHistogram, latency_histogram, |
| ("Event.Latency.HitTest", 0, 10000000, 100)); |
| latency_histogram.Count(duration.InMicroseconds()); |
| } |
| } |
| |
| private: |
| TimeTicks start_; |
| bool allows_child_frame_content_; |
| }; |
| |
| } // namespace |
| |
| LayoutView::LayoutView(Document* document) |
| : LayoutBlockFlow(document), |
| frame_view_(document->View()), |
| layout_state_(nullptr), |
| layout_quote_head_(nullptr), |
| layout_counter_count_(0), |
| hit_test_count_(0), |
| hit_test_cache_hits_(0), |
| hit_test_cache_(HitTestCache::Create()) { |
| // init LayoutObject attributes |
| SetInline(false); |
| |
| min_preferred_logical_width_ = LayoutUnit(); |
| max_preferred_logical_width_ = LayoutUnit(); |
| |
| SetPreferredLogicalWidthsDirty(kMarkOnlyThis); |
| |
| SetPositionState(EPosition::kAbsolute); // to 0,0 :) |
| } |
| |
| LayoutView::~LayoutView() = default; |
| |
| bool LayoutView::HitTest(HitTestResult& result) { |
| // We have to recursively update layout/style here because otherwise, when the |
| // hit test recurses into a child document, it could trigger a layout on the |
| // parent document, which can destroy PaintLayer that are higher up in the |
| // call stack, leading to crashes. |
| // Note that Document::updateLayout calls its parent's updateLayout. |
| // Note that if an iframe has its render pipeline throttled, it will not |
| // update layout here, and it will also not propagate the hit test into the |
| // iframe's inner document. |
| if (!GetFrameView()->UpdateLifecycleToPrePaintClean()) |
| return false; |
| HitTestLatencyRecorder hit_test_latency_recorder( |
| result.GetHitTestRequest().AllowsChildFrameContent()); |
| return HitTestNoLifecycleUpdate(result); |
| } |
| |
| bool LayoutView::HitTestNoLifecycleUpdate(HitTestResult& result) { |
| TRACE_EVENT_BEGIN0("blink,devtools.timeline", "HitTest"); |
| hit_test_count_++; |
| |
| DCHECK(!result.GetHitTestLocation().IsRectBasedTest() || |
| result.GetHitTestRequest().ListBased()); |
| |
| uint64_t dom_tree_version = GetDocument().DomTreeVersion(); |
| HitTestResult cache_result = result; |
| bool hit_layer = false; |
| if (hit_test_cache_->LookupCachedResult(cache_result, dom_tree_version)) { |
| hit_test_cache_hits_++; |
| hit_layer = true; |
| result = cache_result; |
| } else { |
| hit_layer = Layer()->HitTest(result); |
| |
| // If hitTestResult include scrollbar, innerNode should be the parent of the |
| // scrollbar. |
| if (result.GetScrollbar()) { |
| // Clear innerNode if we hit a scrollbar whose ScrollableArea isn't |
| // associated with a LayoutBox so we aren't hitting some random element |
| // below too. |
| result.SetInnerNode(nullptr); |
| result.SetURLElement(nullptr); |
| ScrollableArea* scrollable_area = |
| result.GetScrollbar()->GetScrollableArea(); |
| if (scrollable_area && scrollable_area->GetLayoutBox() && |
| scrollable_area->GetLayoutBox()->GetNode()) { |
| Node* node = scrollable_area->GetLayoutBox()->GetNode(); |
| |
| // If scrollbar belongs to Document, we should set innerNode to the |
| // <html> element to match other browser. |
| if (node->IsDocumentNode()) |
| node = node->GetDocument().documentElement(); |
| |
| result.SetInnerNode(node); |
| result.SetURLElement(node->EnclosingLinkEventParentOrSelf()); |
| } |
| } |
| |
| if (hit_layer) |
| hit_test_cache_->AddCachedResult(result, dom_tree_version); |
| } |
| |
| TRACE_EVENT_END1( |
| "blink,devtools.timeline", "HitTest", "endData", |
| InspectorHitTestEvent::EndData(result.GetHitTestRequest(), |
| result.GetHitTestLocation(), result)); |
| return hit_layer; |
| } |
| |
| void LayoutView::ClearHitTestCache() { |
| hit_test_cache_->Clear(); |
| auto* object = GetFrame()->OwnerLayoutObject(); |
| if (object) |
| object->View()->ClearHitTestCache(); |
| } |
| |
| void LayoutView::ComputeLogicalHeight( |
| LayoutUnit logical_height, |
| LayoutUnit, |
| LogicalExtentComputedValues& computed_values) const { |
| computed_values.extent_ = LayoutUnit(ViewLogicalHeightForBoxSizing()); |
| } |
| |
| void LayoutView::UpdateLogicalWidth() { |
| SetLogicalWidth(LayoutUnit(ViewLogicalWidthForBoxSizing())); |
| } |
| |
| bool LayoutView::IsChildAllowed(LayoutObject* child, |
| const ComputedStyle&) const { |
| return child->IsBox(); |
| } |
| |
| bool LayoutView::CanHaveChildren() const { |
| FrameOwner* owner = GetFrame()->Owner(); |
| if (!owner) |
| return true; |
| if (!RuntimeEnabledFeatures::DisplayNoneIFrameCreatesNoLayoutObjectEnabled()) |
| return true; |
| // Although it is not spec compliant, many websites intentionally call |
| // Window.print() on display:none iframes. https://crbug.com/819327. |
| if (GetDocument().Printing()) |
| return true; |
| // A PluginDocument needs a layout tree during loading, even if it is inside a |
| // display: none iframe. This is because WebLocalFrameImpl::DidFinish expects |
| // the PluginDocument's <embed> element to have an EmbeddedContentView, which |
| // it acquires during LocalFrameView::UpdatePlugins, which operates on the |
| // <embed> element's layout object (LayoutEmbeddedObject). |
| if (GetDocument().IsPluginDocument()) |
| return true; |
| return !owner->IsDisplayNone(); |
| } |
| |
| #if DCHECK_IS_ON() |
| void LayoutView::CheckLayoutState() { |
| DCHECK(!layout_state_->Next()); |
| } |
| #endif |
| |
| void LayoutView::SetShouldDoFullPaintInvalidationOnResizeIfNeeded( |
| bool width_changed, |
| bool height_changed) { |
| // When background-attachment is 'fixed', we treat the viewport (instead of |
| // the 'root' i.e. html or body) as the background positioning area, and we |
| // should fully invalidate on viewport resize if the background image is not |
| // composited and needs full paint invalidation on background positioning area |
| // resize. |
| if (Style()->HasFixedBackgroundImage()) { |
| if ((width_changed && MustInvalidateFillLayersPaintOnWidthChange( |
| Style()->BackgroundLayers())) || |
| (height_changed && MustInvalidateFillLayersPaintOnHeightChange( |
| Style()->BackgroundLayers()))) |
| SetShouldDoFullPaintInvalidation(PaintInvalidationReason::kBackground); |
| } |
| } |
| |
| void LayoutView::UpdateBlockLayout(bool relayout_children) { |
| SubtreeLayoutScope layout_scope(*this); |
| |
| // Use calcWidth/Height to get the new width/height, since this will take the |
| // full page zoom factor into account. |
| relayout_children |= |
| !ShouldUsePrintingLayout() && |
| (!frame_view_ || LogicalWidth() != ViewLogicalWidthForBoxSizing() || |
| LogicalHeight() != ViewLogicalHeightForBoxSizing()); |
| |
| if (relayout_children) { |
| layout_scope.SetChildNeedsLayout(this); |
| for (LayoutObject* child = FirstChild(); child; |
| child = child->NextSibling()) { |
| if (child->IsSVGRoot()) |
| continue; |
| |
| if ((child->IsBox() && ToLayoutBox(child)->HasRelativeLogicalHeight()) || |
| child->Style()->LogicalHeight().IsPercentOrCalc() || |
| child->Style()->LogicalMinHeight().IsPercentOrCalc() || |
| child->Style()->LogicalMaxHeight().IsPercentOrCalc()) |
| layout_scope.SetChildNeedsLayout(child); |
| } |
| |
| if (GetDocument().SvgExtensions()) |
| GetDocument() |
| .AccessSVGExtensions() |
| .InvalidateSVGRootsWithRelativeLengthDescendents(&layout_scope); |
| } |
| |
| if (!NeedsLayout()) |
| return; |
| |
| LayoutBlockFlow::UpdateBlockLayout(relayout_children); |
| } |
| |
| void LayoutView::UpdateLayout() { |
| if (!GetDocument().Printing()) |
| SetPageLogicalHeight(LayoutUnit()); |
| |
| // TODO(wangxianzhu): Move this into ViewPaintInvalidator. |
| SetShouldDoFullPaintInvalidationOnResizeIfNeeded( |
| OffsetWidth() != GetLayoutSize(kIncludeScrollbars).Width(), |
| OffsetHeight() != GetLayoutSize(kIncludeScrollbars).Height()); |
| |
| if (PageLogicalHeight() && ShouldUsePrintingLayout()) { |
| min_preferred_logical_width_ = max_preferred_logical_width_ = |
| LogicalWidth(); |
| if (!fragmentation_context_) { |
| fragmentation_context_ = |
| std::make_unique<ViewFragmentationContext>(*this); |
| pagination_state_changed_ = true; |
| } |
| } else if (fragmentation_context_) { |
| fragmentation_context_.reset(); |
| pagination_state_changed_ = true; |
| } |
| |
| DCHECK(!layout_state_); |
| LayoutState root_layout_state(*this); |
| |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) |
| // The font code in FontPlatformData does not have a direct connection to the |
| // document, the frame or anything from which we could retrieve the device |
| // scale factor. After using zoom for DSF, the GraphicsContext does only ever |
| // have a DSF of 1 on Linux. In order for the font code to be aware of an up |
| // to date DSF when layout happens, we plumb this through to the FontCache, so |
| // that we can correctly retrieve RenderStyleForStrike from out of |
| // process. crbug.com/845468 |
| FontCache::SetDeviceScaleFactor(GetFrameView() |
| ->GetFrame() |
| .GetChromeClient() |
| .GetScreenInfo() |
| .device_scale_factor); |
| #endif |
| |
| LayoutBlockFlow::UpdateLayout(); |
| |
| #if DCHECK_IS_ON() |
| CheckLayoutState(); |
| #endif |
| ClearNeedsLayout(); |
| } |
| |
| LayoutRect LayoutView::LocalVisualRectIgnoringVisibility() const { |
| LayoutRect rect = VisualOverflowRect(); |
| rect.Unite(LayoutRect(rect.Location(), ViewRect().Size())); |
| return rect; |
| } |
| |
| void LayoutView::MapLocalToAncestor(const LayoutBoxModelObject* ancestor, |
| TransformState& transform_state, |
| MapCoordinatesFlags mode) const { |
| if (!ancestor && mode & kUseTransforms && |
| ShouldUseTransformFromContainer(nullptr)) { |
| TransformationMatrix t; |
| GetTransformFromContainer(nullptr, LayoutSize(), t); |
| transform_state.ApplyTransform(t); |
| } |
| |
| if ((mode & kIsFixed) && frame_view_) { |
| transform_state.Move(OffsetForFixedPosition()); |
| // IsFixed flag is only applicable within this LayoutView. |
| mode &= ~kIsFixed; |
| } |
| |
| if (ancestor == this) |
| return; |
| |
| if (mode & kTraverseDocumentBoundaries) { |
| auto* parent_doc_layout_object = GetFrame()->OwnerLayoutObject(); |
| if (parent_doc_layout_object) { |
| if (!(mode & kInputIsInFrameCoordinates)) { |
| transform_state.Move( |
| LayoutSize(-GetFrame()->View()->GetScrollOffset())); |
| } else { |
| // The flag applies to immediate LayoutView only. |
| mode &= ~kInputIsInFrameCoordinates; |
| } |
| |
| transform_state.Move(parent_doc_layout_object->ContentBoxOffset()); |
| |
| parent_doc_layout_object->MapLocalToAncestor(ancestor, transform_state, |
| mode); |
| } else { |
| GetFrameView()->ApplyTransformForTopFrameSpace(transform_state); |
| } |
| } |
| } |
| |
| const LayoutObject* LayoutView::PushMappingToContainer( |
| const LayoutBoxModelObject* ancestor_to_stop_at, |
| LayoutGeometryMap& geometry_map) const { |
| LayoutSize offset; |
| LayoutObject* container = nullptr; |
| |
| if (geometry_map.GetMapCoordinatesFlags() & kTraverseDocumentBoundaries) { |
| if (auto* parent_doc_layout_object = GetFrame()->OwnerLayoutObject()) { |
| offset = -LayoutSize(frame_view_->GetScrollOffset()); |
| offset += parent_doc_layout_object->ContentBoxOffset(); |
| container = parent_doc_layout_object; |
| } |
| } |
| |
| // If a container was specified, and was not 0 or the LayoutView, then we |
| // should have found it by now unless we're traversing to a parent document. |
| DCHECK(!ancestor_to_stop_at || ancestor_to_stop_at == this || container); |
| |
| if ((!ancestor_to_stop_at || container) && |
| ShouldUseTransformFromContainer(container)) { |
| TransformationMatrix t; |
| GetTransformFromContainer(container, LayoutSize(), t); |
| geometry_map.Push(this, t, kContainsFixedPosition, |
| OffsetForFixedPosition()); |
| } else { |
| geometry_map.Push(this, offset, 0, OffsetForFixedPosition()); |
| } |
| |
| return container; |
| } |
| |
| void LayoutView::MapAncestorToLocal(const LayoutBoxModelObject* ancestor, |
| TransformState& transform_state, |
| MapCoordinatesFlags mode) const { |
| if (this != ancestor && (mode & kTraverseDocumentBoundaries)) { |
| if (auto* parent_doc_layout_object = GetFrame()->OwnerLayoutObject()) { |
| // A LayoutView is a containing block for fixed-position elements, so |
| // don't carry this state across frames. |
| parent_doc_layout_object->MapAncestorToLocal(ancestor, transform_state, |
| mode & ~kIsFixed); |
| |
| transform_state.Move(parent_doc_layout_object->ContentBoxOffset()); |
| transform_state.Move(LayoutSize(-GetFrame()->View()->GetScrollOffset())); |
| } |
| } else { |
| DCHECK(this == ancestor || !ancestor); |
| } |
| |
| if (mode & kIsFixed) |
| transform_state.Move(OffsetForFixedPosition()); |
| } |
| |
| void LayoutView::ComputeSelfHitTestRects(Vector<LayoutRect>& rects, |
| const LayoutPoint&) const { |
| // Record the entire size of the contents of the frame. Note that we don't |
| // just use the viewport size (containing block) here because we want to |
| // ensure this includes all children (so we can avoid walking them |
| // explicitly). |
| rects.push_back(LayoutRect(LayoutPoint::Zero(), |
| LayoutSize(GetFrameView()->ContentsSize()))); |
| } |
| |
| void LayoutView::Paint(const PaintInfo& paint_info, |
| const LayoutPoint& paint_offset) const { |
| ViewPainter(*this).Paint(paint_info, paint_offset); |
| } |
| |
| void LayoutView::PaintBoxDecorationBackground(const PaintInfo& paint_info, |
| const LayoutPoint&) const { |
| ViewPainter(*this).PaintBoxDecorationBackground(paint_info); |
| } |
| |
| static void SetShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal( |
| LayoutObject* object) { |
| object->SetShouldDoFullPaintInvalidation(); |
| for (LayoutObject* child = object->SlowFirstChild(); child; |
| child = child->NextSibling()) { |
| SetShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(child); |
| } |
| } |
| |
| void LayoutView::SetShouldDoFullPaintInvalidationForViewAndAllDescendants() { |
| if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) |
| SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); |
| else |
| SetShouldDoFullPaintInvalidationForViewAndAllDescendantsInternal(this); |
| } |
| |
| void LayoutView::InvalidatePaintForViewAndCompositedLayers() { |
| SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); |
| |
| // The only way we know how to hit these ASSERTS below this point is via the |
| // Chromium OS login screen. |
| DisableCompositingQueryAsserts disabler; |
| |
| if (Compositor()->InCompositingMode()) |
| Compositor()->FullyInvalidatePaint(); |
| } |
| |
| bool LayoutView::MapToVisualRectInAncestorSpace( |
| const LayoutBoxModelObject* ancestor, |
| LayoutRect& rect, |
| MapCoordinatesFlags mode, |
| VisualRectFlags visual_rect_flags) const { |
| bool intersects = true; |
| if (MapToVisualRectInAncestorSpaceInternalFastPath( |
| ancestor, rect, visual_rect_flags, intersects)) |
| return intersects; |
| |
| TransformState transform_state(TransformState::kApplyTransformDirection, |
| FloatQuad(FloatRect(rect))); |
| intersects = MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state, |
| mode, visual_rect_flags); |
| transform_state.Flatten(); |
| rect = LayoutRect(transform_state.LastPlanarQuad().BoundingBox()); |
| return intersects; |
| } |
| |
| bool LayoutView::MapToVisualRectInAncestorSpaceInternal( |
| const LayoutBoxModelObject* ancestor, |
| TransformState& transform_state, |
| VisualRectFlags visual_rect_flags) const { |
| return MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state, 0, |
| visual_rect_flags); |
| } |
| |
| bool LayoutView::MapToVisualRectInAncestorSpaceInternal( |
| const LayoutBoxModelObject* ancestor, |
| TransformState& transform_state, |
| MapCoordinatesFlags mode, |
| VisualRectFlags visual_rect_flags) const { |
| if (mode & kIsFixed) |
| transform_state.Move(OffsetForFixedPosition()); |
| |
| // Apply our transform if we have one (because of full page zooming). |
| if (Layer() && Layer()->Transform()) { |
| transform_state.ApplyTransform(Layer()->CurrentTransform(), |
| TransformState::kFlattenTransform); |
| } |
| |
| transform_state.Flatten(); |
| |
| if (ancestor == this) |
| return true; |
| |
| Element* owner = GetDocument().LocalOwner(); |
| if (!owner) { |
| LayoutRect rect(transform_state.LastPlanarQuad().BoundingBox()); |
| bool retval = GetFrameView()->MapToVisualRectInTopFrameSpace(rect); |
| transform_state.SetQuad(FloatQuad(FloatRect(rect))); |
| return retval; |
| } |
| |
| if (LayoutBox* obj = owner->GetLayoutBox()) { |
| LayoutRect rect(transform_state.LastPlanarQuad().BoundingBox()); |
| if (!(mode & kInputIsInFrameCoordinates)) { |
| // Intersect the viewport with the visual rect. |
| LayoutRect view_rectangle = ViewRect(); |
| if (visual_rect_flags & kEdgeInclusive) { |
| if (!rect.InclusiveIntersect(view_rectangle)) { |
| transform_state.SetQuad(FloatQuad(FloatRect(rect))); |
| return false; |
| } |
| } else { |
| rect.Intersect(view_rectangle); |
| } |
| |
| // Adjust for scroll offset of the view. |
| rect.MoveBy(-view_rectangle.Location()); |
| } |
| // Frames are painted at rounded-int position. Since we cannot efficiently |
| // compute the subpixel offset of painting at this point in a a bottom-up |
| // walk, round to the enclosing int rect, which will enclose the actual |
| // visible rect. |
| rect = LayoutRect(EnclosingIntRect(rect)); |
| |
| // Adjust for frame border. |
| rect.Move(obj->ContentBoxOffset()); |
| transform_state.SetQuad(FloatQuad(FloatRect(rect))); |
| |
| return obj->MapToVisualRectInAncestorSpaceInternal( |
| ancestor, transform_state, visual_rect_flags); |
| } |
| |
| // This can happen, e.g., if the iframe element has display:none. |
| transform_state.SetQuad(FloatQuad(FloatRect())); |
| return false; |
| } |
| |
| LayoutSize LayoutView::OffsetForFixedPosition() const { |
| FloatSize adjustment; |
| if (frame_view_) { |
| adjustment += frame_view_->GetScrollOffset(); |
| } |
| |
| if (HasOverflowClip()) |
| adjustment += FloatSize(ScrolledContentOffset()); |
| |
| return RoundedLayoutSize(adjustment); |
| } |
| |
| void LayoutView::AbsoluteRects(Vector<IntRect>& rects, |
| const LayoutPoint& accumulated_offset) const { |
| rects.push_back( |
| PixelSnappedIntRect(accumulated_offset, LayoutSize(Layer()->Size()))); |
| } |
| |
| void LayoutView::AbsoluteQuads(Vector<FloatQuad>& quads, |
| MapCoordinatesFlags mode) const { |
| quads.push_back(LocalToAbsoluteQuad( |
| FloatRect(FloatPoint(), FloatSize(Layer()->Size())), mode)); |
| } |
| |
| void LayoutView::ClearSelection() { |
| frame_view_->GetFrame().Selection().ClearLayoutSelection(); |
| } |
| |
| void LayoutView::CommitPendingSelection() { |
| TRACE_EVENT0("blink", "LayoutView::commitPendingSelection"); |
| DCHECK(!NeedsLayout()); |
| frame_view_->GetFrame().Selection().CommitAppearanceIfNeeded(); |
| } |
| |
| bool LayoutView::ShouldUsePrintingLayout() const { |
| if (!GetDocument().Printing() || !frame_view_) |
| return false; |
| return frame_view_->GetFrame().ShouldUsePrintingLayout(); |
| } |
| |
| LayoutRect LayoutView::ViewRect() const { |
| if (ShouldUsePrintingLayout()) |
| return LayoutRect(LayoutPoint(), Size()); |
| if (frame_view_) |
| return LayoutRect(LayoutPoint(), LayoutSize(frame_view_->Size())); |
| return LayoutRect(); |
| } |
| |
| LayoutRect LayoutView::OverflowClipRect( |
| const LayoutPoint& location, |
| OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const { |
| LayoutRect rect = ViewRect(); |
| if (rect.IsEmpty()) |
| return LayoutBox::OverflowClipRect(location, |
| overlay_scrollbar_clip_behavior); |
| |
| rect.SetLocation(location); |
| if (HasOverflowClip()) |
| ExcludeScrollbars(rect, overlay_scrollbar_clip_behavior); |
| |
| return rect; |
| } |
| |
| void LayoutView::CalculateScrollbarModes(ScrollbarMode& h_mode, |
| ScrollbarMode& v_mode) const { |
| #define RETURN_SCROLLBAR_MODE(mode) \ |
| { \ |
| h_mode = v_mode = mode; \ |
| return; \ |
| } |
| |
| LocalFrame* frame = GetFrame(); |
| if (!frame) |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| |
| if (FrameOwner* owner = frame->Owner()) { |
| // Setting scrolling="no" on an iframe element disables scrolling. |
| if (owner->ScrollingMode() == kScrollbarAlwaysOff) |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| } |
| |
| Document& document = GetDocument(); |
| if (Node* body = document.body()) { |
| // Framesets can't scroll. |
| if (IsHTMLFrameSetElement(body) && body->GetLayoutObject()) |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| } |
| |
| if (document.Printing()) { |
| // When printing, frame-level scrollbars are never displayed. |
| // TODO(szager): Figure out the right behavior when printing an overflowing |
| // iframe. https://bugs.chromium.org/p/chromium/issues/detail?id=777528 |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| } |
| |
| if (LocalFrameView* frameView = GetFrameView()) { |
| // Scrollbars can be disabled by LocalFrameView::setCanHaveScrollbars. |
| if (!frameView->CanHaveScrollbars()) |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| } |
| |
| Element* viewportDefiningElement = document.ViewportDefiningElement(); |
| if (!viewportDefiningElement) |
| RETURN_SCROLLBAR_MODE(kScrollbarAuto); |
| |
| LayoutObject* viewport = viewportDefiningElement->GetLayoutObject(); |
| if (!viewport) |
| RETURN_SCROLLBAR_MODE(kScrollbarAuto); |
| |
| const ComputedStyle* style = viewport->Style(); |
| if (!style) |
| RETURN_SCROLLBAR_MODE(kScrollbarAuto); |
| |
| if (viewport->IsSVGRoot()) { |
| // Don't allow overflow to affect <img> and css backgrounds |
| if (ToLayoutSVGRoot(viewport)->IsEmbeddedThroughSVGImage()) |
| RETURN_SCROLLBAR_MODE(kScrollbarAuto); |
| |
| // FIXME: evaluate if we can allow overflow for these cases too. |
| // Overflow is always hidden when stand-alone SVG documents are embedded. |
| if (ToLayoutSVGRoot(viewport) |
| ->IsEmbeddedThroughFrameContainingSVGDocument()) |
| RETURN_SCROLLBAR_MODE(kScrollbarAlwaysOff); |
| } |
| |
| h_mode = v_mode = kScrollbarAuto; |
| |
| EOverflow overflow_x = style->OverflowX(); |
| EOverflow overflow_y = style->OverflowY(); |
| |
| bool shouldIgnoreOverflowHidden = false; |
| if (Settings* settings = document.GetSettings()) { |
| if (settings->GetIgnoreMainFrameOverflowHiddenQuirk() && |
| frame->IsMainFrame()) |
| shouldIgnoreOverflowHidden = true; |
| } |
| if (!shouldIgnoreOverflowHidden) { |
| if (overflow_x == EOverflow::kHidden) |
| h_mode = kScrollbarAlwaysOff; |
| if (overflow_y == EOverflow::kHidden) |
| v_mode = kScrollbarAlwaysOff; |
| } |
| |
| if (overflow_x == EOverflow::kScroll) |
| h_mode = kScrollbarAlwaysOn; |
| if (overflow_y == EOverflow::kScroll) |
| v_mode = kScrollbarAlwaysOn; |
| |
| #undef RETURN_SCROLLBAR_MODE |
| } |
| |
| void LayoutView::DispatchFakeMouseMoveEventSoon(EventHandler& event_handler) { |
| event_handler.DispatchFakeMouseMoveEventSoon( |
| MouseEventManager::FakeMouseMoveReason::kDuringScroll); |
| } |
| |
| IntRect LayoutView::DocumentRect() const { |
| LayoutRect overflow_rect(LayoutOverflowRect()); |
| FlipForWritingMode(overflow_rect); |
| // TODO(crbug.com/650768): The pixel snapping looks incorrect. |
| return PixelSnappedIntRect(overflow_rect); |
| } |
| |
| IntSize LayoutView::GetLayoutSize( |
| IncludeScrollbarsInRect scrollbar_inclusion) const { |
| if (ShouldUsePrintingLayout()) |
| return IntSize(Size().Width().ToInt(), PageLogicalHeight().ToInt()); |
| |
| if (!frame_view_) |
| return IntSize(); |
| |
| IntSize result = frame_view_->GetLayoutSize(kIncludeScrollbars); |
| if (scrollbar_inclusion == kExcludeScrollbars) |
| result = frame_view_->LayoutViewport()->ExcludeScrollbars(result); |
| return result; |
| } |
| |
| int LayoutView::ViewLogicalWidth( |
| IncludeScrollbarsInRect scrollbar_inclusion) const { |
| return Style()->IsHorizontalWritingMode() ? ViewWidth(scrollbar_inclusion) |
| : ViewHeight(scrollbar_inclusion); |
| } |
| |
| int LayoutView::ViewLogicalHeight( |
| IncludeScrollbarsInRect scrollbar_inclusion) const { |
| return Style()->IsHorizontalWritingMode() ? ViewHeight(scrollbar_inclusion) |
| : ViewWidth(scrollbar_inclusion); |
| } |
| |
| LayoutUnit LayoutView::ViewLogicalHeightForPercentages() const { |
| if (ShouldUsePrintingLayout()) |
| return PageLogicalHeight(); |
| return LayoutUnit(ViewLogicalHeight()); |
| } |
| |
| float LayoutView::ZoomFactor() const { |
| return frame_view_->GetFrame().PageZoomFactor(); |
| } |
| |
| const LayoutBox& LayoutView::RootBox() const { |
| Element* document_element = GetDocument().documentElement(); |
| DCHECK(document_element); |
| DCHECK(document_element->GetLayoutObject()); |
| DCHECK(document_element->GetLayoutObject()->IsBox()); |
| return ToLayoutBox(*document_element->GetLayoutObject()); |
| } |
| |
| void LayoutView::UpdateAfterLayout() { |
| // Unlike every other layer, the root PaintLayer takes its size from the |
| // layout viewport size. The call to AdjustViewSize() will update the |
| // frame's contents size, which will also update the page's minimum scale |
| // factor. The call to ResizeAfterLayout() will calculate the layout viewport |
| // size based on the page minimum scale factor, and then update the |
| // LocalFrameView with the new size. |
| LocalFrame& frame = GetFrameView()->GetFrame(); |
| if (!GetDocument().Printing()) |
| GetFrameView()->AdjustViewSize(); |
| if (frame.IsMainFrame()) |
| frame.GetChromeClient().ResizeAfterLayout(); |
| if (HasOverflowClip()) |
| GetScrollableArea()->ClampScrollOffsetAfterOverflowChange(); |
| LayoutBlockFlow::UpdateAfterLayout(); |
| } |
| |
| void LayoutView::UpdateHitTestResult(HitTestResult& result, |
| const LayoutPoint& point) const { |
| if (result.InnerNode()) |
| return; |
| |
| Node* node = GetDocument().documentElement(); |
| if (node) { |
| LayoutPoint adjusted_point = point; |
| OffsetForContents(adjusted_point); |
| result.SetNodeAndPosition(node, adjusted_point); |
| } |
| } |
| |
| bool LayoutView::UsesCompositing() const { |
| return compositor_ && compositor_->StaleInCompositingMode(); |
| } |
| |
| PaintLayerCompositor* LayoutView::Compositor() { |
| if (!compositor_) |
| compositor_ = std::make_unique<PaintLayerCompositor>(*this); |
| |
| return compositor_.get(); |
| } |
| |
| void LayoutView::SetIsInWindow(bool is_in_window) { |
| if (compositor_) |
| compositor_->SetIsInWindow(is_in_window); |
| } |
| |
| IntervalArena* LayoutView::GetIntervalArena() { |
| if (!interval_arena_) |
| interval_arena_ = IntervalArena::Create(); |
| return interval_arena_.get(); |
| } |
| |
| bool LayoutView::BackgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const { |
| // FIXME: Remove this main frame check. Same concept applies to subframes too. |
| if (!GetFrame()->IsMainFrame()) |
| return false; |
| |
| return frame_view_->HasOpaqueBackground(); |
| } |
| |
| FloatSize LayoutView::ViewportSizeForViewportUnits() const { |
| return GetFrameView() ? GetFrameView()->ViewportSizeForViewportUnits() |
| : FloatSize(); |
| } |
| |
| void LayoutView::WillBeDestroyed() { |
| // TODO(wangxianzhu): This is a workaround of crbug.com/570706. |
| // Should find and fix the root cause. |
| if (PaintLayer* layer = Layer()) |
| layer->SetNeedsRepaint(); |
| LayoutBlockFlow::WillBeDestroyed(); |
| compositor_.reset(); |
| } |
| |
| void LayoutView::UpdateFromStyle() { |
| LayoutBlockFlow::UpdateFromStyle(); |
| |
| // LayoutView of the main frame is responsible for painting base background. |
| if (GetDocument().IsInMainFrame()) |
| SetHasBoxDecorationBackground(true); |
| } |
| |
| LayoutRect LayoutView::DebugRect() const { |
| LayoutRect rect; |
| LayoutBlock* block = ContainingBlock(); |
| if (block) |
| block->AdjustChildDebugRect(rect); |
| |
| rect.SetWidth(LayoutUnit(ViewWidth(kIncludeScrollbars))); |
| rect.SetHeight(LayoutUnit(ViewHeight(kIncludeScrollbars))); |
| |
| return rect; |
| } |
| |
| IntSize LayoutView::ScrolledContentOffset() const { |
| DCHECK(HasOverflowClip()); |
| // FIXME: Return DoubleSize here. crbug.com/414283. |
| return GetScrollableArea()->ScrollOffsetInt(); |
| } |
| |
| bool LayoutView::UpdateLogicalWidthAndColumnWidth() { |
| bool relayout_children = LayoutBlockFlow::UpdateLogicalWidthAndColumnWidth(); |
| // When we're printing, the size of LayoutView is changed outside of layout, |
| // so we'll fail to detect any changes here. Just return true. |
| return relayout_children || ShouldUsePrintingLayout(); |
| } |
| |
| void LayoutView::UpdateCounters() { |
| if (!needs_counter_update_) |
| return; |
| |
| needs_counter_update_ = false; |
| if (!HasLayoutCounters()) |
| return; |
| |
| for (LayoutObject* layout_object = this; layout_object; |
| layout_object = layout_object->NextInPreOrder()) { |
| if (!layout_object->IsCounter()) |
| continue; |
| |
| ToLayoutCounter(layout_object)->UpdateCounter(); |
| } |
| } |
| |
| Vector<IntRect> LayoutView::GetTickmarks() const { |
| if (!tickmarks_override_.IsEmpty()) |
| return tickmarks_override_; |
| |
| return GetDocument().Markers().LayoutRectsForTextMatchMarkers(); |
| } |
| |
| void LayoutView::OverrideTickmarks(const Vector<IntRect>& tickmarks) { |
| tickmarks_override_ = tickmarks; |
| InvalidatePaintForTickmarks(); |
| } |
| |
| void LayoutView::InvalidatePaintForTickmarks() { |
| ScrollableArea* scrollable_area = GetScrollableArea(); |
| if (!scrollable_area) |
| return; |
| Scrollbar* scrollbar = scrollable_area->VerticalScrollbar(); |
| if (!scrollbar) |
| return; |
| scrollbar->SetNeedsPaintInvalidation(static_cast<ScrollbarPart>(~kThumbPart)); |
| } |
| |
| } // namespace blink |