blob: 25fd42ffc454d3e7ae5e01c29d13aa9e2113adba [file] [log] [blame]
/*
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* 1999 Lars Knoll <knoll@kde.org>
* 1999 Antti Koivisto <koivisto@kde.org>
* 2000 Dirk Mueller <mueller@kde.org>
* Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
* (C) 2006 Graham Dennis (graham.dennis@gmail.com)
* (C) 2006 Alexey Proskuryakov (ap@nypop.com)
* Copyright (C) 2009 Google 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 "core/frame/LocalFrameView.h"
#include <algorithm>
#include <memory>
#include "core/HTMLNames.h"
#include "core/MediaTypeNames.h"
#include "core/animation/DocumentAnimations.h"
#include "core/css/FontFaceSet.h"
#include "core/dom/AXObjectCache.h"
#include "core/dom/DOMNodeIds.h"
#include "core/dom/ElementVisibilityObserver.h"
#include "core/dom/Fullscreen.h"
#include "core/dom/IntersectionObserverCallback.h"
#include "core/dom/IntersectionObserverController.h"
#include "core/dom/IntersectionObserverInit.h"
#include "core/dom/ResizeObserverController.h"
#include "core/dom/StyleChangeReason.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/editing/DragCaret.h"
#include "core/editing/EditingUtilities.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/RenderedPosition.h"
#include "core/editing/markers/DocumentMarkerController.h"
#include "core/events/ErrorEvent.h"
#include "core/frame/BrowserControls.h"
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/Location.h"
#include "core/frame/PageScaleConstraintsSet.h"
#include "core/frame/RemoteFrame.h"
#include "core/frame/RemoteFrameView.h"
#include "core/frame/Settings.h"
#include "core/frame/VisualViewport.h"
#include "core/html/HTMLFrameElement.h"
#include "core/html/HTMLPlugInElement.h"
#include "core/html/TextControlElement.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "core/input/EventHandler.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "core/layout/LayoutAnalyzer.h"
#include "core/layout/LayoutCounter.h"
#include "core/layout/LayoutEmbeddedContent.h"
#include "core/layout/LayoutEmbeddedObject.h"
#include "core/layout/LayoutScrollbar.h"
#include "core/layout/LayoutScrollbarPart.h"
#include "core/layout/LayoutView.h"
#include "core/layout/ScrollAlignment.h"
#include "core/layout/TextAutosizer.h"
#include "core/layout/TracedLayoutObject.h"
#include "core/layout/api/LayoutBoxModel.h"
#include "core/layout/api/LayoutEmbeddedContentItem.h"
#include "core/layout/api/LayoutItem.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/layout/compositing/CompositedLayerMapping.h"
#include "core/layout/compositing/CompositedSelection.h"
#include "core/layout/compositing/CompositingInputsUpdater.h"
#include "core/layout/compositing/PaintLayerCompositor.h"
#include "core/layout/svg/LayoutSVGRoot.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameLoader.h"
#include "core/page/AutoscrollController.h"
#include "core/page/ChromeClient.h"
#include "core/page/FocusController.h"
#include "core/page/FrameTree.h"
#include "core/page/Page.h"
#include "core/page/PrintContext.h"
#include "core/page/scrolling/RootScrollerUtil.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/page/scrolling/TopDocumentRootScrollerController.h"
#include "core/paint/BlockPaintInvalidator.h"
#include "core/paint/FramePainter.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintTiming.h"
#include "core/paint/PrePaintTreeWalk.h"
#include "core/plugins/PluginView.h"
#include "core/probe/CoreProbes.h"
#include "core/style/ComputedStyle.h"
#include "core/svg/SVGDocumentExtensions.h"
#include "core/svg/SVGSVGElement.h"
#include "platform/Histogram.h"
#include "platform/Language.h"
#include "platform/PlatformChromeClient.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/ScriptForbiddenScope.h"
#include "platform/WebFrameScheduler.h"
#include "platform/fonts/FontCache.h"
#include "platform/geometry/DoubleRect.h"
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/LayoutRect.h"
#include "platform/geometry/TransformState.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/GraphicsLayerDebugInfo.h"
#include "platform/graphics/compositing/PaintArtifactCompositor.h"
#include "platform/graphics/paint/CullRect.h"
#include "platform/graphics/paint/PaintController.h"
#include "platform/graphics/paint/ScopedPaintChunkProperties.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/instrumentation/tracing/TracedValue.h"
#include "platform/json/JSONValues.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/scroll/ScrollAnimatorBase.h"
#include "platform/scroll/ScrollbarTheme.h"
#include "platform/scroll/ScrollerSizeMetrics.h"
#include "platform/text/TextStream.h"
#include "platform/wtf/CurrentTime.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/StdLibExtras.h"
#include "public/platform/WebDisplayItemList.h"
// Used to check for dirty layouts violating document lifecycle rules.
// If arg evaluates to true, the program will continue. If arg evaluates to
// false, program will crash if DCHECK_IS_ON() or return false from the current
// function.
#define CHECK_FOR_DIRTY_LAYOUT(arg) \
do { \
if (!(arg)) { \
NOTREACHED(); \
return false; \
} \
} while (false)
namespace {
// Page dimensions in pixels at 72 DPI.
constexpr int kA4PortraitPageWidth = 595;
constexpr int kA4PortraitPageHeight = 842;
constexpr int kLetterPortraitPageWidth = 612;
constexpr int kLetterPortraitPageHeight = 792;
} // namespace
namespace blink {
using namespace HTMLNames;
// The maximum number of updatePlugins iterations that should be done before
// returning.
static const unsigned kMaxUpdatePluginsIterations = 2;
static const double kResourcePriorityUpdateDelayAfterScroll = 0.250;
static bool g_initial_track_all_paint_invalidations = false;
LocalFrameView::LocalFrameView(LocalFrame& frame, IntRect frame_rect)
: frame_(frame),
frame_rect_(frame_rect),
is_attached_(false),
display_mode_(kWebDisplayModeBrowser),
can_have_scrollbars_(true),
has_pending_layout_(false),
in_synchronous_post_layout_(false),
post_layout_tasks_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &frame),
this,
&LocalFrameView::PostLayoutTimerFired),
update_plugins_timer_(
TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &frame),
this,
&LocalFrameView::UpdatePluginsTimerFired),
base_background_color_(Color::kWhite),
media_type_(MediaTypeNames::screen),
safe_to_propagate_scroll_to_parent_(true),
scroll_corner_(nullptr),
sticky_position_object_count_(0),
input_events_scale_factor_for_emulation_(1),
layout_size_fixed_to_frame_size_(true),
did_scroll_timer_(TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &frame),
this,
&LocalFrameView::DidScrollTimerFired),
needs_update_geometries_(false),
horizontal_scrollbar_mode_(kScrollbarAuto),
vertical_scrollbar_mode_(kScrollbarAuto),
horizontal_scrollbar_lock_(false),
vertical_scrollbar_lock_(false),
scrollbars_suppressed_(false),
in_update_scrollbars_(false),
frame_timing_requests_dirty_(true),
hidden_for_throttling_(false),
subtree_throttled_(false),
lifecycle_updates_throttled_(false),
needs_paint_property_update_(true),
current_update_lifecycle_phases_target_state_(
DocumentLifecycle::kUninitialized),
scroll_anchor_(this),
scrollbar_manager_(*this),
needs_scrollbars_update_(false),
suppress_adjust_view_size_(false),
allows_layout_invalidation_after_layout_clean_(true),
main_thread_scrolling_reasons_(0) {
Init();
}
LocalFrameView* LocalFrameView::Create(LocalFrame& frame) {
LocalFrameView* view = new LocalFrameView(frame, IntRect());
view->Show();
return view;
}
LocalFrameView* LocalFrameView::Create(LocalFrame& frame,
const IntSize& initial_size) {
LocalFrameView* view =
new LocalFrameView(frame, IntRect(IntPoint(), initial_size));
view->SetLayoutSizeInternal(initial_size);
view->Show();
return view;
}
LocalFrameView::~LocalFrameView() {
#if DCHECK_IS_ON()
DCHECK(has_been_disposed_);
#endif
}
DEFINE_TRACE(LocalFrameView) {
visitor->Trace(frame_);
visitor->Trace(fragment_anchor_);
visitor->Trace(scrollable_areas_);
visitor->Trace(animating_scrollable_areas_);
visitor->Trace(auto_size_info_);
visitor->Trace(plugins_);
visitor->Trace(scrollbars_);
visitor->Trace(viewport_scrollable_area_);
visitor->Trace(visibility_observer_);
visitor->Trace(scroll_anchor_);
visitor->Trace(anchoring_adjustment_queue_);
visitor->Trace(scrollbar_manager_);
visitor->Trace(print_context_);
ScrollableArea::Trace(visitor);
}
void LocalFrameView::Reset() {
// The compositor throttles the main frame using deferred commits, we can't
// throttle it here or it seems the root compositor doesn't get setup
// properly.
if (RuntimeEnabledFeatures::
RenderingPipelineThrottlingLoadingIframesEnabled())
lifecycle_updates_throttled_ = !GetFrame().IsMainFrame();
has_pending_layout_ = false;
layout_scheduling_enabled_ = true;
in_synchronous_post_layout_ = false;
layout_count_ = 0;
nested_layout_count_ = 0;
post_layout_tasks_timer_.Stop();
update_plugins_timer_.Stop();
first_layout_ = true;
safe_to_propagate_scroll_to_parent_ = true;
last_viewport_size_ = IntSize();
last_zoom_factor_ = 1.0f;
tracked_object_paint_invalidations_ =
WTF::WrapUnique(g_initial_track_all_paint_invalidations
? new Vector<ObjectPaintInvalidation>
: nullptr);
visually_non_empty_character_count_ = 0;
visually_non_empty_pixel_count_ = 0;
is_visually_non_empty_ = false;
main_thread_scrolling_reasons_ = 0;
layout_object_counter_.Reset();
ClearFragmentAnchor();
viewport_constrained_objects_.reset();
layout_subtree_root_list_.Clear();
orthogonal_writing_mode_root_list_.Clear();
}
template <typename Function>
void LocalFrameView::ForAllChildViewsAndPlugins(const Function& function) {
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (child->View())
function(*child->View());
}
for (const auto& plugin : plugins_) {
function(*plugin);
}
}
template <typename Function>
void LocalFrameView::ForAllChildLocalFrameViews(const Function& function) {
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
if (LocalFrameView* child_view = ToLocalFrame(child)->View())
function(*child_view);
}
}
// Call function for each non-throttled frame view in pre tree order.
// Note it needs a null check of the frame's layoutView to access it in case of
// detached frames.
template <typename Function>
void LocalFrameView::ForAllNonThrottledLocalFrameViews(
const Function& function) {
if (ShouldThrottleRendering())
return;
function(*this);
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
if (LocalFrameView* child_view = ToLocalFrame(child)->View())
child_view->ForAllNonThrottledLocalFrameViews(function);
}
}
void LocalFrameView::Init() {
Reset();
size_ = LayoutSize();
// Propagate the marginwidth/height and scrolling modes to the view.
if (frame_->Owner() &&
frame_->Owner()->ScrollingMode() == kScrollbarAlwaysOff)
SetCanHaveScrollbars(false);
}
void LocalFrameView::SetupRenderThrottling() {
if (visibility_observer_)
return;
// We observe the frame owner element instead of the document element, because
// if the document has no content we can falsely think the frame is invisible.
// Note that this means we cannot throttle top-level frames or (currently)
// frames whose owner element is remote.
Element* target_element = GetFrame().DeprecatedLocalOwner();
if (!target_element)
return;
visibility_observer_ = new ElementVisibilityObserver(
target_element, WTF::Bind(
[](LocalFrameView* frame_view, bool is_visible) {
if (!frame_view)
return;
frame_view->UpdateRenderThrottlingStatus(
!is_visible, frame_view->subtree_throttled_);
},
WrapWeakPersistent(this)));
visibility_observer_->Start();
}
void LocalFrameView::Dispose() {
CHECK(!IsInPerformLayout());
if (ScrollAnimatorBase* scroll_animator = ExistingScrollAnimator())
scroll_animator->CancelAnimation();
CancelProgrammaticScrollAnimation();
DetachScrollbars();
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->WillDestroyScrollableArea(this);
Page* page = frame_->GetPage();
// TODO(dcheng): It's wrong that the frame can be detached before the
// LocalFrameView. Figure out what's going on and fix LocalFrameView to be
// disposed with the correct timing.
if (page)
page->GlobalRootScrollerController().DidDisposeScrollableArea(*this);
// We need to clear the RootFrameViewport's animator since it gets called
// from non-GC'd objects and RootFrameViewport will still have a pointer to
// this class.
if (viewport_scrollable_area_)
viewport_scrollable_area_->ClearScrollableArea();
ClearScrollableArea();
// Destroy |m_autoSizeInfo| as early as possible, to avoid dereferencing
// partially destroyed |this| via |m_autoSizeInfo->m_frameView|.
auto_size_info_.Clear();
post_layout_tasks_timer_.Stop();
did_scroll_timer_.Stop();
// FIXME: Do we need to do something here for OOPI?
HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
// TODO(dcheng): It seems buggy that we can have an owner element that points
// to another EmbeddedContentView. This can happen when a plugin element loads
// a frame (EmbeddedContentView A of type LocalFrameView) and then loads a
// plugin (EmbeddedContentView B of type WebPluginContainerImpl). In this
// case, the frame's view is A and the frame element's
// OwnedEmbeddedContentView is B. See https://crbug.com/673170 for an example.
if (owner_element && owner_element->OwnedEmbeddedContentView() == this)
owner_element->SetEmbeddedContentView(nullptr);
ClearPrintContext();
#if DCHECK_IS_ON()
has_been_disposed_ = true;
#endif
}
void LocalFrameView::DetachScrollbars() {
// Previously, we detached custom scrollbars as early as possible to prevent
// Document::detachLayoutTree() from messing with the view such that its
// scroll bars won't be torn down. However, scripting in
// Document::detachLayoutTree() is forbidden
// now, so it's not clear if these edge cases can still happen.
// However, for Oilpan, we still need to remove the native scrollbars before
// we lose the connection to the ChromeClient, so we just unconditionally
// detach any scrollbars now.
scrollbar_manager_.Dispose();
if (scroll_corner_) {
scroll_corner_->Destroy();
scroll_corner_ = nullptr;
}
}
void LocalFrameView::ScrollbarManager::SetHasHorizontalScrollbar(
bool has_scrollbar) {
if (has_scrollbar == HasHorizontalScrollbar())
return;
if (has_scrollbar) {
h_bar_ = CreateScrollbar(kHorizontalScrollbar);
h_bar_is_attached_ = 1;
scrollable_area_->DidAddScrollbar(*h_bar_, kHorizontalScrollbar);
h_bar_->StyleChanged();
} else {
h_bar_is_attached_ = 0;
DestroyScrollbar(kHorizontalScrollbar);
}
scrollable_area_->SetScrollCornerNeedsPaintInvalidation();
}
void LocalFrameView::ScrollbarManager::SetHasVerticalScrollbar(
bool has_scrollbar) {
if (has_scrollbar == HasVerticalScrollbar())
return;
if (has_scrollbar) {
v_bar_ = CreateScrollbar(kVerticalScrollbar);
v_bar_is_attached_ = 1;
scrollable_area_->DidAddScrollbar(*v_bar_, kVerticalScrollbar);
v_bar_->StyleChanged();
} else {
v_bar_is_attached_ = 0;
DestroyScrollbar(kVerticalScrollbar);
}
scrollable_area_->SetScrollCornerNeedsPaintInvalidation();
}
Scrollbar* LocalFrameView::ScrollbarManager::CreateScrollbar(
ScrollbarOrientation orientation) {
Element* custom_scrollbar_element = nullptr;
LayoutBox* box = scrollable_area_->GetLayoutBox();
if (box->GetDocument().View()->ShouldUseCustomScrollbars(
custom_scrollbar_element)) {
return LayoutScrollbar::CreateCustomScrollbar(
scrollable_area_.Get(), orientation, custom_scrollbar_element);
}
// Nobody set a custom style, so we just use a native scrollbar.
return Scrollbar::Create(scrollable_area_.Get(), orientation,
kRegularScrollbar,
&box->GetFrame()->GetPage()->GetChromeClient());
}
void LocalFrameView::ScrollbarManager::DestroyScrollbar(
ScrollbarOrientation orientation) {
Member<Scrollbar>& scrollbar =
orientation == kHorizontalScrollbar ? h_bar_ : v_bar_;
DCHECK(orientation == kHorizontalScrollbar ? !h_bar_is_attached_
: !v_bar_is_attached_);
if (!scrollbar)
return;
scrollable_area_->WillRemoveScrollbar(*scrollbar, orientation);
scrollbar->DisconnectFromScrollableArea();
scrollbar = nullptr;
}
void LocalFrameView::RecalculateCustomScrollbarStyle() {
bool did_style_change = false;
if (HorizontalScrollbar() && HorizontalScrollbar()->IsCustomScrollbar()) {
HorizontalScrollbar()->StyleChanged();
did_style_change = true;
}
if (VerticalScrollbar() && VerticalScrollbar()->IsCustomScrollbar()) {
VerticalScrollbar()->StyleChanged();
did_style_change = true;
}
if (did_style_change) {
UpdateScrollbarGeometry();
UpdateScrollCorner();
PositionScrollbarLayers();
}
}
void LocalFrameView::InvalidateAllCustomScrollbarsOnActiveChanged() {
bool uses_window_inactive_selector =
frame_->GetDocument()->GetStyleEngine().UsesWindowInactiveSelector();
ForAllChildLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.InvalidateAllCustomScrollbarsOnActiveChanged();
});
for (const auto& scrollbar : scrollbars_) {
if (uses_window_inactive_selector && scrollbar->IsCustomScrollbar())
scrollbar->StyleChanged();
}
if (uses_window_inactive_selector)
RecalculateCustomScrollbarStyle();
}
bool LocalFrameView::DidFirstLayout() const {
return !first_layout_;
}
void LocalFrameView::InvalidateRect(const IntRect& rect) {
LayoutEmbeddedContentItem layout_item = frame_->OwnerLayoutItem();
if (layout_item.IsNull())
return;
IntRect paint_invalidation_rect = rect;
paint_invalidation_rect.Move(
(layout_item.BorderLeft() + layout_item.PaddingLeft()).ToInt(),
(layout_item.BorderTop() + layout_item.PaddingTop()).ToInt());
// FIXME: We should not allow paint invalidation out of paint invalidation
// state. crbug.com/457415
DisablePaintInvalidationStateAsserts paint_invalidation_assert_disabler;
layout_item.InvalidatePaintRectangle(LayoutRect(paint_invalidation_rect));
}
void LocalFrameView::SetFrameRect(const IntRect& frame_rect) {
if (frame_rect == frame_rect_)
return;
const bool width_changed = frame_rect_.Width() != frame_rect.Width();
const bool height_changed = frame_rect_.Height() != frame_rect.Height();
frame_rect_ = frame_rect;
needs_scrollbars_update_ |= width_changed || height_changed;
FrameRectsChanged();
UpdateParentScrollableAreaSet();
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// The overflow clip property depends on the frame size and the pre
// translation property depends on the frame location.
SetNeedsPaintPropertyUpdate();
}
if (auto layout_view_item = this->GetLayoutViewItem())
layout_view_item.SetMayNeedPaintInvalidation();
if (width_changed || height_changed) {
ViewportSizeChanged(width_changed, height_changed);
if (frame_->IsMainFrame())
frame_->GetPage()->GetVisualViewport().MainFrameDidChangeSize();
GetFrame().Loader().RestoreScrollPositionAndViewState();
}
}
Page* LocalFrameView::GetPage() const {
return GetFrame().GetPage();
}
LayoutView* LocalFrameView::GetLayoutView() const {
return GetFrame().ContentLayoutObject();
}
LayoutViewItem LocalFrameView::GetLayoutViewItem() const {
return LayoutViewItem(GetFrame().ContentLayoutObject());
}
ScrollingCoordinator* LocalFrameView::GetScrollingCoordinator() const {
Page* p = GetPage();
return p ? p->GetScrollingCoordinator() : 0;
}
CompositorAnimationHost* LocalFrameView::GetCompositorAnimationHost() const {
// When m_animationHost is not nullptr, this is the LocalFrameView for an
// OOPIF.
if (animation_host_)
return animation_host_.get();
if (&frame_->LocalFrameRoot() != frame_)
return frame_->LocalFrameRoot().View()->GetCompositorAnimationHost();
if (!frame_->IsMainFrame())
return nullptr;
ScrollingCoordinator* c = GetScrollingCoordinator();
return c ? c->GetCompositorAnimationHost() : nullptr;
}
CompositorAnimationTimeline* LocalFrameView::GetCompositorAnimationTimeline()
const {
if (animation_timeline_)
return animation_timeline_.get();
if (&frame_->LocalFrameRoot() != frame_)
return frame_->LocalFrameRoot().View()->GetCompositorAnimationTimeline();
if (!frame_->IsMainFrame())
return nullptr;
ScrollingCoordinator* c = GetScrollingCoordinator();
return c ? c->GetCompositorAnimationTimeline() : nullptr;
}
LayoutBox* LocalFrameView::GetLayoutBox() const {
return GetLayoutView();
}
FloatQuad LocalFrameView::LocalToVisibleContentQuad(
const FloatQuad& quad,
const LayoutObject* local_object,
MapCoordinatesFlags flags) const {
LayoutBox* box = GetLayoutBox();
if (!box)
return quad;
DCHECK(local_object);
FloatQuad result = local_object->LocalToAncestorQuad(quad, box, flags);
result.Move(-GetScrollOffset());
return result;
}
RefPtr<WebTaskRunner> LocalFrameView::GetTimerTaskRunner() const {
return TaskRunnerHelper::Get(TaskType::kUnspecedTimer, frame_.Get());
}
void LocalFrameView::SetCanHaveScrollbars(bool can_have_scrollbars) {
can_have_scrollbars_ = can_have_scrollbars;
ScrollbarMode new_vertical_mode = vertical_scrollbar_mode_;
if (can_have_scrollbars && vertical_scrollbar_mode_ == kScrollbarAlwaysOff)
new_vertical_mode = kScrollbarAuto;
else if (!can_have_scrollbars)
new_vertical_mode = kScrollbarAlwaysOff;
ScrollbarMode new_horizontal_mode = horizontal_scrollbar_mode_;
if (can_have_scrollbars && horizontal_scrollbar_mode_ == kScrollbarAlwaysOff)
new_horizontal_mode = kScrollbarAuto;
else if (!can_have_scrollbars)
new_horizontal_mode = kScrollbarAlwaysOff;
SetScrollbarModes(new_horizontal_mode, new_vertical_mode);
}
bool LocalFrameView::ShouldUseCustomScrollbars(
Element*& custom_scrollbar_element) const {
custom_scrollbar_element = nullptr;
if (Settings* settings = frame_->GetSettings()) {
if (!settings->GetAllowCustomScrollbarInMainFrame() &&
frame_->IsMainFrame())
return false;
}
Document* doc = frame_->GetDocument();
// Try the <body> element first as a scrollbar source.
Element* body = doc ? doc->body() : 0;
if (body && body->GetLayoutObject() &&
body->GetLayoutObject()->Style()->HasPseudoStyle(kPseudoIdScrollbar)) {
custom_scrollbar_element = body;
return true;
}
// If the <body> didn't have a custom style, then the root element might.
Element* doc_element = doc ? doc->documentElement() : 0;
if (doc_element && doc_element->GetLayoutObject() &&
doc_element->GetLayoutObject()->Style()->HasPseudoStyle(
kPseudoIdScrollbar)) {
custom_scrollbar_element = doc_element;
return true;
}
return false;
}
Scrollbar* LocalFrameView::CreateScrollbar(ScrollbarOrientation orientation) {
return scrollbar_manager_.CreateScrollbar(orientation);
}
void LocalFrameView::SetContentsSize(const IntSize& size) {
if (size == ContentsSize())
return;
contents_size_ = size;
needs_scrollbars_update_ = true;
ScrollableArea::ContentsResized();
Page* page = GetFrame().GetPage();
if (!page)
return;
page->GetChromeClient().ContentsSizeChanged(frame_.Get(), size);
// Ensure the scrollToFragmentAnchor is called before
// restoreScrollPositionAndViewState when reload
ScrollToFragmentAnchor();
GetFrame().Loader().RestoreScrollPositionAndViewState();
}
void LocalFrameView::AdjustViewSize() {
if (suppress_adjust_view_size_)
return;
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull())
return;
DCHECK_EQ(frame_->View(), this);
const IntRect rect = layout_view_item.DocumentRect();
const IntSize& size = rect.Size();
const IntPoint origin(-rect.X(), -rect.Y());
if (ScrollOrigin() != origin)
SetScrollOrigin(origin);
SetContentsSize(size);
}
void LocalFrameView::AdjustViewSizeAndLayout() {
AdjustViewSize();
if (NeedsLayout()) {
AutoReset<bool> suppress_adjust_view_size(&suppress_adjust_view_size_,
true);
UpdateLayout();
}
}
void LocalFrameView::UpdateAcceleratedCompositingSettings() {
if (LayoutViewItem layout_view_item = this->GetLayoutViewItem())
layout_view_item.Compositor()->UpdateAcceleratedCompositingSettings();
}
void LocalFrameView::RecalcOverflowAfterStyleChange() {
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
CHECK(!layout_view_item.IsNull());
if (!layout_view_item.NeedsOverflowRecalcAfterStyleChange())
return;
layout_view_item.RecalcOverflowAfterStyleChange();
// Changing overflow should notify scrolling coordinator to ensures that it
// updates non-fast scroll rects even if there is no layout.
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->NotifyOverflowUpdated();
IntRect document_rect = layout_view_item.DocumentRect();
if (ScrollOrigin() == -document_rect.Location() &&
ContentsSize() == document_rect.Size())
return;
if (NeedsLayout())
return;
// If the visualViewport supplies scrollbars, we won't get a paint
// invalidation from computeScrollbarExistence so we need to force one.
if (VisualViewportSuppliesScrollbars())
layout_view_item.SetMayNeedPaintInvalidation();
// TODO(pdr): This should be refactored to just block scrollbar updates as
// we are not in a scrollbar update here and m_inUpdateScrollbars has other
// side effects. This scope is only for preventing a synchronous layout from
// scroll origin changes which would not be allowed during style recalc.
InUpdateScrollbarsScope in_update_scrollbars_scope(this);
bool should_have_horizontal_scrollbar = false;
bool should_have_vertical_scrollbar = false;
ComputeScrollbarExistence(should_have_horizontal_scrollbar,
should_have_vertical_scrollbar,
document_rect.Size());
bool has_horizontal_scrollbar = HorizontalScrollbar();
bool has_vertical_scrollbar = VerticalScrollbar();
if (has_horizontal_scrollbar != should_have_horizontal_scrollbar ||
has_vertical_scrollbar != should_have_vertical_scrollbar) {
SetNeedsLayout();
return;
}
AdjustViewSize();
UpdateScrollbarGeometry();
SetNeedsPaintPropertyUpdate();
if (ScrollOriginChanged())
SetNeedsLayout();
}
bool LocalFrameView::UsesCompositedScrolling() const {
LayoutViewItem layout_view = this->GetLayoutViewItem();
if (layout_view.IsNull())
return false;
if (frame_->GetSettings() &&
frame_->GetSettings()->GetPreferCompositingToLCDTextEnabled())
return layout_view.Compositor()->InCompositingMode();
return false;
}
bool LocalFrameView::ShouldScrollOnMainThread() const {
if (GetMainThreadScrollingReasons())
return true;
return ScrollableArea::ShouldScrollOnMainThread();
}
GraphicsLayer* LocalFrameView::LayerForScrolling() const {
LayoutViewItem layout_view = this->GetLayoutViewItem();
if (layout_view.IsNull())
return nullptr;
return layout_view.Compositor()->FrameScrollLayer();
}
GraphicsLayer* LocalFrameView::LayerForHorizontalScrollbar() const {
LayoutViewItem layout_view = this->GetLayoutViewItem();
if (layout_view.IsNull())
return nullptr;
return layout_view.Compositor()->LayerForHorizontalScrollbar();
}
GraphicsLayer* LocalFrameView::LayerForVerticalScrollbar() const {
LayoutViewItem layout_view = this->GetLayoutViewItem();
if (layout_view.IsNull())
return nullptr;
return layout_view.Compositor()->LayerForVerticalScrollbar();
}
GraphicsLayer* LocalFrameView::LayerForScrollCorner() const {
LayoutViewItem layout_view = this->GetLayoutViewItem();
if (layout_view.IsNull())
return nullptr;
return layout_view.Compositor()->LayerForScrollCorner();
}
bool LocalFrameView::IsEnclosedInCompositingLayer() const {
// FIXME: It's a bug that compositing state isn't always up to date when this
// is called. crbug.com/366314
DisableCompositingQueryAsserts disabler;
LayoutItem frame_owner_layout_item = frame_->OwnerLayoutItem();
return !frame_owner_layout_item.IsNull() &&
frame_owner_layout_item.EnclosingLayer()
->EnclosingLayerForPaintInvalidationCrossingFrameBoundaries();
}
void LocalFrameView::CountObjectsNeedingLayout(unsigned& needs_layout_objects,
unsigned& total_objects,
bool& is_subtree) {
needs_layout_objects = 0;
total_objects = 0;
is_subtree = IsSubtreeLayout();
if (is_subtree) {
layout_subtree_root_list_.CountObjectsNeedingLayout(needs_layout_objects,
total_objects);
} else {
LayoutSubtreeRootList::CountObjectsNeedingLayoutInRoot(
GetLayoutView(), needs_layout_objects, total_objects);
}
}
inline void LocalFrameView::ForceLayoutParentViewIfNeeded() {
LayoutEmbeddedContentItem owner_layout_item = frame_->OwnerLayoutItem();
if (owner_layout_item.IsNull() || !owner_layout_item.GetFrame())
return;
LayoutReplaced* content_box = EmbeddedReplacedContent();
if (!content_box)
return;
LayoutSVGRoot* svg_root = ToLayoutSVGRoot(content_box);
if (svg_root->EverHadLayout() && !svg_root->NeedsLayout())
return;
// If the embedded SVG document appears the first time, the ownerLayoutObject
// has already finished layout without knowing about the existence of the
// embedded SVG document, because LayoutReplaced embeddedReplacedContent()
// returns 0, as long as the embedded document isn't loaded yet. Before
// bothering to lay out the SVG document, mark the ownerLayoutObject needing
// layout and ask its LocalFrameView for a layout. After that the
// LayoutEmbeddedObject (ownerLayoutObject) carries the correct size, which
// LayoutSVGRoot::computeReplacedLogicalWidth/Height rely on, when laying out
// for the first time, or when the LayoutSVGRoot size has changed dynamically
// (eg. via <script>).
LocalFrameView* frame_view = owner_layout_item.GetFrame()->View();
// Mark the owner layoutObject as needing layout.
owner_layout_item.SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::kUnknown);
// Synchronously enter layout, to layout the view containing the host
// object/embed/iframe.
DCHECK(frame_view);
frame_view->UpdateLayout();
}
void LocalFrameView::PerformPreLayoutTasks() {
TRACE_EVENT0("blink,benchmark", "LocalFrameView::performPreLayoutTasks");
Lifecycle().AdvanceTo(DocumentLifecycle::kInPreLayout);
// Don't schedule more layouts, we're in one.
AutoReset<bool> change_scheduling_enabled(&layout_scheduling_enabled_, false);
if (!nested_layout_count_ && !in_synchronous_post_layout_ &&
post_layout_tasks_timer_.IsActive()) {
// This is a new top-level layout. If there are any remaining tasks from the
// previous layout, finish them now.
in_synchronous_post_layout_ = true;
PerformPostLayoutTasks();
in_synchronous_post_layout_ = false;
}
bool was_resized = WasViewportResized();
Document* document = frame_->GetDocument();
if (was_resized)
document->SetResizedForViewportUnits();
// Viewport-dependent or device-dependent media queries may cause us to need
// completely different style information.
bool main_frame_rotation =
frame_->IsMainFrame() && frame_->GetSettings() &&
frame_->GetSettings()->GetMainFrameResizesAreOrientationChanges();
if ((was_resized &&
document->GetStyleEngine().MediaQueryAffectedByViewportChange()) ||
(was_resized && main_frame_rotation &&
document->GetStyleEngine().MediaQueryAffectedByDeviceChange())) {
document->MediaQueryAffectingValueChanged();
} else if (was_resized) {
document->EvaluateMediaQueryList();
}
document->UpdateStyleAndLayoutTree();
Lifecycle().AdvanceTo(DocumentLifecycle::kStyleClean);
if (was_resized)
document->ClearResizedForViewportUnits();
if (ShouldPerformScrollAnchoring())
scroll_anchor_.NotifyBeforeLayout();
}
bool LocalFrameView::ShouldPerformScrollAnchoring() const {
return RuntimeEnabledFeatures::ScrollAnchoringEnabled() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled() &&
scroll_anchor_.HasScroller() &&
GetLayoutBox()->Style()->OverflowAnchor() != EOverflowAnchor::kNone &&
!frame_->GetDocument()->FinishingOrIsPrinting();
}
static inline void LayoutFromRootObject(LayoutObject& root) {
LayoutState layout_state(root);
root.UpdateLayout();
}
void LocalFrameView::PrepareLayoutAnalyzer() {
bool is_tracing = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(
TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"), &is_tracing);
if (!is_tracing) {
analyzer_.reset();
return;
}
if (!analyzer_)
analyzer_ = WTF::MakeUnique<LayoutAnalyzer>();
analyzer_->Reset();
}
std::unique_ptr<TracedValue> LocalFrameView::AnalyzerCounters() {
if (!analyzer_)
return TracedValue::Create();
std::unique_ptr<TracedValue> value = analyzer_->ToTracedValue();
value->SetString("host",
GetLayoutViewItem().GetDocument().location()->host());
value->SetString(
"frame",
String::Format("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(frame_.Get())));
value->SetInteger("contentsHeightAfterLayout",
GetLayoutViewItem().DocumentRect().Height());
value->SetInteger("visibleHeight", VisibleHeight());
value->SetInteger(
"approximateBlankCharacterCount",
FontFaceSet::ApproximateBlankCharacterCount(*frame_->GetDocument()));
return value;
}
#define PERFORM_LAYOUT_TRACE_CATEGORIES \
"blink,benchmark,rail," TRACE_DISABLED_BY_DEFAULT("blink.debug.layout")
void LocalFrameView::PerformLayout(bool in_subtree_layout) {
DCHECK(in_subtree_layout || layout_subtree_root_list_.IsEmpty());
int contents_height_before_layout =
GetLayoutViewItem().DocumentRect().Height();
TRACE_EVENT_BEGIN1(
PERFORM_LAYOUT_TRACE_CATEGORIES, "LocalFrameView::performLayout",
"contentsHeightBeforeLayout", contents_height_before_layout);
PrepareLayoutAnalyzer();
ScriptForbiddenScope forbid_script;
if (in_subtree_layout && HasOrthogonalWritingModeRoots()) {
// If we're going to lay out from each subtree root, rather than once from
// LayoutView, we need to merge the depth-ordered orthogonal writing mode
// root list into the depth-ordered list of subtrees scheduled for
// layout. Otherwise, during layout of one such subtree, we'd risk skipping
// over a subtree of objects needing layout.
DCHECK(!layout_subtree_root_list_.IsEmpty());
ScheduleOrthogonalWritingModeRootsForLayout();
}
DCHECK(!IsInPerformLayout());
Lifecycle().AdvanceTo(DocumentLifecycle::kInPerformLayout);
// performLayout is the actual guts of layout().
// FIXME: The 300 other lines in layout() probably belong in other helper
// functions so that a single human could understand what layout() is actually
// doing.
// FIXME: ForceLayoutParentViewIfNeeded can cause this document's lifecycle
// to change, which should not happen.
ForceLayoutParentViewIfNeeded();
CHECK(IsInPerformLayout() ||
Lifecycle().GetState() >= DocumentLifecycle::kLayoutClean);
if (IsInPerformLayout()) {
if (in_subtree_layout) {
if (analyzer_) {
analyzer_->Increment(LayoutAnalyzer::kPerformLayoutRootLayoutObjects,
layout_subtree_root_list_.size());
}
for (auto& root : layout_subtree_root_list_.Ordered()) {
if (!root->NeedsLayout())
continue;
LayoutFromRootObject(*root);
// We need to ensure that we mark up all layoutObjects up to the
// LayoutView for paint invalidation. This simplifies our code as we
// just always do a full tree walk.
if (LayoutItem container = LayoutItem(root->Container()))
container.SetMayNeedPaintInvalidation();
}
layout_subtree_root_list_.Clear();
} else {
if (HasOrthogonalWritingModeRoots() &&
!RuntimeEnabledFeatures::LayoutNGEnabled())
LayoutOrthogonalWritingModeRoots();
GetLayoutView()->UpdateLayout();
}
} else {
DCHECK(!NeedsLayout());
}
frame_->GetDocument()->Fetcher()->UpdateAllImageResourcePriorities();
Lifecycle().AdvanceTo(DocumentLifecycle::kAfterPerformLayout);
TRACE_EVENT_END1(PERFORM_LAYOUT_TRACE_CATEGORIES,
"LocalFrameView::performLayout", "counters",
AnalyzerCounters());
FirstMeaningfulPaintDetector::From(*frame_->GetDocument())
.MarkNextPaintAsMeaningfulIfNeeded(
layout_object_counter_, contents_height_before_layout,
GetLayoutViewItem().DocumentRect().Height(), VisibleHeight());
}
void LocalFrameView::ScheduleOrPerformPostLayoutTasks() {
if (post_layout_tasks_timer_.IsActive())
return;
if (!in_synchronous_post_layout_) {
in_synchronous_post_layout_ = true;
// Calls resumeScheduledEvents()
PerformPostLayoutTasks();
in_synchronous_post_layout_ = false;
}
if (!post_layout_tasks_timer_.IsActive() &&
(NeedsLayout() || in_synchronous_post_layout_)) {
// If we need layout or are already in a synchronous call to
// postLayoutTasks(), defer LocalFrameView updates and event dispatch until
// after we return. postLayoutTasks() can make us need to update again, and
// we can get stuck in a nasty cycle unless we call it through the timer
// here.
post_layout_tasks_timer_.StartOneShot(0, BLINK_FROM_HERE);
if (NeedsLayout())
UpdateLayout();
}
}
void LocalFrameView::UpdateLayout() {
// We should never layout a Document which is not in a LocalFrame.
DCHECK(frame_);
DCHECK_EQ(frame_->View(), this);
DCHECK(frame_->GetPage());
ScriptForbiddenScope forbid_script;
if (IsInPerformLayout() || ShouldThrottleRendering() ||
!frame_->GetDocument()->IsActive())
return;
TRACE_EVENT0("blink,benchmark", "LocalFrameView::layout");
RuntimeCallTimerScope scope(
RuntimeCallStats::From(V8PerIsolateData::MainThreadIsolate()),
RuntimeCallStats::CounterId::kUpdateLayout);
if (auto_size_info_)
auto_size_info_->AutoSizeIfNeeded();
has_pending_layout_ = false;
DocumentLifecycle::Scope lifecycle_scope(Lifecycle(),
DocumentLifecycle::kLayoutClean);
Document* document = frame_->GetDocument();
TRACE_EVENT_BEGIN1("devtools.timeline", "Layout", "beginData",
InspectorLayoutEvent::BeginData(this));
probe::UpdateLayout probe(document);
PerformPreLayoutTasks();
// TODO(crbug.com/460956): The notion of a single root for layout is no longer
// applicable. Remove or update this code.
LayoutObject* root_for_this_layout = GetLayoutView();
FontCachePurgePreventer font_cache_purge_preventer;
{
AutoReset<bool> change_scheduling_enabled(&layout_scheduling_enabled_,
false);
nested_layout_count_++;
UpdateCounters();
// If the layout view was marked as needing layout after we added items in
// the subtree roots we need to clear the roots and do the layout from the
// layoutView.
if (GetLayoutViewItem().NeedsLayout())
ClearLayoutSubtreeRootsAndMarkContainingBlocks();
GetLayoutViewItem().ClearHitTestCache();
bool in_subtree_layout = IsSubtreeLayout();
// TODO(crbug.com/460956): The notion of a single root for layout is no
// longer applicable. Remove or update this code.
if (in_subtree_layout)
root_for_this_layout = layout_subtree_root_list_.RandomRoot();
if (!root_for_this_layout) {
// FIXME: Do we need to set m_size here?
NOTREACHED();
return;
}
if (!in_subtree_layout) {
ClearLayoutSubtreeRootsAndMarkContainingBlocks();
Node* body = document->body();
if (body && body->GetLayoutObject()) {
if (isHTMLFrameSetElement(*body)) {
body->GetLayoutObject()->SetChildNeedsLayout();
} else if (isHTMLBodyElement(*body)) {
if (!first_layout_ && size_.Height() != GetLayoutSize().Height() &&
body->GetLayoutObject()->EnclosingBox()->StretchesToViewport())
body->GetLayoutObject()->SetChildNeedsLayout();
}
}
ScrollbarMode h_mode;
ScrollbarMode v_mode;
GetLayoutView()->CalculateScrollbarModes(h_mode, v_mode);
// Now set our scrollbar state for the layout.
ScrollbarMode current_h_mode = HorizontalScrollbarMode();
ScrollbarMode current_v_mode = VerticalScrollbarMode();
if (first_layout_) {
SetScrollbarsSuppressed(true);
first_layout_ = false;
last_viewport_size_ = GetLayoutSize(kIncludeScrollbars);
last_zoom_factor_ = GetLayoutViewItem().Style()->Zoom();
// Set the initial vMode to AlwaysOn if we're auto.
if (v_mode == kScrollbarAuto) {
// This causes a vertical scrollbar to appear.
SetVerticalScrollbarMode(kScrollbarAlwaysOn);
}
// Set the initial hMode to AlwaysOff if we're auto.
if (h_mode == kScrollbarAuto) {
// This causes a horizontal scrollbar to disappear.
SetHorizontalScrollbarMode(kScrollbarAlwaysOff);
}
SetScrollbarModes(h_mode, v_mode);
SetScrollbarsSuppressed(false);
} else if (h_mode != current_h_mode || v_mode != current_v_mode) {
SetScrollbarModes(h_mode, v_mode);
}
UpdateScrollbarsIfNeeded();
LayoutSize old_size = size_;
size_ = LayoutSize(GetLayoutSize());
if (old_size != size_ && !first_layout_) {
LayoutBox* root_layout_object =
document->documentElement()
? document->documentElement()->GetLayoutBox()
: 0;
LayoutBox* body_layout_object = root_layout_object && document->body()
? document->body()->GetLayoutBox()
: 0;
if (body_layout_object && body_layout_object->StretchesToViewport())
body_layout_object->SetChildNeedsLayout();
else if (root_layout_object &&
root_layout_object->StretchesToViewport())
root_layout_object->SetChildNeedsLayout();
}
}
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"), "LayoutTree",
this, TracedLayoutObject::Create(*GetLayoutView(), false));
IntSize old_size(Size());
PerformLayout(in_subtree_layout);
UpdateScrollbars();
UpdateParentScrollableAreaSet();
IntSize new_size(Size());
if (old_size != new_size) {
needs_scrollbars_update_ = true;
SetNeedsLayout();
MarkViewportConstrainedObjectsForLayout(
old_size.Width() != new_size.Width(),
old_size.Height() != new_size.Height());
}
if (NeedsLayout()) {
AutoReset<bool> suppress(&suppress_adjust_view_size_, true);
UpdateLayout();
}
DCHECK(layout_subtree_root_list_.IsEmpty());
} // Reset m_layoutSchedulingEnabled to its previous value.
CheckDoesNotNeedLayout();
frame_timing_requests_dirty_ = true;
// FIXME: Could find the common ancestor layer of all dirty subtrees and mark
// from there. crbug.com/462719
GetLayoutViewItem().EnclosingLayer()->UpdateLayerPositionsAfterLayout();
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"), "LayoutTree", this,
TracedLayoutObject::Create(*GetLayoutView(), true));
GetLayoutViewItem().Compositor()->DidLayout();
layout_count_++;
if (AXObjectCache* cache = document->AxObjectCache()) {
const KURL& url = document->Url();
if (url.IsValid() && !url.IsAboutBlankURL())
cache->HandleLayoutComplete(document);
}
UpdateDocumentAnnotatedRegions();
CheckDoesNotNeedLayout();
ScheduleOrPerformPostLayoutTasks();
CheckDoesNotNeedLayout();
// FIXME: The notion of a single root for layout is no longer applicable.
// Remove or update this code. crbug.com/460596
TRACE_EVENT_END1("devtools.timeline", "Layout", "endData",
InspectorLayoutEvent::EndData(root_for_this_layout));
probe::didChangeViewport(frame_.Get());
nested_layout_count_--;
if (nested_layout_count_)
return;
#if DCHECK_IS_ON()
// Post-layout assert that nobody was re-marked as needing layout during
// layout.
GetLayoutView()->AssertSubtreeIsLaidOut();
#endif
GetFrame().GetDocument()->LayoutUpdated();
CheckDoesNotNeedLayout();
}
void LocalFrameView::DeprecatedInvalidateTree(
const PaintInvalidationState& paint_invalidation_state) {
DCHECK(!RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled());
if (ShouldThrottleRendering())
return;
Lifecycle().AdvanceTo(DocumentLifecycle::kInPaintInvalidation);
CHECK(!GetLayoutViewItem().IsNull());
LayoutViewItem root_for_paint_invalidation = GetLayoutViewItem();
DCHECK(!root_for_paint_invalidation.NeedsLayout());
TRACE_EVENT1("blink", "LocalFrameView::invalidateTree", "root",
root_for_paint_invalidation.DebugName().Ascii());
InvalidatePaint(paint_invalidation_state);
root_for_paint_invalidation.DeprecatedInvalidateTree(
paint_invalidation_state);
#if DCHECK_IS_ON()
GetLayoutView()->AssertSubtreeClearedPaintInvalidationFlags();
#endif
Lifecycle().AdvanceTo(DocumentLifecycle::kPaintInvalidationClean);
}
void LocalFrameView::InvalidatePaint(
const PaintInvalidationState& paint_invalidation_state) {
CHECK(!GetLayoutViewItem().IsNull());
if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled())
InvalidatePaintOfScrollControlsIfNeeded(paint_invalidation_state);
}
void LocalFrameView::SetNeedsPaintPropertyUpdate() {
needs_paint_property_update_ = true;
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
if (auto* layout_view = this->GetLayoutView()) {
layout_view->SetNeedsPaintPropertyUpdate();
return;
}
}
if (LayoutObject* owner = GetFrame().OwnerLayoutObject())
owner->SetNeedsPaintPropertyUpdate();
}
void LocalFrameView::SetSubtreeNeedsPaintPropertyUpdate() {
SetNeedsPaintPropertyUpdate();
GetLayoutView()->SetSubtreeNeedsPaintPropertyUpdate();
}
IntRect LocalFrameView::ComputeVisibleArea() {
// Return our clipping bounds in the root frame.
IntRect us(FrameRect());
if (LocalFrameView* parent = ParentFrameView()) {
us = parent->ContentsToRootFrame(us);
IntRect parent_rect = parent->ComputeVisibleArea();
if (parent_rect.IsEmpty())
return IntRect();
us.Intersect(parent_rect);
}
return us;
}
FloatSize LocalFrameView::ViewportSizeForViewportUnits() const {
float zoom = 1;
if (!frame_->GetDocument() || !frame_->GetDocument()->Printing())
zoom = GetFrame().PageZoomFactor();
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull())
return FloatSize();
FloatSize layout_size;
layout_size.SetWidth(layout_view_item.ViewWidth(kIncludeScrollbars) / zoom);
layout_size.SetHeight(layout_view_item.ViewHeight(kIncludeScrollbars) / zoom);
BrowserControls& browser_controls = frame_->GetPage()->GetBrowserControls();
if (browser_controls.PermittedState() != kWebBrowserControlsHidden) {
// We use the layoutSize rather than frameRect to calculate viewport units
// so that we get correct results on mobile where the page is laid out into
// a rect that may be larger than the viewport (e.g. the 980px fallback
// width for desktop pages). Since the layout height is statically set to
// be the viewport with browser controls showing, we add the browser
// controls height, compensating for page scale as well, since we want to
// use the viewport with browser controls hidden for vh (to match Safari).
int viewport_width = frame_->GetPage()->GetVisualViewport().Size().Width();
if (frame_->IsMainFrame() && layout_size.Width() && viewport_width) {
float page_scale_at_layout_width = viewport_width / layout_size.Width();
layout_size.Expand(
0, browser_controls.Height() / page_scale_at_layout_width);
}
}
return layout_size;
}
FloatSize LocalFrameView::ViewportSizeForMediaQueries() const {
FloatSize viewport_size(GetLayoutSize(kIncludeScrollbars));
if (!frame_->GetDocument() || !frame_->GetDocument()->Printing())
viewport_size.Scale(1 / GetFrame().PageZoomFactor());
return viewport_size;
}
DocumentLifecycle& LocalFrameView::Lifecycle() const {
DCHECK(frame_);
DCHECK(frame_->GetDocument());
return frame_->GetDocument()->Lifecycle();
}
LayoutReplaced* LocalFrameView::EmbeddedReplacedContent() const {
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull())
return nullptr;
LayoutObject* first_child = GetLayoutView()->FirstChild();
if (!first_child || !first_child->IsBox())
return nullptr;
// Currently only embedded SVG documents participate in the size-negotiation
// logic.
if (first_child->IsSVGRoot())
return ToLayoutSVGRoot(first_child);
return nullptr;
}
void LocalFrameView::AddPart(LayoutEmbeddedContent* object) {
parts_.insert(object);
}
void LocalFrameView::RemovePart(LayoutEmbeddedContent* object) {
parts_.erase(object);
}
void LocalFrameView::UpdateGeometries() {
Vector<RefPtr<LayoutEmbeddedContent>> parts;
CopyToVector(parts_, parts);
for (auto part : parts) {
// Script or plugins could detach the frame so abort processing if that
// happens.
if (GetLayoutViewItem().IsNull())
break;
if (part->GetEmbeddedContentView()) {
if (LocalFrameView* frame_view = part->ChildFrameView()) {
bool did_need_layout = frame_view->NeedsLayout();
part->UpdateGeometry();
if (!did_need_layout && !frame_view->ShouldThrottleRendering())
frame_view->CheckDoesNotNeedLayout();
} else {
part->UpdateGeometry();
}
}
}
}
void LocalFrameView::AddPartToUpdate(LayoutEmbeddedObject& object) {
DCHECK(IsInPerformLayout());
// Tell the DOM element that it needs a Plugin update.
Node* node = object.GetNode();
DCHECK(node);
if (isHTMLObjectElement(*node) || isHTMLEmbedElement(*node))
ToHTMLPlugInElement(node)->SetNeedsPluginUpdate(true);
part_update_set_.insert(&object);
}
void LocalFrameView::SetDisplayMode(WebDisplayMode mode) {
if (mode == display_mode_)
return;
display_mode_ = mode;
if (frame_->GetDocument())
frame_->GetDocument()->MediaQueryAffectingValueChanged();
}
void LocalFrameView::SetDisplayShape(DisplayShape display_shape) {
if (display_shape == display_shape_)
return;
display_shape_ = display_shape;
if (frame_->GetDocument())
frame_->GetDocument()->MediaQueryAffectingValueChanged();
}
void LocalFrameView::SetMediaType(const AtomicString& media_type) {
DCHECK(frame_->GetDocument());
media_type_ = media_type;
frame_->GetDocument()->MediaQueryAffectingValueChanged();
}
AtomicString LocalFrameView::MediaType() const {
// See if we have an override type.
if (frame_->GetSettings() &&
!frame_->GetSettings()->GetMediaTypeOverride().IsEmpty())
return AtomicString(frame_->GetSettings()->GetMediaTypeOverride());
return media_type_;
}
void LocalFrameView::AdjustMediaTypeForPrinting(bool printing) {
if (printing) {
if (media_type_when_not_printing_.IsNull())
media_type_when_not_printing_ = MediaType();
SetMediaType(MediaTypeNames::print);
} else {
if (!media_type_when_not_printing_.IsNull())
SetMediaType(media_type_when_not_printing_);
media_type_when_not_printing_ = g_null_atom;
}
frame_->GetDocument()->SetNeedsStyleRecalc(
kSubtreeStyleChange, StyleChangeReasonForTracing::Create(
StyleChangeReason::kStyleSheetChange));
}
bool LocalFrameView::ContentsInCompositedLayer() const {
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
return !layout_view_item.IsNull() &&
layout_view_item.GetCompositingState() == kPaintsIntoOwnBacking;
}
void LocalFrameView::AddBackgroundAttachmentFixedObject(LayoutObject* object) {
DCHECK(!background_attachment_fixed_objects_.Contains(object));
background_attachment_fixed_objects_.insert(object);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator()) {
scrolling_coordinator
->FrameViewHasBackgroundAttachmentFixedObjectsDidChange(this);
}
// Ensure main thread scrolling reasons are recomputed.
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled()) {
SetNeedsPaintPropertyUpdate();
// The object's scroll properties are not affected by its own background.
object->SetAncestorsNeedPaintPropertyUpdateForMainThreadScrolling();
}
}
void LocalFrameView::RemoveBackgroundAttachmentFixedObject(
LayoutObject* object) {
DCHECK(background_attachment_fixed_objects_.Contains(object));
background_attachment_fixed_objects_.erase(object);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator()) {
scrolling_coordinator
->FrameViewHasBackgroundAttachmentFixedObjectsDidChange(this);
}
// Ensure main thread scrolling reasons are recomputed.
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled()) {
SetNeedsPaintPropertyUpdate();
// The object's scroll properties are not affected by its own background.
object->SetAncestorsNeedPaintPropertyUpdateForMainThreadScrolling();
}
}
void LocalFrameView::AddViewportConstrainedObject(LayoutObject& object) {
if (!viewport_constrained_objects_) {
viewport_constrained_objects_ =
WTF::WrapUnique(new ViewportConstrainedObjectSet);
}
if (!viewport_constrained_objects_->Contains(&object)) {
viewport_constrained_objects_->insert(&object);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->FrameViewFixedObjectsDidChange(this);
}
}
void LocalFrameView::RemoveViewportConstrainedObject(LayoutObject& object) {
if (viewport_constrained_objects_ &&
viewport_constrained_objects_->Contains(&object)) {
viewport_constrained_objects_->erase(&object);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->FrameViewFixedObjectsDidChange(this);
}
}
void LocalFrameView::ViewportSizeChanged(bool width_changed,
bool height_changed) {
DCHECK(width_changed || height_changed);
DCHECK(frame_->GetPage());
bool root_layer_scrolling_enabled =
RuntimeEnabledFeatures::RootLayerScrollingEnabled();
if (LayoutView* layout_view = this->GetLayoutView()) {
// If this is the main frame, we might have got here by hiding/showing the
// top controls. In that case, layout won't be triggered, so we need to
// clamp the scroll offset here.
if (GetFrame().IsMainFrame()) {
if (root_layer_scrolling_enabled) {
layout_view->GetScrollableArea()
->ClampScrollOffsetAfterOverflowChange();
} else {
AdjustScrollOffsetFromUpdateScrollbars();
}
}
if (layout_view->UsesCompositing()) {
if (root_layer_scrolling_enabled) {
layout_view->Layer()->SetNeedsCompositingInputsUpdate();
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled())
SetNeedsPaintPropertyUpdate();
} else {
layout_view->Compositor()->FrameViewDidChangeSize();
}
}
}
if (GetFrame().GetDocument())
GetFrame().GetDocument()->GetRootScrollerController().DidResizeFrameView();
ShowOverlayScrollbars();
if (GetLayoutView() && frame_->IsMainFrame() &&
frame_->GetPage()->GetBrowserControls().Height()) {
if (GetLayoutView()->Style()->HasFixedBackgroundImage()) {
// In the case where we don't change layout size from top control resizes,
// we wont perform a layout. If we have a fixed background image however,
// the background layer needs to get resized so we should request a layout
// explicitly.
PaintLayer* layer = GetLayoutView()->Layer();
if (GetLayoutView()->Compositor()->NeedsFixedRootBackgroundLayer(layer)) {
SetNeedsLayout();
} else {
// If root layer scrolls is on, we've already issued a full invalidation
// above.
GetLayoutView()->SetShouldDoFullPaintInvalidationOnResizeIfNeeded(
width_changed, height_changed);
}
} else if (height_changed) {
// If the document rect doesn't fill the full view height, hiding the
// URL bar will expose area outside the current LayoutView so we need to
// paint additional background. If RLS is on, we've already invalidated
// above.
LayoutViewItem lvi = GetLayoutViewItem();
DCHECK(!lvi.IsNull());
if (lvi.DocumentRect().Height() < lvi.ViewRect().Height()) {
lvi.SetShouldDoFullPaintInvalidation(
PaintInvalidationReason::kGeometry);
}
}
}
if (GetFrame().GetDocument() && !IsInPerformLayout())
MarkViewportConstrainedObjectsForLayout(width_changed, height_changed);
}
void LocalFrameView::MarkViewportConstrainedObjectsForLayout(
bool width_changed,
bool height_changed) {
if (!HasViewportConstrainedObjects() || !(width_changed || height_changed))
return;
for (const auto& viewport_constrained_object :
*viewport_constrained_objects_) {
LayoutObject* layout_object = viewport_constrained_object;
const ComputedStyle& style = layout_object->StyleRef();
if (width_changed) {
if (style.Width().IsFixed() &&
(style.Left().IsAuto() || style.Right().IsAuto())) {
layout_object->SetNeedsPositionedMovementLayout();
} else {
layout_object->SetNeedsLayoutAndFullPaintInvalidation(
LayoutInvalidationReason::kSizeChanged);
}
}
if (height_changed) {
if (style.Height().IsFixed() &&
(style.Top().IsAuto() || style.Bottom().IsAuto())) {
layout_object->SetNeedsPositionedMovementLayout();
} else {
layout_object->SetNeedsLayoutAndFullPaintInvalidation(
LayoutInvalidationReason::kSizeChanged);
}
}
}
}
IntPoint LocalFrameView::LastKnownMousePosition() const {
return frame_->GetEventHandler().LastKnownMousePosition();
}
bool LocalFrameView::ShouldSetCursor() const {
Page* page = GetFrame().GetPage();
return page && page->VisibilityState() != kPageVisibilityStateHidden &&
!frame_->GetEventHandler().IsMousePositionUnknown() &&
page->GetFocusController().IsActive();
}
void LocalFrameView::ScrollContentsIfNeededRecursive() {
ForAllNonThrottledLocalFrameViews(
[](LocalFrameView& frame_view) { frame_view.ScrollContentsIfNeeded(); });
}
void LocalFrameView::InvalidateBackgroundAttachmentFixedObjects() {
for (const auto& layout_object : background_attachment_fixed_objects_) {
layout_object->SetShouldDoFullPaintInvalidation(
PaintInvalidationReason::kBackground);
}
}
bool LocalFrameView::HasBackgroundAttachmentFixedDescendants(
const LayoutObject& object) const {
for (const auto* potential_descendant :
background_attachment_fixed_objects_) {
if (potential_descendant == &object)
continue;
if (potential_descendant->IsDescendantOf(&object))
return true;
}
return false;
}
bool LocalFrameView::InvalidateViewportConstrainedObjects() {
bool fast_path_allowed = true;
for (const auto& viewport_constrained_object :
*viewport_constrained_objects_) {
LayoutObject* layout_object = viewport_constrained_object;
LayoutItem layout_item = LayoutItem(layout_object);
DCHECK(layout_item.Style()->HasViewportConstrainedPosition());
DCHECK(layout_item.HasLayer());
PaintLayer* layer = LayoutBoxModel(layout_item).Layer();
if (layer->IsPaintInvalidationContainer())
continue;
if (layer->SubtreeIsInvisible())
continue;
// invalidate even if there is an ancestor with a filter that moves pixels.
layout_item
.SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
TRACE_EVENT_INSTANT1(
TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"),
"ScrollInvalidationTracking", TRACE_EVENT_SCOPE_THREAD, "data",
InspectorScrollInvalidationTrackingEvent::Data(*layout_object));
// If the fixed layer has a blur/drop-shadow filter applied on at least one
// of its parents, we cannot scroll using the fast path, otherwise the
// outsets of the filter will be moved around the page.
if (layer->HasAncestorWithFilterThatMovesPixels())
fast_path_allowed = false;
}
return fast_path_allowed;
}
bool LocalFrameView::ScrollContentsFastPath(const IntSize& scroll_delta) {
if (!ContentsInCompositedLayer())
return false;
InvalidateBackgroundAttachmentFixedObjects();
if (!viewport_constrained_objects_ ||
viewport_constrained_objects_->IsEmpty()) {
probe::didChangeViewport(frame_.Get());
return true;
}
if (!InvalidateViewportConstrainedObjects())
return false;
probe::didChangeViewport(frame_.Get());
return true;
}
void LocalFrameView::ScrollContentsSlowPath() {
TRACE_EVENT0("blink", "LocalFrameView::scrollContentsSlowPath");
// We need full invalidation during slow scrolling. For slimming paint, full
// invalidation of the LayoutView is not enough. We also need to invalidate
// all of the objects.
// FIXME: Find out what are enough to invalidate in slow path scrolling.
// crbug.com/451090#9.
DCHECK(!GetLayoutViewItem().IsNull());
if (ContentsInCompositedLayer()) {
GetLayoutViewItem()
.Layer()
->GetCompositedLayerMapping()
->SetContentsNeedDisplay();
} else {
GetLayoutViewItem()
.SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
}
if (ContentsInCompositedLayer()) {
IntRect update_rect = VisibleContentRect();
DCHECK(!GetLayoutViewItem().IsNull());
// FIXME: We should not allow paint invalidation out of paint invalidation
// state. crbug.com/457415
DisablePaintInvalidationStateAsserts disabler;
GetLayoutViewItem().InvalidatePaintRectangle(LayoutRect(update_rect));
}
LayoutEmbeddedContentItem frame_layout_item = frame_->OwnerLayoutItem();
if (!frame_layout_item.IsNull()) {
if (IsEnclosedInCompositingLayer()) {
LayoutRect rect(
frame_layout_item.BorderLeft() + frame_layout_item.PaddingLeft(),
frame_layout_item.BorderTop() + frame_layout_item.PaddingTop(),
LayoutUnit(VisibleWidth()), LayoutUnit(VisibleHeight()));
// FIXME: We should not allow paint invalidation out of paint invalidation
// state. crbug.com/457415
DisablePaintInvalidationStateAsserts disabler;
frame_layout_item.InvalidatePaintRectangle(rect);
return;
}
}
}
void LocalFrameView::RestoreScrollbar() {
SetScrollbarsSuppressed(false);
}
void LocalFrameView::ProcessUrlFragment(const KURL& url,
UrlFragmentBehavior behavior) {
// If our URL has no ref, then we have no place we need to jump to.
// OTOH If CSS target was set previously, we want to set it to 0, recalc
// and possibly paint invalidation because :target pseudo class may have been
// set (see bug 11321).
// Similarly for svg, if we had a previous svgView() then we need to reset
// the initial view if we don't have a fragment.
if (!url.HasFragmentIdentifier() && !frame_->GetDocument()->CssTarget() &&
!frame_->GetDocument()->IsSVGDocument())
return;
String fragment_identifier = url.FragmentIdentifier();
if (ProcessUrlFragmentHelper(fragment_identifier, behavior))
return;
// Try again after decoding the ref, based on the document's encoding.
if (frame_->GetDocument()->Encoding().IsValid()) {
ProcessUrlFragmentHelper(
DecodeURLEscapeSequences(fragment_identifier,
frame_->GetDocument()->Encoding()),
behavior);
}
}
bool LocalFrameView::ProcessUrlFragmentHelper(const String& name,
UrlFragmentBehavior behavior) {
DCHECK(frame_->GetDocument());
if (behavior == kUrlFragmentScroll &&
!frame_->GetDocument()->IsRenderingReady()) {
frame_->GetDocument()->SetGotoAnchorNeededAfterStylesheetsLoad(true);
return false;
}
frame_->GetDocument()->SetGotoAnchorNeededAfterStylesheetsLoad(false);
Element* anchor_node = frame_->GetDocument()->FindAnchor(name);
// Setting to null will clear the current target.
frame_->GetDocument()->SetCSSTarget(anchor_node);
if (frame_->GetDocument()->IsSVGDocument()) {
if (SVGSVGElement* svg =
SVGDocumentExtensions::rootElement(*frame_->GetDocument())) {
svg->SetupInitialView(name, anchor_node);
if (!anchor_node)
return true;
}
}
// Implement the rule that "" and "top" both mean top of page as in other
// browsers.
if (!anchor_node &&
!(name.IsEmpty() || DeprecatedEqualIgnoringCase(name, "top")))
return false;
if (behavior == kUrlFragmentScroll) {
SetFragmentAnchor(anchor_node ? static_cast<Node*>(anchor_node)
: frame_->GetDocument());
}
// If the anchor accepts keyboard focus and fragment scrolling is allowed,
// move focus there to aid users relying on keyboard navigation.
// If anchorNode is not focusable or fragment scrolling is not allowed,
// clear focus, which matches the behavior of other browsers.
if (anchor_node) {
frame_->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
if (behavior == kUrlFragmentScroll && anchor_node->IsFocusable()) {
anchor_node->focus();
} else {
if (behavior == kUrlFragmentScroll) {
frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
anchor_node);
}
frame_->GetDocument()->ClearFocusedElement();
}
}
return true;
}
void LocalFrameView::SetFragmentAnchor(Node* anchor_node) {
DCHECK(anchor_node);
fragment_anchor_ = anchor_node;
// We need to update the layout tree before scrolling.
frame_->GetDocument()->UpdateStyleAndLayoutTree();
// If layout is needed, we will scroll in performPostLayoutTasks. Otherwise,
// scroll immediately.
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (!layout_view_item.IsNull() && layout_view_item.NeedsLayout())
UpdateLayout();
else
ScrollToFragmentAnchor();
}
void LocalFrameView::ClearFragmentAnchor() {
fragment_anchor_ = nullptr;
}
void LocalFrameView::DidUpdateElasticOverscroll() {
Page* page = GetFrame().GetPage();
if (!page)
return;
FloatSize elastic_overscroll = page->GetChromeClient().ElasticOverscroll();
if (HorizontalScrollbar()) {
float delta =
elastic_overscroll.Width() - HorizontalScrollbar()->ElasticOverscroll();
if (delta != 0) {
HorizontalScrollbar()->SetElasticOverscroll(elastic_overscroll.Width());
GetScrollAnimator().NotifyContentAreaScrolled(FloatSize(delta, 0),
kCompositorScroll);
SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
}
}
if (VerticalScrollbar()) {
float delta =
elastic_overscroll.Height() - VerticalScrollbar()->ElasticOverscroll();
if (delta != 0) {
VerticalScrollbar()->SetElasticOverscroll(elastic_overscroll.Height());
GetScrollAnimator().NotifyContentAreaScrolled(FloatSize(0, delta),
kCompositorScroll);
SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
}
}
}
IntSize LocalFrameView::GetLayoutSize(
IncludeScrollbarsInRect scrollbar_inclusion) const {
return scrollbar_inclusion == kExcludeScrollbars
? ExcludeScrollbars(layout_size_)
: layout_size_;
}
void LocalFrameView::SetLayoutSize(const IntSize& size) {
DCHECK(!LayoutSizeFixedToFrameSize());
SetLayoutSizeInternal(size);
}
void LocalFrameView::SetLayoutSizeFixedToFrameSize(bool is_fixed) {
if (layout_size_fixed_to_frame_size_ == is_fixed)
return;
layout_size_fixed_to_frame_size_ = is_fixed;
if (is_fixed)
SetLayoutSizeInternal(Size());
}
void LocalFrameView::DidScrollTimerFired(TimerBase*) {
if (frame_->GetDocument() &&
!frame_->GetDocument()->GetLayoutViewItem().IsNull())
frame_->GetDocument()->Fetcher()->UpdateAllImageResourcePriorities();
}
void LocalFrameView::UpdateLayersAndCompositingAfterScrollIfNeeded() {
// Nothing to do after scrolling if there are no fixed position elements.
if (!HasViewportConstrainedObjects())
return;
// Update sticky position objects which are stuck to the viewport. In order to
// correctly compute the sticky position offsets the layers must be visited
// top-down, so start at the 'root' sticky elements and recurse downwards.
for (const auto& viewport_constrained_object :
*viewport_constrained_objects_) {
LayoutObject* layout_object = viewport_constrained_object;
if (layout_object->Style()->GetPosition() != EPosition::kSticky)
continue;
PaintLayer* layer = ToLayoutBoxModelObject(layout_object)->Layer();
// This method can be called during layout at which point the ancestor
// overflow layer may not be set yet. We can safely skip such cases as we
// will revisit this method during compositing inputs update.
if (!layer->AncestorOverflowLayer())
continue;
StickyConstraintsMap constraints_map = layer->AncestorOverflowLayer()
->GetScrollableArea()
->GetStickyConstraintsMap();
if (constraints_map.Contains(layer) &&
!constraints_map.at(layer).HasAncestorStickyElement()) {
// TODO(skobes): Resolve circular dependency between scroll offset and
// compositing state, and remove this disabler. https://crbug.com/420741
DisableCompositingQueryAsserts disabler;
layer->UpdateLayerPositionsAfterOverflowScroll();
layout_object->SetMayNeedPaintInvalidationSubtree();
}
}
// If there fixed position elements, scrolling may cause compositing layers to
// change. Update LocalFrameView and layer positions after scrolling, but
// only if we're not inside of layout.
if (!nested_layout_count_) {
UpdateGeometries();
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (!layout_view_item.IsNull())
layout_view_item.Layer()->SetNeedsCompositingInputsUpdate();
}
}
bool LocalFrameView::ComputeCompositedSelection(
LocalFrame& frame,
CompositedSelection& selection) {
if (!frame.View() || frame.View()->ShouldThrottleRendering())
return false;
const VisibleSelection& visible_selection =
frame.Selection().ComputeVisibleSelectionInDOMTree();
if (!frame.Selection().IsHandleVisible() || frame.Selection().IsHidden())
return false;
// Non-editable caret selections lack any kind of UI affordance, and
// needn't be tracked by the client.
if (visible_selection.IsCaret() && !visible_selection.IsContentEditable())
return false;
VisiblePosition visible_start(visible_selection.VisibleStart());
RenderedPosition rendered_start(visible_start);
rendered_start.PositionInGraphicsLayerBacking(selection.start, true);
if (!selection.start.layer)
return false;
VisiblePosition visible_end(visible_selection.VisibleEnd());
RenderedPosition rendered_end(visible_end);
rendered_end.PositionInGraphicsLayerBacking(selection.end, false);
if (!selection.end.layer)
return false;
selection.type = visible_selection.GetSelectionType();
selection.start.is_text_direction_rtl |=
PrimaryDirectionOf(*visible_selection.Start().AnchorNode()) ==
TextDirection::kRtl;
selection.end.is_text_direction_rtl |=
PrimaryDirectionOf(*visible_selection.End().AnchorNode()) ==
TextDirection::kRtl;
return true;
}
void LocalFrameView::UpdateCompositedSelectionIfNeeded() {
if (!RuntimeEnabledFeatures::CompositedSelectionUpdateEnabled())
return;
TRACE_EVENT0("blink", "LocalFrameView::updateCompositedSelectionIfNeeded");
Page* page = GetFrame().GetPage();
DCHECK(page);
CompositedSelection selection;
LocalFrame* focused_frame = page->GetFocusController().FocusedFrame();
LocalFrame* local_frame =
(focused_frame &&
(focused_frame->LocalFrameRoot() == frame_->LocalFrameRoot()))
? focused_frame
: nullptr;
if (local_frame && ComputeCompositedSelection(*local_frame, selection)) {
page->GetChromeClient().UpdateCompositedSelection(local_frame, selection);
} else {
if (!local_frame) {
// Clearing the mainframe when there is no focused frame (and hence
// no localFrame) is legacy behaviour, and implemented here to
// satisfy ParameterizedWebFrameTest.CompositedSelectionBoundsCleared's
// first check that the composited selection has been cleared even
// though no frame has focus yet. If this is not desired, then the
// expectation needs to be removed from the test.
local_frame = &frame_->LocalFrameRoot();
}
if (local_frame)
page->GetChromeClient().ClearCompositedSelection(local_frame);
}
}
void LocalFrameView::SetNeedsCompositingUpdate(
CompositingUpdateType update_type) {
if (!GetLayoutViewItem().IsNull() && frame_->GetDocument()->IsActive())
GetLayoutViewItem().Compositor()->SetNeedsCompositingUpdate(update_type);
}
PlatformChromeClient* LocalFrameView::GetChromeClient() const {
Page* page = GetFrame().GetPage();
if (!page)
return nullptr;
return &page->GetChromeClient();
}
SmoothScrollSequencer* LocalFrameView::GetSmoothScrollSequencer() const {
Page* page = GetFrame().GetPage();
if (!page)
return nullptr;
return page->GetSmoothScrollSequencer();
}
void LocalFrameView::ContentsResized() {
if (frame_->IsMainFrame() && frame_->GetDocument()) {
if (TextAutosizer* text_autosizer =
frame_->GetDocument()->GetTextAutosizer())
text_autosizer->UpdatePageInfoInAllFrames();
}
ScrollableArea::ContentsResized();
SetNeedsLayout();
}
void LocalFrameView::ScrollbarExistenceDidChange() {
// We check to make sure the view is attached to a frame() as this method can
// be triggered before the view is attached by LocalFrame::createView(...)
// setting various values such as setScrollBarModes(...) for example. An
// ASSERT is triggered when a view is layout before being attached to a
// frame().
if (!GetFrame().View())
return;
Element* custom_scrollbar_element = nullptr;
bool uses_overlay_scrollbars =
ScrollbarTheme::GetTheme().UsesOverlayScrollbars() &&
!ShouldUseCustomScrollbars(custom_scrollbar_element);
if (!uses_overlay_scrollbars) {
if (NeedsLayout())
UpdateLayout();
if (frame_->IsMainFrame() &&
RuntimeEnabledFeatures::VisualViewportAPIEnabled())
frame_->GetDocument()->EnqueueVisualViewportResizeEvent();
}
if (!GetLayoutViewItem().IsNull() && GetLayoutViewItem().UsesCompositing()) {
GetLayoutViewItem().Compositor()->FrameViewScrollbarsExistenceDidChange();
if (!uses_overlay_scrollbars)
GetLayoutViewItem().Compositor()->FrameViewDidChangeSize();
}
}
void LocalFrameView::HandleLoadCompleted() {
// Once loading has completed, allow autoSize one last opportunity to
// reduce the size of the frame.
if (auto_size_info_)
auto_size_info_->AutoSizeIfNeeded();
// If there is a pending layout, the fragment anchor will be cleared when it
// finishes.
if (!NeedsLayout())
ClearFragmentAnchor();
if (!scrollable_areas_)
return;
for (const auto& scrollable_area : *scrollable_areas_) {
if (!scrollable_area->IsPaintLayerScrollableArea())
continue;
PaintLayerScrollableArea* paint_layer_scrollable_area =
ToPaintLayerScrollableArea(scrollable_area);
if (paint_layer_scrollable_area->ScrollsOverflow() &&
!paint_layer_scrollable_area->Layer()->IsRootLayer()) {
DEFINE_STATIC_LOCAL(
CustomCountHistogram, scrollable_area_size_histogram,
("Event.Scroll.ScrollerSize.OnLoad", 1, kScrollerSizeLargestBucket,
kScrollerSizeBucketCount));
scrollable_area_size_histogram.Count(
paint_layer_scrollable_area->VisibleContentRect().Width() *
paint_layer_scrollable_area->VisibleContentRect().Height());
}
}
}
void LocalFrameView::ClearLayoutSubtreeRoot(const LayoutObject& root) {
layout_subtree_root_list_.Remove(const_cast<LayoutObject&>(root));
}
void LocalFrameView::ClearLayoutSubtreeRootsAndMarkContainingBlocks() {
layout_subtree_root_list_.ClearAndMarkContainingBlocksForLayout();
}
void LocalFrameView::AddOrthogonalWritingModeRoot(LayoutBox& root) {
DCHECK(!root.IsLayoutScrollbarPart());
orthogonal_writing_mode_root_list_.Add(root);
}
void LocalFrameView::RemoveOrthogonalWritingModeRoot(LayoutBox& root) {
orthogonal_writing_mode_root_list_.Remove(root);
}
bool LocalFrameView::HasOrthogonalWritingModeRoots() const {
return !orthogonal_writing_mode_root_list_.IsEmpty();
}
static inline void RemoveFloatingObjectsForSubtreeRoot(LayoutObject& root) {
// TODO(kojii): Under certain conditions, moveChildTo() defers
// removeFloatingObjects() until the containing block layouts. For
// instance, when descendants of the moving child is floating,
// removeChildNode() does not clear them. In such cases, at this
// point, FloatingObjects may contain old or even deleted objects.
// Dealing this in markAllDescendantsWithFloatsForLayout() could
// solve, but since that is likely to suffer the performance and
// since the containing block of orthogonal writing mode roots
// having floats is very rare, prefer to re-create
// FloatingObjects.
if (LayoutBlock* cb = root.ContainingBlock()) {
if ((cb->NormalChildNeedsLayout() || cb->SelfNeedsLayout()) &&
cb->IsLayoutBlockFlow()) {
ToLayoutBlockFlow(cb)->RemoveFloatingObjectsFromDescendants();
}
}
}
static bool PrepareOrthogonalWritingModeRootForLayout(LayoutObject& root) {
DCHECK(root.IsBox() && ToLayoutBox(root).IsOrthogonalWritingModeRoot());
if (!root.NeedsLayout() || root.IsOutOfFlowPositioned() ||
root.IsColumnSpanAll() ||
!root.StyleRef().LogicalHeight().IsIntrinsicOrAuto() ||
ToLayoutBox(root).IsGridItem())
return false;
RemoveFloatingObjectsForSubtreeRoot(root);
return true;
}
void LocalFrameView::LayoutOrthogonalWritingModeRoots() {
for (auto& root : orthogonal_writing_mode_root_list_.Ordered()) {
if (PrepareOrthogonalWritingModeRootForLayout(*root))
LayoutFromRootObject(*root);
}
}
void LocalFrameView::ScheduleOrthogonalWritingModeRootsForLayout() {
for (auto& root : orthogonal_writing_mode_root_list_.Ordered()) {
if (PrepareOrthogonalWritingModeRootForLayout(*root))
layout_subtree_root_list_.Add(*root);
}
}
bool LocalFrameView::CheckLayoutInvalidationIsAllowed() const {
if (allows_layout_invalidation_after_layout_clean_)
return true;
// If we are updating all lifecycle phases beyond LayoutClean, we don't expect
// dirty layout after LayoutClean.
CHECK_FOR_DIRTY_LAYOUT(Lifecycle().GetState() <
DocumentLifecycle::kLayoutClean);
return true;
}
void LocalFrameView::ScheduleRelayout() {
DCHECK(frame_->View() == this);
if (!layout_scheduling_enabled_)
return;
// TODO(crbug.com/590856): It's still broken when we choose not to crash when
// the check fails.
if (!CheckLayoutInvalidationIsAllowed())
return;
if (!NeedsLayout())
return;
if (!frame_->GetDocument()->ShouldScheduleLayout())
return;
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
"InvalidateLayout", TRACE_EVENT_SCOPE_THREAD, "data",
InspectorInvalidateLayoutEvent::Data(frame_.Get()));
ClearLayoutSubtreeRootsAndMarkContainingBlocks();
if (has_pending_layout_)
return;
has_pending_layout_ = true;
if (!ShouldThrottleRendering())
GetPage()->Animator().ScheduleVisualUpdate(frame_.Get());
}
void LocalFrameView::ScheduleRelayoutOfSubtree(LayoutObject* relayout_root) {
DCHECK(frame_->View() == this);
// TODO(crbug.com/590856): It's still broken when we choose not to crash when
// the check fails.
if (!CheckLayoutInvalidationIsAllowed())
return;
// FIXME: Should this call shouldScheduleLayout instead?
if (!frame_->GetDocument()->IsActive())
return;
LayoutView* layout_view = this->GetLayoutView();
if (layout_view && layout_view->NeedsLayout()) {
if (relayout_root)
relayout_root->MarkContainerChainForLayout(false);
return;
}
if (relayout_root == layout_view)
layout_subtree_root_list_.ClearAndMarkContainingBlocksForLayout();
else
layout_subtree_root_list_.Add(*relayout_root);
if (layout_scheduling_enabled_) {
has_pending_layout_ = true;
if (!ShouldThrottleRendering())
GetPage()->Animator().ScheduleVisualUpdate(frame_.Get());
Lifecycle().EnsureStateAtMost(DocumentLifecycle::kStyleClean);
}
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
"InvalidateLayout", TRACE_EVENT_SCOPE_THREAD, "data",
InspectorInvalidateLayoutEvent::Data(frame_.Get()));
}
bool LocalFrameView::LayoutPending() const {
// FIXME: This should check Document::lifecycle instead.
return has_pending_layout_;
}
bool LocalFrameView::IsInPerformLayout() const {
return Lifecycle().GetState() == DocumentLifecycle::kInPerformLayout;
}
bool LocalFrameView::NeedsLayout() const {
// This can return true in cases where the document does not have a body yet.
// Document::shouldScheduleLayout takes care of preventing us from scheduling
// layout in that case.
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
return LayoutPending() ||
(!layout_view_item.IsNull() && layout_view_item.NeedsLayout()) ||
IsSubtreeLayout();
}
NOINLINE bool LocalFrameView::CheckDoesNotNeedLayout() const {
CHECK_FOR_DIRTY_LAYOUT(!LayoutPending());
CHECK_FOR_DIRTY_LAYOUT(GetLayoutViewItem().IsNull() ||
!GetLayoutViewItem().NeedsLayout());
CHECK_FOR_DIRTY_LAYOUT(!IsSubtreeLayout());
return true;
}
void LocalFrameView::SetNeedsLayout() {
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull())
return;
// TODO(crbug.com/590856): It's still broken if we choose not to crash when
// the check fails.
if (!CheckLayoutInvalidationIsAllowed())
return;
layout_view_item.SetNeedsLayout(LayoutInvalidationReason::kUnknown);
}
bool LocalFrameView::HasOpaqueBackground() const {
return !base_background_color_.HasAlpha();
}
Color LocalFrameView::BaseBackgroundColor() const {
return base_background_color_;
}
void LocalFrameView::SetBaseBackgroundColor(const Color& background_color) {
if (base_background_color_ == background_color)
return;
base_background_color_ = background_color;
if (!GetLayoutViewItem().IsNull() &&
GetLayoutViewItem().Layer()->HasCompositedLayerMapping()) {
CompositedLayerMapping* composited_layer_mapping =
GetLayoutViewItem().Layer()->GetCompositedLayerMapping();
composited_layer_mapping->UpdateContentsOpaque();
if (composited_layer_mapping->MainGraphicsLayer())
composited_layer_mapping->MainGraphicsLayer()->SetNeedsDisplay();
}
RecalculateScrollbarOverlayColorTheme(DocumentBackgroundColor());
if (!ShouldThrottleRendering())
GetPage()->Animator().ScheduleVisualUpdate(frame_.Get());
}
void LocalFrameView::UpdateBaseBackgroundColorRecursively(
const Color& base_background_color) {
ForAllNonThrottledLocalFrameViews(
[base_background_color](LocalFrameView& frame_view) {
frame_view.SetBaseBackgroundColor(base_background_color);
});
}
void LocalFrameView::ScrollToFragmentAnchor() {
Node* anchor_node = fragment_anchor_;
if (!anchor_node)
return;
// Scrolling is disabled during updateScrollbars (see
// isProgrammaticallyScrollable). Bail now to avoid clearing m_fragmentAnchor
// before we actually have a chance to scroll.
if (in_update_scrollbars_)
return;
if (anchor_node->GetLayoutObject()) {
LayoutRect rect;
if (anchor_node != frame_->GetDocument()) {
rect = anchor_node->BoundingBox();
} else if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
if (Element* document_element = frame_->GetDocument()->documentElement())
rect = document_element->BoundingBox();
}
Frame* boundary_frame = frame_->FindUnsafeParentScrollPropagationBoundary();
// FIXME: Handle RemoteFrames
if (boundary_frame && boundary_frame->IsLocalFrame()) {
ToLocalFrame(boundary_frame)
->View()
->SetSafeToPropagateScrollToParent(false);
}
// Scroll nested layers and frames to reveal the anchor.
// Align to the top and to the closest side (this matches other browsers).
anchor_node->GetLayoutObject()->ScrollRectToVisible(
rect, ScrollAlignment::kAlignToEdgeIfNeeded,
ScrollAlignment::kAlignTopAlways);
if (boundary_frame && boundary_frame->IsLocalFrame()) {
ToLocalFrame(boundary_frame)
->View()
->SetSafeToPropagateScrollToParent(true);
}
if (AXObjectCache* cache = frame_->GetDocument()->ExistingAXObjectCache())
cache->HandleScrolledToAnchor(anchor_node);
}
// The fragment anchor should only be maintained while the frame is still
// loading. If the frame is done loading, clear the anchor now. Otherwise,
// restore it since it may have been cleared during scrollRectToVisible.
fragment_anchor_ =
frame_->GetDocument()->IsLoadCompleted() ? nullptr : anchor_node;
}
bool LocalFrameView::UpdatePlugins() {
// This is always called from UpdatePluginsTimerFired.
// update_plugins_timer should only be scheduled if we have FrameViews to
// update. Thus I believe we can stop checking isEmpty here, and just ASSERT
// isEmpty:
// FIXME: This assert has been temporarily removed due to
// https://crbug.com/430344
if (nested_layout_count_ > 1 || part_update_set_.IsEmpty())
return true;
// Need to swap because script will run inside the below loop and invalidate
// the iterator.
EmbeddedObjectSet objects;
objects.swap(part_update_set_);
for (const auto& embedded_object : objects) {
LayoutEmbeddedObject& object = *embedded_object;
HTMLPlugInElement* element = ToHTMLPlugInElement(object.GetNode());
// The object may have already been destroyed (thus node cleared),
// but LocalFrameView holds a manual ref, so it won't have been deleted.
if (!element)
continue;
// No need to update if it's already crashed or known to be missing.
if (object.ShowsUnavailablePluginIndicator())
continue;
if (element->NeedsPluginUpdate())
element->UpdatePlugin();
object.UpdateGeometry();
// Prevent plugins from causing infinite updates of themselves.
// FIXME: Do we really need to prevent this?
part_update_set_.erase(&object);
}
return part_update_set_.IsEmpty();
}
void LocalFrameView::UpdatePluginsTimerFired(TimerBase*) {
DCHECK(!IsInPerformLayout());
for (unsigned i = 0; i < kMaxUpdatePluginsIterations; ++i) {
if (UpdatePlugins())
return;
}
}
void LocalFrameView::FlushAnyPendingPostLayoutTasks() {
DCHECK(!IsInPerformLayout());
if (post_layout_tasks_timer_.IsActive())
PerformPostLayoutTasks();
if (update_plugins_timer_.IsActive()) {
update_plugins_timer_.Stop();
UpdatePluginsTimerFired(nullptr);
}
}
void LocalFrameView::ScheduleUpdatePluginsIfNecessary() {
DCHECK(!IsInPerformLayout());
if (update_plugins_timer_.IsActive() || part_update_set_.IsEmpty())
return;
update_plugins_timer_.StartOneShot(0, BLINK_FROM_HERE);
}
void LocalFrameView::PerformPostLayoutTasks() {
// FIXME: We can reach here, even when the page is not active!
// http/tests/inspector/elements/html-link-import.html and many other
// tests hit that case.
// We should DCHECK(isActive()); or at least return early if we can!
// Always called before or after performLayout(), part of the highest-level
// layout() call.
DCHECK(!IsInPerformLayout());
TRACE_EVENT0("blink,benchmark", "LocalFrameView::performPostLayoutTasks");
post_layout_tasks_timer_.Stop();
frame_->Selection().DidLayout();
DCHECK(frame_->GetDocument());
FontFaceSet::DidLayout(*frame_->GetDocument());
// Cursor update scheduling is done by the local root, which is the main frame
// if there are no RemoteFrame ancestors in the frame tree. Use of
// localFrameRoot() is discouraged but will change when cursor update
// scheduling is moved from EventHandler to PageEventHandler.
GetFrame().LocalFrameRoot().GetEventHandler().ScheduleCursorUpdate();
UpdateGeometries();
// Plugins could have torn down the page inside updateGeometries().
if (GetLayoutViewItem().IsNull())
return;
ScheduleUpdatePluginsIfNecessary();
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->NotifyGeometryChanged();
ScrollToFragmentAnchor();
SendResizeEventIfNeeded();
}
bool LocalFrameView::WasViewportResized() {
DCHECK(frame_);
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull())
return false;
DCHECK(layout_view_item.Style());
return (GetLayoutSize(kIncludeScrollbars) != last_viewport_size_ ||
layout_view_item.Style()->Zoom() != last_zoom_factor_);
}
void LocalFrameView::SendResizeEventIfNeeded() {
DCHECK(frame_);
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (layout_view_item.IsNull() || layout_view_item.GetDocument().Printing())
return;
if (!WasViewportResized())
return;
last_viewport_size_ = GetLayoutSize(kIncludeScrollbars);
last_zoom_factor_ = layout_view_item.Style()->Zoom();
if (RuntimeEnabledFeatures::VisualViewportAPIEnabled())
frame_->GetDocument()->EnqueueVisualViewportResizeEvent();
frame_->GetDocument()->EnqueueResizeEvent();
if (frame_->IsMainFrame())
probe::didResizeMainFrame(frame_.Get());
}
void LocalFrameView::PostLayoutTimerFired(TimerBase*) {
PerformPostLayoutTasks();
}
void LocalFrameView::UpdateCounters() {
LayoutView* view = GetLayoutView();
if (!view->HasLayoutCounters())
return;
for (LayoutObject* layout_object = view; layout_object;
layout_object = layout_object->NextInPreOrder()) {
if (!layout_object->IsCounter())
continue;
ToLayoutCounter(layout_object)->UpdateCounter();
}
}
bool LocalFrameView::ShouldUseIntegerScrollOffset() const {
if (frame_->GetSettings() &&
!frame_->GetSettings()->GetPreferCompositingToLCDTextEnabled())
return true;
return ScrollableArea::ShouldUseIntegerScrollOffset();
}
bool LocalFrameView::IsActive() const {
Page* page = GetFrame().GetPage();
return page && page->GetFocusController().IsActive();
}
void LocalFrameView::InvalidatePaintForTickmarks() {
if (Scrollbar* scrollbar = VerticalScrollbar()) {
scrollbar->SetNeedsPaintInvalidation(
static_cast<ScrollbarPart>(~kThumbPart));
}
}
void LocalFrameView::GetTickmarks(Vector<IntRect>& tickmarks) const {
if (!tickmarks_.IsEmpty()) {
tickmarks = tickmarks_;
return;
}
tickmarks =
GetFrame().GetDocument()->Markers().LayoutRectsForTextMatchMarkers();
}
void LocalFrameView::SetInputEventsTransformForEmulation(
const IntSize& offset,
float content_scale_factor) {
input_events_offset_for_emulation_ = offset;
input_events_scale_factor_for_emulation_ = content_scale_factor;
}
IntSize LocalFrameView::InputEventsOffsetForEmulation() const {
return input_events_offset_for_emulation_;
}
float LocalFrameView::InputEventsScaleFactor() const {
float page_scale = frame_->GetPage()->GetVisualViewport().Scale();
return page_scale * input_events_scale_factor_for_emulation_;
}
bool LocalFrameView::ScrollbarsCanBeActive() const {
if (frame_->View() != this)
return false;
return !!frame_->GetDocument();
}
void LocalFrameView::ScrollbarVisibilityChanged() {
UpdateScrollbarEnabledState();
LayoutViewItem view_item = GetLayoutViewItem();
if (!view_item.IsNull())
view_item.ClearHitTestCache();
}
void LocalFrameView::ScrollbarFrameRectChanged() {
SetNeedsPaintPropertyUpdate();
}
IntRect LocalFrameView::ScrollableAreaBoundingBox() const {
LayoutEmbeddedContentItem owner_layout_item = GetFrame().OwnerLayoutItem();
if (owner_layout_item.IsNull())
return FrameRect();
return owner_layout_item.AbsoluteContentQuad(kTraverseDocumentBoundaries)
.EnclosingBoundingBox();
}
bool LocalFrameView::IsScrollable() const {
return GetScrollingReasons() == kScrollable;
}
bool LocalFrameView::IsProgrammaticallyScrollable() {
return !in_update_scrollbars_;
}
LocalFrameView::ScrollingReasons LocalFrameView::GetScrollingReasons() const {
// Check for:
// 1) If there an actual overflow.
// 2) display:none or visibility:hidden set to self or inherited.
// 3) overflow{-x,-y}: hidden;
// 4) scrolling: no;
// Covers #1
IntSize contents_size = this->ContentsSize();
IntSize visible_content_size = VisibleContentRect().Size();
if ((contents_size.Height() <= visible_content_size.Height() &&
contents_size.Width() <= visible_content_size.Width()))
return kNotScrollableNoOverflow;
// Covers #2.
// FIXME: Do we need to fix this for OOPI?
HTMLFrameOwnerElement* owner = frame_->DeprecatedLocalOwner();
if (owner && (!owner->GetLayoutObject() ||
!owner->GetLayoutObject()->VisibleToHitTesting()))
return kNotScrollableNotVisible;
// Cover #3 and #4.
ScrollbarMode horizontal_mode;
ScrollbarMode vertical_mode;
GetLayoutView()->CalculateScrollbarModes(horizontal_mode, vertical_mode);
if (horizontal_mode == kScrollbarAlwaysOff &&
vertical_mode == kScrollbarAlwaysOff)
return kNotScrollableExplicitlyDisabled;
return kScrollable;
}
void LocalFrameView::UpdateParentScrollableAreaSet() {
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled())
return;
// That ensures that only inner frames are cached.
LocalFrameView* parent_frame_view = ParentFrameView();
if (!parent_frame_view)
return;
if (!IsScrollable()) {
parent_frame_view->RemoveScrollableArea(this);
return;
}
parent_frame_view->AddScrollableArea(this);
}
bool LocalFrameView::ShouldSuspendScrollAnimations() const {
return !frame_->GetDocument()->LoadEventFinished();
}
void LocalFrameView::ScrollbarStyleChanged() {
// FIXME: Why does this only apply to the main frame?
if (!frame_->IsMainFrame())
return;
AdjustScrollbarOpacity();
ContentsResized();
UpdateScrollbars();
PositionScrollbarLayers();
}
bool LocalFrameView::ScheduleAnimation() {
if (PlatformChromeClient* client = GetChromeClient()) {
client->ScheduleAnimation(this);
return true;
}
return false;
}
void LocalFrameView::NotifyPageThatContentAreaWillPaint() const {
Page* page = frame_->GetPage();
if (!page)
return;
ContentAreaWillPaint();
if (!scrollable_areas_)
return;
for (const auto& scrollable_area : *scrollable_areas_) {
if (!scrollable_area->ScrollbarsCanBeActive())
continue;
scrollable_area->ContentAreaWillPaint();
}
}
bool LocalFrameView::ScrollAnimatorEnabled() const {
return frame_->GetSettings() &&
frame_->GetSettings()->GetScrollAnimatorEnabled();
}
void LocalFrameView::UpdateDocumentAnnotatedRegions() const {
Document* document = frame_->GetDocument();
if (!document->HasAnnotatedRegions())
return;
Vector<AnnotatedRegionValue> new_regions;
CollectAnnotatedRegions(*(document->GetLayoutBox()), new_regions);
if (new_regions == document->AnnotatedRegions())
return;
document->SetAnnotatedRegions(new_regions);
if (Page* page = frame_->GetPage())
page->GetChromeClient().AnnotatedRegionsChanged();
}
void LocalFrameView::DidAttachDocument() {
Page* page = frame_->GetPage();
DCHECK(page);
DCHECK(frame_->GetDocument());
if (frame_->IsMainFrame()) {
ScrollableArea& visual_viewport = frame_->GetPage()->GetVisualViewport();
ScrollableArea* layout_viewport = LayoutViewportScrollableArea();
DCHECK(layout_viewport);
RootFrameViewport* root_frame_viewport =
RootFrameViewport::Create(visual_viewport, *layout_viewport);
viewport_scrollable_area_ = root_frame_viewport;
page->GlobalRootScrollerController().InitializeViewportScrollCallback(
*root_frame_viewport);
}
}
void LocalFrameView::UpdateScrollCorner() {
RefPtr<ComputedStyle> corner_style;
IntRect corner_rect = ScrollCornerRect();
Document* doc = frame_->GetDocument();
if (doc && !corner_rect.IsEmpty()) {
// Try the <body> element first as a scroll corner source.
if (Element* body = doc->body()) {
if (LayoutObject* layout_object = body->GetLayoutObject()) {
corner_style = layout_object->GetUncachedPseudoStyle(
PseudoStyleRequest(kPseudoIdScrollbarCorner),
layout_object->Style());
}
}
if (!corner_style) {
// If the <body> didn't have a custom style, then the root element might.
if (Element* doc_element = doc->documentElement()) {
if (LayoutObject* layout_object = doc_element->GetLayoutObject()) {
corner_style = layout_object->GetUncachedPseudoStyle(
PseudoStyleRequest(kPseudoIdScrollbarCorner),
layout_object->Style());
}
}
}
if (!corner_style) {
// If we have an owning ipage/LocalFrame element, then it can set the
// custom scrollbar also.
LayoutEmbeddedContentItem layout_item = frame_->OwnerLayoutItem();
if (!layout_item.IsNull()) {
corner_style = layout_item.GetUncachedPseudoStyle(
PseudoStyleRequest(kPseudoIdScrollbarCorner), layout_item.Style());
}
}
}
if (corner_style) {
if (!scroll_corner_)
scroll_corner_ = LayoutScrollbarPart::CreateAnonymous(doc, this);
scroll_corner_->SetStyleWithWritingModeOfParent(std::move(corner_style));
SetScrollCornerNeedsPaintInvalidation();
} else if (scroll_corner_) {
scroll_corner_->Destroy();
scroll_corner_ = nullptr;
}
}
Color LocalFrameView::DocumentBackgroundColor() const {
// The LayoutView's background color is set in
// Document::inheritHtmlAndBodyElementStyles. Blend this with the base
// background color of the LocalFrameView. This should match the color drawn
// by ViewPainter::paintBoxDecorationBackground.
Color result = BaseBackgroundColor();
LayoutItem document_layout_object = GetLayoutViewItem();
if (!document_layout_object.IsNull()) {
result = result.Blend(
document_layout_object.ResolveColor(CSSPropertyBackgroundColor));
}
return result;
}
LocalFrameView* LocalFrameView::ParentFrameView() const {
if (!is_attached_)
return nullptr;
Frame* parent_frame = frame_->Tree().Parent();
if (parent_frame && parent_frame->IsLocalFrame())
return ToLocalFrame(parent_frame)->View();
return nullptr;
}
void LocalFrameView::DidChangeGlobalRootScroller() {
// Being the global root scroller will affect clipping size due to browser
// controls behavior so we need to update compositing based on updated clip
// geometry.
SetNeedsCompositingUpdate(kCompositingUpdateAfterGeometryChange);
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled())
SetNeedsPaintPropertyUpdate();
// Avoid drawing two sets of scrollbars when visual viewport provides
// scrollbars.
if (frame_->GetSettings() && frame_->GetSettings()->GetViewportEnabled())
VisualViewportScrollbarsChanged();
}
// TODO(pdr): This logic is similar to adjustScrollbarExistence and the common
// logic should be factored into a helper.
void LocalFrameView::VisualViewportScrollbarsChanged() {
bool has_horizontal_scrollbar = HorizontalScrollbar();
bool has_vertical_scrollbar = VerticalScrollbar();
bool should_have_horizontal_scrollbar = false;
bool should_have_vertical_scrollbar = false;
ComputeScrollbarExistence(should_have_horizontal_scrollbar,
should_have_vertical_scrollbar, ContentsSize());
scrollbar_manager_.SetHasHorizontalScrollbar(
should_have_horizontal_scrollbar);
scrollbar_manager_.SetHasVerticalScrollbar(should_have_vertical_scrollbar);
if (has_horizontal_scrollbar != should_have_horizontal_scrollbar ||
has_vertical_scrollbar != should_have_vertical_scrollbar) {
ScrollbarExistenceDidChange();
if (!VisualViewportSuppliesScrollbars())
UpdateScrollbarGeometry();
}
}
void LocalFrameView::UpdateGeometriesIfNeeded() {
if (!needs_update_geometries_)
return;
needs_update_geometries_ = false;
UpdateGeometries();
}
void LocalFrameView::UpdateAllLifecyclePhases() {
GetFrame().LocalFrameRoot().View()->UpdateLifecyclePhasesInternal(
DocumentLifecycle::kPaintClean);
}
// TODO(chrishtr): add a scrolling update lifecycle phase.
void LocalFrameView::UpdateLifecycleToCompositingCleanPlusScrolling() {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
UpdateAllLifecyclePhasesExceptPaint();
} else {
GetFrame().LocalFrameRoot().View()->UpdateLifecyclePhasesInternal(
DocumentLifecycle::kCompositingClean);
}
}
void LocalFrameView::UpdateLifecycleToCompositingInputsClean() {
// When SPv2 is enabled, the standard compositing lifecycle steps do not
// exist; compositing is done after paint instead.
DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
GetFrame().LocalFrameRoot().View()->UpdateLifecyclePhasesInternal(
DocumentLifecycle::kCompositingInputsClean);
}
void LocalFrameView::UpdateAllLifecyclePhasesExceptPaint() {
GetFrame().LocalFrameRoot().View()->UpdateLifecyclePhasesInternal(
DocumentLifecycle::kPrePaintClean);
}
void LocalFrameView::UpdateLifecycleToLayoutClean() {
GetFrame().LocalFrameRoot().View()->UpdateLifecyclePhasesInternal(
DocumentLifecycle::kLayoutClean);
}
void LocalFrameView::ScheduleVisualUpdateForPaintInvalidationIfNeeded() {
LocalFrame& local_frame_root = GetFrame().LocalFrameRoot();
if (local_frame_root.View()->current_update_lifecycle_phases_target_state_ <
DocumentLifecycle::kPaintInvalidationClean ||
Lifecycle().GetState() >= DocumentLifecycle::kPrePaintClean) {
// Schedule visual update to process the paint invalidation in the next
// cycle.
local_frame_root.ScheduleVisualUpdateUnlessThrottled();
}
// Otherwise the paint invalidation will be handled in paint invalidation
// phase of this cycle.
}
void LocalFrameView::NotifyResizeObservers() {
// Controller exists only if ResizeObserver was created.
if (!GetFrame().GetDocument()->GetResizeObserverController())
return;
ResizeObserverController& resize_controller =
frame_->GetDocument()->EnsureResizeObserverController();
DCHECK(Lifecycle().GetState() >= DocumentLifecycle::kLayoutClean);
size_t min_depth = 0;
for (min_depth = resize_controller.GatherObservations(0);
min_depth != ResizeObserverController::kDepthBottom;
min_depth = resize_controller.GatherObservations(min_depth)) {
resize_controller.DeliverObservations();
GetFrame().GetDocument()->UpdateStyleAndLayout();
}
if (resize_controller.SkippedObservations()) {
resize_controller.ClearObservations();
ErrorEvent* error = ErrorEvent::Create(
"ResizeObserver loop limit exceeded",
SourceLocation::Capture(frame_->GetDocument()), nullptr);
frame_->GetDocument()->DispatchErrorEvent(error, kNotSharableCrossOrigin);
// Ensure notifications will get delivered in next cycle.
if (LocalFrameView* frame_view = frame_->View())
frame_view->ScheduleAnimation();
}
DCHECK(!GetLayoutView()->NeedsLayout());
}
void LocalFrameView::DispatchEventsForPrintingOnAllFrames() {
DCHECK(frame_->IsMainFrame());
for (Frame* current_frame = frame_; current_frame;
current_frame = current_frame->Tree().TraverseNext(frame_)) {
if (current_frame->IsLocalFrame())
ToLocalFrame(current_frame)->GetDocument()->DispatchEventsForPrinting();
}
}
void LocalFrameView::SetupPrintContext() {
if (frame_->GetDocument()->Printing())
return;
if (!print_context_)
print_context_ = new PrintContext(frame_);
if (frame_->GetSettings())
frame_->GetSettings()->SetShouldPrintBackgrounds(true);
bool is_us = DefaultLanguage() == "en-US";
int width = is_us ? kLetterPortraitPageWidth : kA4PortraitPageWidth;
int height = is_us ? kLetterPortraitPageHeight : kA4PortraitPageHeight;
print_context_->BeginPrintMode(width, height);
print_context_->ComputePageRects(FloatSize(width, height));
DispatchEventsForPrintingOnAllFrames();
}
void LocalFrameView::ClearPrintContext() {
if (!print_context_)
return;
print_context_->EndPrintMode();
print_context_.Clear();
}
// TODO(leviw): We don't assert lifecycle information from documents in child
// PluginViews.
void LocalFrameView::UpdateLifecyclePhasesInternal(
DocumentLifecycle::LifecycleState target_state) {
if (current_update_lifecycle_phases_target_state_ !=
DocumentLifecycle::kUninitialized) {
NOTREACHED()
<< "LocalFrameView::updateLifecyclePhasesInternal() reentrance";
return;
}
// This must be called from the root frame, since it recurses down, not up.
// Otherwise the lifecycles of the frames might be out of sync.
DCHECK(frame_->IsLocalRoot());
// Only the following target states are supported.
DCHECK(target_state == DocumentLifecycle::kLayoutClean ||
target_state == DocumentLifecycle::kCompositingInputsClean ||
target_state == DocumentLifecycle::kCompositingClean ||
target_state == DocumentLifecycle::kPrePaintClean ||
target_state == DocumentLifecycle::kPaintClean);
if (!frame_->GetDocument()->IsActive())
return;
AutoReset<DocumentLifecycle::LifecycleState> target_state_scope(
&current_update_lifecycle_phases_target_state_, target_state);
if (ShouldThrottleRendering()) {
UpdateViewportIntersectionsForSubtree(
std::min(target_state, DocumentLifecycle::kCompositingClean));
return;
}
if (RuntimeEnabledFeatures::PrintBrowserEnabled())
SetupPrintContext();
else
ClearPrintContext();
UpdateStyleAndLayoutIfNeededRecursive();
DCHECK(Lifecycle().GetState() >= DocumentLifecycle::kLayoutClean);
if (target_state == DocumentLifecycle::kLayoutClean) {
UpdateViewportIntersectionsForSubtree(target_state);
return;
}
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.PerformScrollAnchoringAdjustments();
});
if (target_state == DocumentLifecycle::kPaintClean) {
ForAllNonThrottledLocalFrameViews(
[](LocalFrameView& frame_view) { frame_view.NotifyResizeObservers(); });
}
if (LayoutViewItem view = GetLayoutViewItem()) {
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.CheckDoesNotNeedLayout();
frame_view.allows_layout_invalidation_after_layout_clean_ = false;
});
{
TRACE_EVENT1("devtools.timeline", "UpdateLayerTree", "data",
InspectorUpdateLayerTreeEvent::Data(frame_.Get()));
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
view.Compositor()->UpdateIfNeededRecursive(target_state);
} else {
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.GetLayoutView()->Layer()->UpdateDescendantDependentFlags();
frame_view.GetLayoutView()->CommitPendingSelection();
});
}
if (target_state >= DocumentLifecycle::kCompositingClean) {
ScrollContentsIfNeededRecursive();
frame_->GetPage()
->GlobalRootScrollerController()
.DidUpdateCompositing();
}
if (target_state >= DocumentLifecycle::kPrePaintClean) {
if (!RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled())
DeprecatedInvalidateTreeRecursive();
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
if (view.Compositor()->InCompositingMode())
GetScrollingCoordinator()->UpdateAfterCompositingChangeIfNeeded();
}
// This is needed since, at present, the ScrollingCoordinator doesn't
// send rects for oopif sub-frames.
// TODO(wjmaclean): Remove this pathway when ScrollingCoordinator
// operates on a per-frame basis. https://crbug.com/680606
GetFrame()
.GetPage()
->GetChromeClient()
.UpdateEventRectsForSubframeIfNecessary(&frame_->LocalFrameRoot());
UpdateCompositedSelectionIfNeeded();
// TODO(pdr): prePaint should be under the "Paint" devtools timeline
// step for slimming paint v2.
PrePaint();
}
}
if (target_state == DocumentLifecycle::kPaintClean) {
if (!frame_->GetDocument()->Printing() ||
RuntimeEnabledFeatures::PrintBrowserEnabled())
PaintTree();
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
Optional<CompositorElementIdSet> composited_element_ids =
CompositorElementIdSet();
PushPaintArtifactToCompositor(composited_element_ids.value());
DocumentAnimations::UpdateAnimations(GetLayoutView()->GetDocument(),
DocumentLifecycle::kPaintClean,
composited_element_ids);
}
DCHECK(!frame_->Selection().NeedsLayoutSelectionUpdate());
DCHECK((frame_->GetDocument()->Printing() &&
Lifecycle().GetState() == DocumentLifecycle::kPrePaintClean) ||
Lifecycle().GetState() == DocumentLifecycle::kPaintClean);
}
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.CheckDoesNotNeedLayout();
frame_view.allows_layout_invalidation_after_layout_clean_ = true;
});
}
UpdateViewportIntersectionsForSubtree(target_state);
}
void LocalFrameView::EnqueueScrollAnchoringAdjustment(
ScrollableArea* scrollable_area) {
anchoring_adjustment_queue_.insert(scrollable_area);
}
void LocalFrameView::PerformScrollAnchoringAdjustments() {
for (WeakMember<ScrollableArea>& scroller : anchoring_adjustment_queue_) {
if (scroller) {
DCHECK(scroller->GetScrollAnchor());
scroller->GetScrollAnchor()->Adjust();
}
}
anchoring_adjustment_queue_.clear();
}
void LocalFrameView::PrePaint() {
TRACE_EVENT0("blink", "LocalFrameView::prePaint");
if (!paint_controller_)
paint_controller_ = PaintController::Create();
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPrePaint);
if (frame_view.CanThrottleRendering()) {
// This frame can be throttled but not throttled, meaning we are not in an
// AllowThrottlingScope. Now this frame may contain dirty paint flags, and
// we need to propagate the flags into the ancestor chain so that
// PrePaintTreeWalk can reach this frame.
frame_view.SetNeedsPaintPropertyUpdate();
if (auto owner = frame_view.GetFrame().OwnerLayoutItem())
owner.SetMayNeedPaintInvalidation();
}
});
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled()) {
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.PrePaint.UpdateTime");
PrePaintTreeWalk().Walk(*this);
}
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kPrePaintClean);
});
}
void LocalFrameView::PaintTree() {
TRACE_EVENT0("blink", "LocalFrameView::paintTree");
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.Paint.UpdateTime");
DCHECK(GetFrame() == GetPage()->MainFrame() ||
(!GetFrame().Tree().Parent()->IsLocalFrame()));
LayoutViewItem view = GetLayoutViewItem();
DCHECK(!view.IsNull());
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
});
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
if (GetLayoutView()->Layer()->NeedsRepaint()) {
paint_controller_->SetupRasterUnderInvalidationChecking();
GraphicsContext graphics_context(*paint_controller_);
if (RuntimeEnabledFeatures::PrintBrowserEnabled())
graphics_context.SetPrinting(true);
Paint(graphics_context, CullRect(LayoutRect::InfiniteIntRect()));
paint_controller_->CommitNewDisplayItems();
}
} else {
// A null graphics layer can occur for painting of SVG images that are not
// parented into the main frame tree, or when the LocalFrameView is the main
// frame view of a page overlay. The page overlay is in the layer tree of
// the host page and will be painted during painting of the host page.
if (GraphicsLayer* root_graphics_layer =
view.Compositor()->RootGraphicsLayer()) {
PaintGraphicsLayerRecursively(root_graphics_layer);
}
// TODO(sataya.m):Main frame doesn't create RootFrameViewport in some
// webkit_unit_tests (http://crbug.com/644788).
if (viewport_scrollable_area_) {
if (GraphicsLayer* layer_for_horizontal_scrollbar =
viewport_scrollable_area_->LayerForHorizontalScrollbar()) {
PaintGraphicsLayerRecursively(layer_for_horizontal_scrollbar);
}
if (GraphicsLayer* layer_for_vertical_scrollbar =
viewport_scrollable_area_->LayerForVerticalScrollbar()) {
PaintGraphicsLayerRecursively(layer_for_vertical_scrollbar);
}
if (GraphicsLayer* layer_for_scroll_corner =
viewport_scrollable_area_->LayerForScrollCorner()) {
PaintGraphicsLayerRecursively(layer_for_scroll_corner);
}
}
}
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.Lifecycle().AdvanceTo(DocumentLifecycle::kPaintClean);
LayoutViewItem layout_view_item = frame_view.GetLayoutViewItem();
if (!layout_view_item.IsNull())
layout_view_item.Layer()->ClearNeedsRepaintRecursively();
});
}
void LocalFrameView::PaintGraphicsLayerRecursively(
GraphicsLayer* graphics_layer) {
DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
if (graphics_layer->DrawsContent()) {
graphics_layer->Paint(nullptr);
}
if (GraphicsLayer* mask_layer = graphics_layer->MaskLayer())
PaintGraphicsLayerRecursively(mask_layer);
if (GraphicsLayer* contents_clipping_mask_layer =
graphics_layer->ContentsClippingMaskLayer())
PaintGraphicsLayerRecursively(contents_clipping_mask_layer);
for (auto& child : graphics_layer->Children())
PaintGraphicsLayerRecursively(child);
}
void LocalFrameView::PushPaintArtifactToCompositor(
CompositorElementIdSet& composited_element_ids) {
TRACE_EVENT0("blink", "LocalFrameView::pushPaintArtifactToCompositor");
DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
Page* page = GetFrame().GetPage();
if (!page)
return;
if (!paint_artifact_compositor_) {
paint_artifact_compositor_ = PaintArtifactCompositor::Create();
page->GetChromeClient().AttachRootLayer(
paint_artifact_compositor_->GetWebLayer(), &GetFrame());
}
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.Compositing.UpdateTime");
paint_artifact_compositor_->Update(
paint_controller_->GetPaintArtifact(),
is_storing_composited_layer_debug_info_, composited_element_ids);
}
std::unique_ptr<JSONObject> LocalFrameView::CompositedLayersAsJSON(
LayerTreeFlags flags) {
return GetFrame()
.LocalFrameRoot()
.View()
->paint_artifact_compositor_->LayersAsJSON(flags);
}
void LocalFrameView::UpdateStyleAndLayoutIfNeededRecursive() {
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.StyleAndLayout.UpdateTime");
UpdateStyleAndLayoutIfNeededRecursiveInternal();
}
void LocalFrameView::UpdateStyleAndLayoutIfNeededRecursiveInternal() {
if (ShouldThrottleRendering() || !frame_->GetDocument()->IsActive())
return;
ScopedFrameBlamer frame_blamer(frame_);
TRACE_EVENT0("blink",
"LocalFrameView::updateStyleAndLayoutIfNeededRecursive");
// We have to crawl our entire subtree looking for any FrameViews that need
// layout and make sure they are up to date.
// Mac actually tests for intersection with the dirty region and tries not to
// update layout for frames that are outside the dirty region. Not only does
// this seem pointless (since those frames will have set a zero timer to
// layout anyway), but it is also incorrect, since if two frames overlap, the
// first could be excluded from the dirty region but then become included
// later by the second frame adding rects to the dirty region when it lays
// out.
frame_->GetDocument()->UpdateStyleAndLayoutTree();
CHECK(!ShouldThrottleRendering());
CHECK(frame_->GetDocument()->IsActive());
CHECK(!nested_layout_count_);
if (NeedsLayout())
UpdateLayout();
CheckDoesNotNeedLayout();
// WebView plugins need to update regardless of whether the
// LayoutEmbeddedObject that owns them needed layout.
// TODO(leviw): This currently runs the entire lifecycle on plugin WebViews.
// We should have a way to only run these other Documents to the same
// lifecycle stage as this frame.
for (const auto& plugin : plugins_) {
plugin->UpdateAllLifecyclePhases();
}
CheckDoesNotNeedLayout();
// FIXME: Calling layout() shouldn't trigger script execution or have any
// observable effects on the frame tree but we're not quite there yet.
HeapVector<Member<LocalFrameView>> frame_views;
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (!child->IsLocalFrame())
continue;
if (LocalFrameView* view = ToLocalFrame(child)->View())
frame_views.push_back(view);
}
for (const auto& frame_view : frame_views)
frame_view->UpdateStyleAndLayoutIfNeededRecursiveInternal();
// These asserts ensure that parent frames are clean, when child frames
// finished updating layout and style.
CheckDoesNotNeedLayout();
#if DCHECK_IS_ON()
frame_->GetDocument()->GetLayoutView()->AssertLaidOut();
#endif
UpdateGeometriesIfNeeded();
if (Lifecycle().GetState() < DocumentLifecycle::kLayoutClean)
Lifecycle().AdvanceTo(DocumentLifecycle::kLayoutClean);
// Ensure that we become visually non-empty eventually.
// TODO(esprehn): This should check isRenderingReady() instead.
if (GetFrame().GetDocument()->HasFinishedParsing() &&
GetFrame().Loader().StateMachine()->CommittedFirstRealDocumentLoad())
is_visually_non_empty_ = true;
GetFrame().Selection().UpdateStyleAndLayoutIfNeeded();
GetFrame().GetPage()->GetDragCaret().UpdateStyleAndLayoutIfNeeded();
}
void LocalFrameView::DeprecatedInvalidateTreeRecursive() {
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.PaintInvalidation.UpdateTime");
{
// For comparison to SlimmingPaintInvalidation.
SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.PrePaint.UpdateTime");
DeprecatedInvalidateTreeRecursiveInternal();
}
}
void LocalFrameView::DeprecatedInvalidateTreeRecursiveInternal() {
DCHECK(!RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled());
CHECK(GetLayoutView());
// We need to stop recursing here since a child frame view might not be
// throttled even though we are (e.g., it didn't compute its visibility yet).
if (ShouldThrottleRendering())
return;
TRACE_EVENT0("blink",
"LocalFrameView::invalidateTreeIfNeededRecursiveInternal");
Vector<const LayoutObject*> pending_delayed_paint_invalidations;
PaintInvalidationState root_paint_invalidation_state(
*GetLayoutView(), pending_delayed_paint_invalidations);
if (Lifecycle().GetState() < DocumentLifecycle::kPaintInvalidationClean)
DeprecatedInvalidateTree(root_paint_invalidation_state);
// Some frames may be not reached during the above DeprecatedInvalidateTree
// because
// - the frame is a detached frame; or
// - it didn't need paint invalidation.
// We need to call invalidateTreeIfNeededRecursiveInternal() for such frames
// to finish required paint invalidation and advance their life cycle state.
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
if (child->IsLocalFrame()) {
LocalFrameView& child_frame_view = *ToLocalFrame(child)->View();
// The children frames can be in any state, including stopping.
// Thus we have to check that it makes sense to do paint
// invalidation onto them here.
if (!child_frame_view.GetLayoutView())
continue;
child_frame_view.DeprecatedInvalidateTreeRecursiveInternal();
}
}
// Process objects needing paint invalidation on the next frame. See the
// definition of PaintInvalidationDelayedFull for more details.
for (auto& target : pending_delayed_paint_invalidations) {
target->GetMutableForPainting().SetShouldDoFullPaintInvalidation(
PaintInvalidationReason::kDelayedFull);
}
}
void LocalFrameView::EnableAutoSizeMode(const IntSize& min_size,
const IntSize& max_size) {
if (!auto_size_info_)
auto_size_info_ = FrameViewAutoSizeInfo::Create(this);
auto_size_info_->ConfigureAutoSizeMode(min_size, max_size);
SetLayoutSizeFixedToFrameSize(true);
SetNeedsLayout();
ScheduleRelayout();
}
void LocalFrameView::DisableAutoSizeMode() {
if (!auto_size_info_)
return;
SetLayoutSizeFixedToFrameSize(false);
SetNeedsLayout();
ScheduleRelayout();
// Since autosize mode forces the scrollbar mode, change them to being auto.
SetVerticalScrollbarLock(false);
SetHorizontalScrollbarLock(false);
SetScrollbarModes(kScrollbarAuto, kScrollbarAuto);
auto_size_info_.Clear();
}
void LocalFrameView::ForceLayoutForPagination(
const FloatSize& page_size,
const FloatSize& original_page_size,
float maximum_shrink_factor) {
// Dumping externalRepresentation(m_frame->layoutObject()).ascii() is a good
// trick to see the state of things before and after the layout
if (LayoutView* layout_view = this->GetLayoutView()) {
float page_logical_width = layout_view->Style()->IsHorizontalWritingMode()
? page_size.Width()
: page_size.Height();
float page_logical_height = layout_view->Style()->IsHorizontalWritingMode()
? page_size.Height()
: page_size.Width();
LayoutUnit floored_page_logical_width =
static_cast<LayoutUnit>(page_logical_width);
LayoutUnit floored_page_logical_height =
static_cast<LayoutUnit>(page_logical_height);
layout_view->SetLogicalWidth(floored_page_logical_width);
layout_view->SetPageLogicalHeight(floored_page_logical_height);
layout_view->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::kPrintingChanged);
UpdateLayout();
// If we don't fit in the given page width, we'll lay out again. If we don't
// fit in the page width when shrunk, we will lay out at maximum shrink and
// clip extra content.
// FIXME: We are assuming a shrink-to-fit printing implementation. A
// cropping implementation should not do this!
bool horizontal_writing_mode =
layout_view->Style()->IsHorizontalWritingMode();
const LayoutRect& document_rect = LayoutRect(layout_view->DocumentRect());
LayoutUnit doc_logical_width = horizontal_writing_mode
? document_rect.Width()
: document_rect.Height();
if (doc_logical_width > page_logical_width) {
FloatSize expected_page_size(
std::min<float>(document_rect.Width().ToFloat(),
page_size.Width() * maximum_shrink_factor),
std::min<float>(document_rect.Height().ToFloat(),
page_size.Height() * maximum_shrink_factor));
FloatSize max_page_size = frame_->ResizePageRectsKeepingRatio(
FloatSize(original_page_size.Width(), original_page_size.Height()),
expected_page_size);
page_logical_width = horizontal_writing_mode ? max_page_size.Width()
: max_page_size.Height();
page_logical_height = horizontal_writing_mode ? max_page_size.Height()
: max_page_size.Width();
floored_page_logical_width = static_cast<LayoutUnit>(page_logical_width);
floored_page_logical_height =
static_cast<LayoutUnit>(page_logical_height);
layout_view->SetLogicalWidth(floored_page_logical_width);
layout_view->SetPageLogicalHeight(floored_page_logical_height);
layout_view->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
LayoutInvalidationReason::kPrintingChanged);
UpdateLayout();
const LayoutRect& updated_document_rect =
LayoutRect(layout_view->DocumentRect());
LayoutUnit doc_logical_height = horizontal_writing_mode
? updated_document_rect.Height()
: updated_document_rect.Width();
LayoutUnit doc_logical_top = horizontal_writing_mode
? updated_document_rect.Y()
: updated_document_rect.X();
LayoutUnit doc_logical_right = horizontal_writing_mode
? updated_document_rect.MaxX()
: updated_document_rect.MaxY();
LayoutUnit clipped_logical_left;
if (!layout_view->Style()->IsLeftToRightDirection()) {
clipped_logical_left =
LayoutUnit(doc_logical_right - page_logical_width);
}
LayoutRect overflow(clipped_logical_left, doc_logical_top,
LayoutUnit(page_logical_width), doc_logical_height);
if (!horizontal_writing_mode)
overflow = overflow.TransposedRect();
AdjustViewSizeAndLayout();
// This is how we clip in case we overflow again.
layout_view->ClearLayoutOverflow();
layout_view->AddLayoutOverflow(overflow);
return;
}
}
if (TextAutosizer* text_autosizer = frame_->GetDocument()->GetTextAutosizer())
text_autosizer->UpdatePageInfo();
AdjustViewSizeAndLayout();
}
IntRect LocalFrameView::ConvertFromLayoutItem(
const LayoutItem& layout_item,
const IntRect& layout_object_rect) const {
// Convert from page ("absolute") to LocalFrameView coordinates.
LayoutRect rect = EnclosingLayoutRect(
layout_item.LocalToAbsoluteQuad(FloatRect(layout_object_rect))
.BoundingBox());
rect.Move(LayoutSize(-GetScrollOffset()));
return PixelSnappedIntRect(rect);
}
IntRect LocalFrameView::ConvertToLayoutItem(const LayoutItem& layout_item,
const IntRect& frame_rect) const {
IntRect rect_in_content = FrameToContents(frame_rect);
// Convert from LocalFrameView coords into page ("absolute") coordinates.
rect_in_content.Move(ScrollOffsetInt());
// FIXME: we don't have a way to map an absolute rect down to a local quad, so
// just move the rect for now.
rect_in_content.SetLocation(RoundedIntPoint(
layout_item.AbsoluteToLocal(rect_in_content.Location(), kUseTransforms)));
return rect_in_content;
}
IntPoint LocalFrameView::ConvertFromLayoutItem(
const LayoutItem& layout_item,
const IntPoint& layout_object_point) const {
IntPoint point = RoundedIntPoint(
layout_item.LocalToAbsolute(layout_object_point, kUseTransforms));
// Convert from page ("absolute") to LocalFrameView coordinates.
point.Move(-ScrollOffsetInt());
return point;
}
IntPoint LocalFrameView::ConvertToLayoutItem(
const LayoutItem& layout_item,
const IntPoint& frame_point) const {
IntPoint point = frame_point;
// Convert from LocalFrameView coords into page ("absolute") coordinates.
point += IntSize(ScrollX(), ScrollY());
return RoundedIntPoint(layout_item.AbsoluteToLocal(point, kUseTransforms));
}
IntPoint LocalFrameView::ConvertSelfToChild(const EmbeddedContentView& child,
const IntPoint& point) const {
IntPoint new_point = point;
new_point = FrameToContents(point);
new_point.MoveBy(-child.FrameRect().Location());
return new_point;
}
IntRect LocalFrameView::ConvertToContainingEmbeddedContentView(
const IntRect& local_rect) const {
if (LocalFrameView* parent = ParentFrameView()) {
// Get our layoutObject in the parent view
LayoutEmbeddedContentItem layout_item = frame_->OwnerLayoutItem();
if (layout_item.IsNull())
return local_rect;
IntRect rect(local_rect);
// Add borders and padding??
rect.Move((layout_item.BorderLeft() + layout_item.PaddingLeft()).ToInt(),
(layout_item.BorderTop() + layout_item.PaddingTop()).ToInt());
return parent->ConvertFromLayoutItem(layout_item, rect);
}
return local_rect;
}
IntRect LocalFrameView::ConvertFromContainingEmbeddedContentView(
const IntRect& parent_rect) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntRect local_rect = parent_rect;
local_rect.SetLocation(
parent->ConvertSelfToChild(*this, local_rect.Location()));
return local_rect;
}
return parent_rect;
}
IntPoint LocalFrameView::ConvertToContainingEmbeddedContentView(
const IntPoint& local_point) const {
if (LocalFrameView* parent = ParentFrameView()) {
// Get our layoutObject in the parent view
LayoutEmbeddedContentItem layout_item = frame_->OwnerLayoutItem();
if (layout_item.IsNull())
return local_point;
IntPoint point(local_point);
// Add borders and padding
point.Move((layout_item.BorderLeft() + layout_item.PaddingLeft()).ToInt(),
(layout_item.BorderTop() + layout_item.PaddingTop()).ToInt());
return parent->ConvertFromLayoutItem(layout_item, point);
}
return local_point;
}
IntPoint LocalFrameView::ConvertFromContainingEmbeddedContentView(
const IntPoint& parent_point) const {
if (LocalFrameView* parent = ParentFrameView()) {
// Get our layoutObject in the parent view
LayoutEmbeddedContentItem layout_item = frame_->OwnerLayoutItem();
if (layout_item.IsNull())
return parent_point;
IntPoint point = parent->ConvertToLayoutItem(layout_item, parent_point);
// Subtract borders and padding
point.Move((-layout_item.BorderLeft() - layout_item.PaddingLeft()).ToInt(),
(-layout_item.BorderTop() - layout_item.PaddingTop()).ToInt());
return point;
}
return parent_point;
}
void LocalFrameView::SetInitialTracksPaintInvalidationsForTesting(
bool track_paint_invalidations) {
g_initial_track_all_paint_invalidations = track_paint_invalidations;
}
void LocalFrameView::SetTracksPaintInvalidations(
bool track_paint_invalidations) {
if (track_paint_invalidations == IsTrackingPaintInvalidations())
return;
// Ensure the document is up-to-date before tracking invalidations.
UpdateAllLifecyclePhases();
for (Frame* frame = &frame_->Tree().Top(); frame;
frame = frame->Tree().TraverseNext()) {
if (!frame->IsLocalFrame())
continue;
if (LayoutViewItem layout_view = ToLocalFrame(frame)->ContentLayoutItem()) {
layout_view.GetFrameView()->tracked_object_paint_invalidations_ =
WTF::WrapUnique(track_paint_invalidations
? new Vector<ObjectPaintInvalidation>
: nullptr);
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
if (!paint_controller_)
paint_controller_ = PaintController::Create();
paint_controller_->SetTracksRasterInvalidations(
track_paint_invalidations);
if (paint_artifact_compositor_)
paint_artifact_compositor_->ResetTrackedRasterInvalidations();
} else {
layout_view.Compositor()->SetTracksRasterInvalidations(
track_paint_invalidations);
}
}
}
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"),
"LocalFrameView::setTracksPaintInvalidations",
TRACE_EVENT_SCOPE_GLOBAL, "enabled",
track_paint_invalidations);
}
void LocalFrameView::TrackObjectPaintInvalidation(
const DisplayItemClient& client,
PaintInvalidationReason reason) {
if (!tracked_object_paint_invalidations_)
return;
ObjectPaintInvalidation invalidation = {client.DebugName(), reason};
tracked_object_paint_invalidations_->push_back(invalidation);
}
std::unique_ptr<JSONArray>
LocalFrameView::TrackedObjectPaintInvalidationsAsJSON() const {
if (!tracked_object_paint_invalidations_)
return nullptr;
std::unique_ptr<JSONArray> result = JSONArray::Create();
for (Frame* frame = &frame_->Tree().Top(); frame;
frame = frame->Tree().TraverseNext()) {
if (!frame->IsLocalFrame())
continue;
if (LayoutViewItem layout_view = ToLocalFrame(frame)->ContentLayoutItem()) {
if (!layout_view.GetFrameView()->tracked_object_paint_invalidations_)
continue;
for (const auto& item :
*layout_view.GetFrameView()->tracked_object_paint_invalidations_) {
std::unique_ptr<JSONObject> item_json = JSONObject::Create();
item_json->SetString("object", item.name);
item_json->SetString("reason",
PaintInvalidationReasonToString(item.reason));
result->PushObject(std::move(item_json));
}
}
}
return result;
}
void LocalFrameView::AddResizerArea(LayoutBox& resizer_box) {
if (!resizer_areas_)
resizer_areas_ = WTF::WrapUnique(new ResizerAreaSet);
resizer_areas_->insert(&resizer_box);
}
void LocalFrameView::RemoveResizerArea(LayoutBox& resizer_box) {
if (!resizer_areas_)
return;
ResizerAreaSet::iterator it = resizer_areas_->find(&resizer_box);
if (it != resizer_areas_->end())
resizer_areas_->erase(it);
}
void LocalFrameView::AddScrollableArea(ScrollableArea* scrollable_area) {
DCHECK(scrollable_area);
if (!scrollable_areas_)
scrollable_areas_ = new ScrollableAreaSet;
scrollable_areas_->insert(scrollable_area);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->ScrollableAreasDidChange();
}
void LocalFrameView::RemoveScrollableArea(ScrollableArea* scrollable_area) {
if (!scrollable_areas_)
return;
scrollable_areas_->erase(scrollable_area);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->ScrollableAreasDidChange();
}
void LocalFrameView::AddAnimatingScrollableArea(
ScrollableArea* scrollable_area) {
DCHECK(scrollable_area);
if (!animating_scrollable_areas_)
animating_scrollable_areas_ = new ScrollableAreaSet;
animating_scrollable_areas_->insert(scrollable_area);
}
void LocalFrameView::RemoveAnimatingScrollableArea(
ScrollableArea* scrollable_area) {
if (!animating_scrollable_areas_)
return;
animating_scrollable_areas_->erase(scrollable_area);
}
void LocalFrameView::Attach() {
DCHECK(!is_attached_);
is_attached_ = true;
if (ParentFrameView()->IsVisible())
SetParentVisible(true);
UpdateParentScrollableAreaSet();
SetupRenderThrottling();
subtree_throttled_ = ParentFrameView()->CanThrottleRendering();
}
void LocalFrameView::Detach() {
DCHECK(is_attached_);
if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled())
ParentFrameView()->RemoveScrollableArea(this);
SetParentVisible(false);
is_attached_ = false;
}
void LocalFrameView::AddPlugin(PluginView* plugin) {
DCHECK(!plugins_.Contains(plugin));
plugins_.insert(plugin);
}
void LocalFrameView::RemoveScrollbar(Scrollbar* scrollbar) {
DCHECK(scrollbars_.Contains(scrollbar));
scrollbars_.erase(scrollbar);
}
void LocalFrameView::AddScrollbar(Scrollbar* scrollbar) {
DCHECK(!scrollbars_.Contains(scrollbar));
scrollbars_.insert(scrollbar);
}
bool LocalFrameView::VisualViewportSuppliesScrollbars() {
// On desktop, we always use the layout viewport's scrollbars.
if (!frame_->GetSettings() || !frame_->GetSettings()->GetViewportEnabled() ||
!frame_->GetDocument() || !frame_->GetPage())
return false;
const TopDocumentRootScrollerController& controller =
frame_->GetPage()->GlobalRootScrollerController();
if (!LayoutViewportScrollableArea())
return false;
return RootScrollerUtil::ScrollableAreaForRootScroller(
controller.GlobalRootScroller()) == LayoutViewportScrollableArea();
}
AXObjectCache* LocalFrameView::AxObjectCache() const {
if (GetFrame().GetDocument())
return GetFrame().GetDocument()->ExistingAXObjectCache();
return nullptr;
}
void LocalFrameView::SetCursor(const Cursor& cursor) {
Page* page = GetFrame().GetPage();
if (!page || frame_->GetEventHandler().IsMousePositionUnknown())
return;
page->GetChromeClient().SetCursor(cursor, frame_);
}
void LocalFrameView::FrameRectsChanged() {
TRACE_EVENT0("blink", "LocalFrameView::frameRectsChanged");
if (LayoutSizeFixedToFrameSize())
SetLayoutSizeInternal(FrameRect().Size());
ForAllChildViewsAndPlugins([](EmbeddedContentView& embedded_content_view) {
embedded_content_view.FrameRectsChanged();
});
}
void LocalFrameView::SetLayoutSizeInternal(const IntSize& size) {
if (layout_size_ == size)
return;
layout_size_ = size;
ContentsResized();
}
void LocalFrameView::DidAddScrollbar(Scrollbar& scrollbar,
ScrollbarOrientation orientation) {
ScrollableArea::DidAddScrollbar(scrollbar, orientation);
}
PaintLayer* LocalFrameView::Layer() const {
LayoutViewItem layout_view = GetLayoutViewItem();
if (layout_view.IsNull() || !layout_view.Compositor())
return nullptr;
return layout_view.Compositor()->RootLayer();
}
IntSize LocalFrameView::MaximumScrollOffsetInt() const {
// Make the same calculation as in CC's LayerImpl::MaxScrollOffset()
// FIXME: We probably shouldn't be storing the bounds in a float.
// crbug.com/422331.
IntSize visible_size = VisibleContentSize(kExcludeScrollbars);
IntSize content_bounds = ContentsSize();
Page* page = frame_->GetPage();
DCHECK(page);
// We need to perform this const_cast since maximumScrollOffsetInt is a const
// method but we can't make layoutViewportScrollableArea const since it can
// return |this|. Once root-layer-scrolls ships layoutViewportScrollableArea
// can be made const.
const ScrollableArea* layout_viewport =
const_cast<LocalFrameView*>(this)->LayoutViewportScrollableArea();
TopDocumentRootScrollerController& controller =
page->GlobalRootScrollerController();
if (layout_viewport == controller.RootScrollerArea())
visible_size = controller.RootScrollerVisibleArea();
IntSize maximum_offset =
ToIntSize(-ScrollOrigin() + (content_bounds - visible_size));
return maximum_offset.ExpandedTo(MinimumScrollOffsetInt());
}
void LocalFrameView::SetScrollbarModes(ScrollbarMode horizontal_mode,
ScrollbarMode vertical_mode,
bool horizontal_lock,
bool vertical_lock) {
bool needs_update = false;
// If the page's overflow setting has disabled scrolling, do not allow
// anything to override that setting, http://crbug.com/426447
LayoutObject* viewport = ViewportLayoutObject();
if (viewport && !ShouldIgnoreOverflowHidden()) {
if (viewport->Style()->OverflowX() == EOverflow::kHidden)
horizontal_mode = kScrollbarAlwaysOff;
if (viewport->Style()->OverflowY() == EOverflow::kHidden)
vertical_mode = kScrollbarAlwaysOff;
}
if (horizontal_mode != HorizontalScrollbarMode() &&
!horizontal_scrollbar_lock_) {
horizontal_scrollbar_mode_ = horizontal_mode;
needs_update = true;
}
if (vertical_mode != VerticalScrollbarMode() && !vertical_scrollbar_lock_) {
vertical_scrollbar_mode_ = vertical_mode;
needs_update = true;
}
if (horizontal_lock)
SetHorizontalScrollbarLock();
if (vertical_lock)
SetVerticalScrollbarLock();
if (!needs_update)
return;
UpdateScrollbars();
if (!LayerForScrolling())
return;
WebLayer* layer = LayerForScrolling()->PlatformLayer();
if (!layer)
return;
layer->SetUserScrollable(UserInputScrollable(kHorizontalScrollbar),
UserInputScrollable(kVerticalScrollbar));
}
IntSize LocalFrameView::VisibleContentSize(
IncludeScrollbarsInRect scrollbar_inclusion) const {
return scrollbar_inclusion == kExcludeScrollbars
? ExcludeScrollbars(FrameRect().Size())
: FrameRect().Size();
}
IntRect LocalFrameView::VisibleContentRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
return IntRect(IntPoint(FlooredIntSize(scroll_offset_)),
VisibleContentSize(scrollbar_inclusion));
}
IntSize LocalFrameView::ContentsSize() const {
return contents_size_;
}
void LocalFrameView::ClipPaintRect(FloatRect* paint_rect) const {
// Paint the whole rect if "mainFrameClipsContent" is false, meaning that
// WebPreferences::record_whole_document is true.
if (!frame_->GetSettings()->GetMainFrameClipsContent())
return;
paint_rect->Intersect(
GetPage()->GetChromeClient().VisibleContentRectForPainting().value_or(
VisibleContentRect()));
}
IntSize LocalFrameView::MinimumScrollOffsetInt() const {
return IntSize(-ScrollOrigin().X(), -ScrollOrigin().Y());
}
void LocalFrameView::AdjustScrollbarOpacity() {
if (HorizontalScrollbar() && LayerForHorizontalScrollbar()) {
bool is_opaque_scrollbar = !HorizontalScrollbar()->IsOverlayScrollbar();
LayerForHorizontalScrollbar()->SetContentsOpaque(is_opaque_scrollbar);
}
if (VerticalScrollbar() && LayerForVerticalScrollbar()) {
bool is_opaque_scrollbar = !VerticalScrollbar()->IsOverlayScrollbar();
LayerForVerticalScrollbar()->SetContentsOpaque(is_opaque_scrollbar);
}
}
int LocalFrameView::ScrollSize(ScrollbarOrientation orientation) const {
Scrollbar* scrollbar =
((orientation == kHorizontalScrollbar) ? HorizontalScrollbar()
: VerticalScrollbar());
// If no scrollbars are present, the content may still be scrollable.
if (!scrollbar) {
IntSize scroll_size = contents_size_ - VisibleContentRect().Size();
scroll_size.ClampNegativeToZero();
return orientation == kHorizontalScrollbar ? scroll_size.Width()
: scroll_size.Height();
}
return scrollbar->TotalSize() - scrollbar->VisibleSize();
}
void LocalFrameView::UpdateScrollOffset(const ScrollOffset& offset,
ScrollType scroll_type) {
ScrollOffset scroll_delta = offset - scroll_offset_;
if (scroll_delta.IsZero())
return;
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// Don't scroll the LocalFrameView!
NOTREACHED();
}
scroll_offset_ = offset;
if (!ScrollbarsSuppressed())
pending_scroll_delta_ += scroll_delta;
UpdateLayersAndCompositingAfterScrollIfNeeded();
Document* document = frame_->GetDocument();
document->EnqueueScrollEventForNode(document);
frame_->GetEventHandler().DispatchFakeMouseMoveEventSoon();
if (scroll_type == kUserScroll || scroll_type == kCompositorScroll) {
Page* page = GetFrame().GetPage();
if (page)
page->GetChromeClient().ClearToolTip(*frame_);
}
LayoutViewItem layout_view_item = document->GetLayoutViewItem();
if (!layout_view_item.IsNull()) {
if (layout_view_item.UsesCompositing())
layout_view_item.Compositor()->FrameViewDidScroll();
layout_view_item.ClearHitTestCache();
}
did_scroll_timer_.StartOneShot(kResourcePriorityUpdateDelayAfterScroll,
BLINK_FROM_HERE);
if (AXObjectCache* cache = frame_->GetDocument()->ExistingAXObjectCache())
cache->HandleScrollPositionChanged(this);
GetFrame().Loader().SaveScrollState();
DidChangeScrollOffset();
if (scroll_type == kCompositorScroll && frame_->IsMainFrame()) {
if (DocumentLoader* document_loader = frame_->Loader().GetDocumentLoader())
document_loader->GetInitialScrollState().was_scrolled_by_user = true;
}
if (IsExplicitScrollType(scroll_type)) {
if (scroll_type != kCompositorScroll)
ShowOverlayScrollbars();
ClearFragmentAnchor();
ClearScrollAnchor();
}
}
void LocalFrameView::DidChangeScrollOffset() {
GetFrame().Loader().Client()->DidChangeScrollOffset();
if (GetFrame().IsMainFrame())
GetFrame().GetPage()->GetChromeClient().MainFrameScrollOffsetChanged();
}
void LocalFrameView::ClearScrollAnchor() {
if (!RuntimeEnabledFeatures::ScrollAnchoringEnabled())
return;
scroll_anchor_.Clear();
}
bool LocalFrameView::HasOverlayScrollbars() const {
return (HorizontalScrollbar() &&
HorizontalScrollbar()->IsOverlayScrollbar()) ||
(VerticalScrollbar() && VerticalScrollbar()->IsOverlayScrollbar());
}
void LocalFrameView::ComputeScrollbarExistence(
bool& new_has_horizontal_scrollbar,
bool& new_has_vertical_scrollbar,
const IntSize& doc_size,
ComputeScrollbarExistenceOption option) {
if ((frame_->GetSettings() && frame_->GetSettings()->GetHideScrollbars()) ||
VisualViewportSuppliesScrollbars()) {
new_has_horizontal_scrollbar = false;
new_has_vertical_scrollbar = false;
return;
}
bool has_horizontal_scrollbar = HorizontalScrollbar();
bool has_vertical_scrollbar = VerticalScrollbar();
new_has_horizontal_scrollbar = has_horizontal_scrollbar;
new_has_vertical_scrollbar = has_vertical_scrollbar;
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled())
return;
ScrollbarMode h_scroll = horizontal_scrollbar_mode_;
ScrollbarMode v_scroll = vertical_scrollbar_mode_;
if (h_scroll != kScrollbarAuto)
new_has_horizontal_scrollbar = (h_scroll == kScrollbarAlwaysOn);
if (v_scroll != kScrollbarAuto)
new_has_vertical_scrollbar = (v_scroll == kScrollbarAlwaysOn);
if (scrollbars_suppressed_ ||
(h_scroll != kScrollbarAuto && v_scroll != kScrollbarAuto))
return;
if (h_scroll == kScrollbarAuto)
new_has_horizontal_scrollbar = doc_size.Width() > VisibleWidth();
if (v_scroll == kScrollbarAuto)
new_has_vertical_scrollbar = doc_size.Height() > VisibleHeight();
if (HasOverlayScrollbars())
return;
IntSize full_visible_size = VisibleContentRect(kIncludeScrollbars).Size();
bool attempt_to_remove_scrollbars =
(option == kFirstPass && doc_size.Width() <= full_visible_size.Width() &&
doc_size.Height() <= full_visible_size.Height());
if (attempt_to_remove_scrollbars) {
if (h_scroll == kScrollbarAuto)
new_has_horizontal_scrollbar = false;
if (v_scroll == kScrollbarAuto)
new_has_vertical_scrollbar = false;
}
}
void LocalFrameView::UpdateScrollbarEnabledState() {
bool force_disabled =
ScrollbarTheme::GetTheme().ShouldDisableInvisibleScrollbars() &&
ScrollbarsHidden();
if (HorizontalScrollbar()) {
HorizontalScrollbar()->SetEnabled(ContentsWidth() > VisibleWidth() &&
!force_disabled);
}
if (VerticalScrollbar()) {
VerticalScrollbar()->SetEnabled(ContentsHeight() > VisibleHeight() &&
!force_disabled);
}
}
void LocalFrameView::UpdateScrollbarGeometry() {
UpdateScrollbarEnabledState();
if (HorizontalScrollbar()) {
int thickness = HorizontalScrollbar()->ScrollbarThickness();
IntRect old_rect(HorizontalScrollbar()->FrameRect());
IntRect h_bar_rect(
(ShouldPlaceVerticalScrollbarOnLeft() && VerticalScrollbar())
? VerticalScrollbar()->Width()
: 0,
Height() - thickness,
Width() - (VerticalScrollbar() ? VerticalScrollbar()->Width() : 0),
thickness);
HorizontalScrollbar()->SetFrameRect(h_bar_rect);
if (old_rect != HorizontalScrollbar()->FrameRect())
SetScrollbarNeedsPaintInvalidation(kHorizontalScrollbar);
HorizontalScrollbar()->SetProportion(VisibleWidth(), ContentsWidth());
HorizontalScrollbar()->OffsetDidChange();
}
if (VerticalScrollbar()) {
int thickness = VerticalScrollbar()->ScrollbarThickness();
IntRect old_rect(VerticalScrollbar()->FrameRect());
IntRect v_bar_rect(
ShouldPlaceVerticalScrollbarOnLeft() ? 0 : (Width() - thickness), 0,
thickness,
Height() -
(HorizontalScrollbar() ? HorizontalScrollbar()->Height() : 0));
VerticalScrollbar()->SetFrameRect(v_bar_rect);
if (old_rect != VerticalScrollbar()->FrameRect())
SetScrollbarNeedsPaintInvalidation(kVerticalScrollbar);
VerticalScrollbar()->SetProportion(VisibleHeight(), ContentsHeight());
VerticalScrollbar()->OffsetDidChange();
}
}
bool LocalFrameView::AdjustScrollbarExistence(
ComputeScrollbarExistenceOption option) {
DCHECK(in_update_scrollbars_);
// If we came in here with the view already needing a layout, then go ahead
// and do that first. (This will be the common case, e.g., when the page
// changes due to window resizing for example). This layout will not re-enter
// updateScrollbars and does not count towards our max layout pass total.
if (!scrollbars_suppressed_)
ScrollbarExistenceDidChange();
bool has_horizontal_scrollbar = HorizontalScrollbar();
bool has_vertical_scrollbar = VerticalScrollbar();
bool new_has_horizontal_scrollbar = false;
bool new_has_vertical_scrollbar = false;
ComputeScrollbarExistence(new_has_horizontal_scrollbar,
new_has_vertical_scrollbar, ContentsSize(), option);
bool scrollbar_existence_changed =
has_horizontal_scrollbar != new_has_horizontal_scrollbar ||
has_vertical_scrollbar != new_has_vertical_scrollbar;
if (!scrollbar_existence_changed)
return false;
scrollbar_manager_.SetHasHorizontalScrollbar(new_has_horizontal_scrollbar);
scrollbar_manager_.SetHasVerticalScrollbar(new_has_vertical_scrollbar);
if (scrollbars_suppressed_)
return true;
if (!HasOverlayScrollbars())
SetNeedsLayout();
ScrollbarExistenceDidChange();
return true;
}
bool LocalFrameView::NeedsScrollbarReconstruction() const {
Scrollbar* scrollbar = HorizontalScrollbar();
if (!scrollbar)
scrollbar = VerticalScrollbar();
if (!scrollbar) {
// We have no scrollbar to reconstruct.
return false;
}
Element* style_source = nullptr;
bool needs_custom = ShouldUseCustomScrollbars(style_source);
bool is_custom = scrollbar->IsCustomScrollbar();
if (needs_custom != is_custom) {
// We have a native scrollbar that should be custom, or vice versa.
return true;
}
if (!needs_custom) {
// We have a native scrollbar that should remain native.
return false;
}
DCHECK(needs_custom && is_custom);
DCHECK(style_source);
if (ToLayoutScrollbar(scrollbar)->StyleSource() !=
style_source->GetLayoutObject()) {
// We have a custom scrollbar with a stale m_owner.
return true;
}
return false;
}
bool LocalFrameView::ShouldIgnoreOverflowHidden() const {
return frame_->GetSettings()->GetIgnoreMainFrameOverflowHiddenQuirk() &&
frame_->IsMainFrame();
}
void LocalFrameView::UpdateScrollbarsIfNeeded() {
if (needs_scrollbars_update_ || NeedsScrollbarReconstruction() ||
ScrollOriginChanged())
UpdateScrollbars();
}
void LocalFrameView::UpdateScrollbars() {
needs_scrollbars_update_ = false;
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled())
return;
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled())
SetNeedsPaintPropertyUpdate();
// Avoid drawing two sets of scrollbars when visual viewport is enabled.
if (VisualViewportSuppliesScrollbars()) {
scrollbar_manager_.SetHasHorizontalScrollbar(false);
scrollbar_manager_.SetHasVerticalScrollbar(false);
AdjustScrollOffsetFromUpdateScrollbars();
return;
}
if (in_update_scrollbars_)
return;
InUpdateScrollbarsScope in_update_scrollbars_scope(this);
bool scrollbar_existence_changed = false;
if (NeedsScrollbarReconstruction()) {
scrollbar_manager_.SetHasHorizontalScrollbar(false);
scrollbar_manager_.SetHasVerticalScrollbar(false);
scrollbar_existence_changed = true;
}
int max_update_scrollbars_pass =
HasOverlayScrollbars() || scrollbars_suppressed_ ? 1 : 3;
for (int update_scrollbars_pass = 0;
update_scrollbars_pass < max_update_scrollbars_pass;
update_scrollbars_pass++) {
if (!AdjustScrollbarExistence(update_scrollbars_pass ? kIncremental
: kFirstPass))
break;
scrollbar_existence_changed = true;
}
UpdateScrollbarGeometry();
if (scrollbar_existence_changed) {
// FIXME: Is frameRectsChanged really necessary here? Have any frame rects
// changed?
FrameRectsChanged();
PositionScrollbarLayers();
UpdateScrollCorner();
}
AdjustScrollOffsetFromUpdateScrollbars();
}
void LocalFrameView::AdjustScrollOffsetFromUpdateScrollbars() {
ScrollOffset clamped = ClampScrollOffset(GetScrollOffset());
if (clamped != GetScrollOffset() || ScrollOriginChanged())
SetScrollOffset(clamped, kClampingScroll);
}
void LocalFrameView::ScrollContentsIfNeeded() {
if (pending_scroll_delta_.IsZero())
return;
ScrollOffset scroll_delta = pending_scroll_delta_;
pending_scroll_delta_ = ScrollOffset();
// FIXME: Change scrollContents() to take DoubleSize. crbug.com/414283.
ScrollContents(FlooredIntSize(scroll_delta));
}
void LocalFrameView::ScrollContents(const IntSize& scroll_delta) {
PlatformChromeClient* client = GetChromeClient();
if (!client)
return;
TRACE_EVENT0("blink", "LocalFrameView::scrollContents");
if (!ScrollContentsFastPath(-scroll_delta))
ScrollContentsSlowPath();
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// Need to update scroll translation property.
SetNeedsPaintPropertyUpdate();
}
// This call will move children with native FrameViews (plugins) and
// invalidate them as well.
FrameRectsChanged();
}
IntPoint LocalFrameView::ContentsToFrame(
const IntPoint& point_in_content_space) const {
return point_in_content_space - ScrollOffsetInt();
}
IntRect LocalFrameView::ContentsToFrame(
const IntRect& rect_in_content_space) const {
return IntRect(ContentsToFrame(rect_in_content_space.Location()),
rect_in_content_space.Size());
}
FloatPoint LocalFrameView::FrameToContents(
const FloatPoint& point_in_frame) const {
return point_in_frame + GetScrollOffset();
}
IntPoint LocalFrameView::FrameToContents(const IntPoint& point_in_frame) const {
return point_in_frame + ScrollOffsetInt();
}
IntRect LocalFrameView::FrameToContents(const IntRect& rect_in_frame) const {
return IntRect(FrameToContents(rect_in_frame.Location()),
rect_in_frame.Size());
}
IntPoint LocalFrameView::RootFrameToContents(
const IntPoint& root_frame_point) const {
IntPoint frame_point = ConvertFromRootFrame(root_frame_point);
return FrameToContents(frame_point);
}
IntRect LocalFrameView::RootFrameToContents(
const IntRect& root_frame_rect) const {
return IntRect(RootFrameToContents(root_frame_rect.Location()),
root_frame_rect.Size());
}
IntPoint LocalFrameView::ContentsToRootFrame(
const IntPoint& contents_point) const {
IntPoint frame_point = ContentsToFrame(contents_point);
return ConvertToRootFrame(frame_point);
}
IntRect LocalFrameView::ContentsToRootFrame(
const IntRect& contents_rect) const {
IntRect rect_in_frame = ContentsToFrame(contents_rect);
return ConvertToRootFrame(rect_in_frame);
}
FloatPoint LocalFrameView::RootFrameToContents(
const FloatPoint& point_in_root_frame) const {
FloatPoint frame_point = ConvertFromRootFrame(point_in_root_frame);
return FrameToContents(frame_point);
}
IntRect LocalFrameView::ViewportToContents(
const IntRect& rect_in_viewport) const {
IntRect rect_in_root_frame =
frame_->GetPage()->GetVisualViewport().ViewportToRootFrame(
rect_in_viewport);
IntRect frame_rect = ConvertFromRootFrame(rect_in_root_frame);
return FrameToContents(frame_rect);
}
IntPoint LocalFrameView::ViewportToContents(
const IntPoint& point_in_viewport) const {
IntPoint point_in_root_frame =
frame_->GetPage()->GetVisualViewport().ViewportToRootFrame(
point_in_viewport);
IntPoint point_in_frame = ConvertFromRootFrame(point_in_root_frame);
return FrameToContents(point_in_frame);
}
IntRect LocalFrameView::ContentsToViewport(
const IntRect& rect_in_contents) const {
IntRect rect_in_frame = ContentsToFrame(rect_in_contents);
IntRect rect_in_root_frame = ConvertToRootFrame(rect_in_frame);
return frame_->GetPage()->GetVisualViewport().RootFrameToViewport(
rect_in_root_frame);
}
IntPoint LocalFrameView::ContentsToViewport(
const IntPoint& point_in_contents) const {
IntPoint point_in_frame = ContentsToFrame(point_in_contents);
IntPoint point_in_root_frame = ConvertToRootFrame(point_in_frame);
return frame_->GetPage()->GetVisualViewport().RootFrameToViewport(
point_in_root_frame);
}
IntRect LocalFrameView::ContentsToScreen(const IntRect& rect) const {
PlatformChromeClient* client = GetChromeClient();
if (!client)
return IntRect();
return client->ViewportToScreen(ContentsToViewport(rect), this);
}
IntPoint LocalFrameView::SoonToBeRemovedUnscaledViewportToContents(
const IntPoint& point_in_viewport) const {
IntPoint point_in_root_frame = FlooredIntPoint(
frame_->GetPage()->GetVisualViewport().ViewportCSSPixelsToRootFrame(
point_in_viewport));
IntPoint point_in_this_frame = ConvertFromRootFrame(point_in_root_frame);
return FrameToContents(point_in_this_frame);
}
Scrollbar* LocalFrameView::ScrollbarAtFramePoint(
const IntPoint& point_in_frame) {
if (HorizontalScrollbar() &&
HorizontalScrollbar()->ShouldParticipateInHitTesting() &&
HorizontalScrollbar()->FrameRect().Contains(point_in_frame))
return HorizontalScrollbar();
if (VerticalScrollbar() &&
VerticalScrollbar()->ShouldParticipateInHitTesting() &&
VerticalScrollbar()->FrameRect().Contains(point_in_frame))
return VerticalScrollbar();
return nullptr;
}
static void PositionScrollbarLayer(GraphicsLayer* graphics_layer,
Scrollbar* scrollbar) {
if (!graphics_layer || !scrollbar)
return;
IntRect scrollbar_rect = scrollbar->FrameRect();
graphics_layer->SetPosition(scrollbar_rect.Location());
if (scrollbar_rect.Size() == graphics_layer->Size())
return;
graphics_layer->SetSize(FloatSize(scrollbar_rect.Size()));
if (graphics_layer->HasContentsLayer()) {
graphics_layer->SetContentsRect(
IntRect(0, 0, scrollbar_rect.Width(), scrollbar_rect.Height()));
return;
}
graphics_layer->SetDrawsContent(true);
graphics_layer->SetNeedsDisplay();
}
static void PositionScrollCornerLayer(GraphicsLayer* graphics_layer,
const IntRect& corner_rect) {
if (!graphics_layer)
return;
graphics_layer->SetDrawsContent(!corner_rect.IsEmpty());
graphics_layer->SetPosition(corner_rect.Location());
if (corner_rect.Size() != graphics_layer->Size())
graphics_layer->SetNeedsDisplay();
graphics_layer->SetSize(FloatSize(corner_rect.Size()));
}
void LocalFrameView::PositionScrollbarLayers() {
PositionScrollbarLayer(LayerForHorizontalScrollbar(), HorizontalScrollbar());
PositionScrollbarLayer(LayerForVerticalScrollbar(), VerticalScrollbar());
PositionScrollCornerLayer(LayerForScrollCorner(), ScrollCornerRect());
}
bool LocalFrameView::UpdateAfterCompositingChange() {
if (ScrollOriginChanged()) {
// If the scroll origin changed, we need to update the layer position on
// the compositor since the offset itself might not have changed.
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (!layout_view_item.IsNull() && layout_view_item.UsesCompositing())
layout_view_item.Compositor()->FrameViewDidScroll();
ResetScrollOriginChanged();
}
return false;
}
bool LocalFrameView::UserInputScrollable(
ScrollbarOrientation orientation) const {
Document* document = GetFrame().GetDocument();
Element* fullscreen_element = Fullscreen::FullscreenElementFrom(*document);
if (fullscreen_element && fullscreen_element != document->documentElement())
return false;
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled())
return false;
ScrollbarMode mode = (orientation == kHorizontalScrollbar)
? horizontal_scrollbar_mode_
: vertical_scrollbar_mode_;
return mode == kScrollbarAuto || mode == kScrollbarAlwaysOn;
}
bool LocalFrameView::ShouldPlaceVerticalScrollbarOnLeft() const {
return false;
}
LayoutRect LocalFrameView::ScrollIntoView(const LayoutRect& rect_in_content,
const ScrollAlignment& align_x,
const ScrollAlignment& align_y,
bool is_smooth,
ScrollType scroll_type) {
LayoutRect view_rect(VisibleContentRect());
LayoutRect expose_rect = ScrollAlignment::GetRectToExpose(
view_rect, rect_in_content, align_x, align_y);
if (expose_rect != view_rect) {
ScrollOffset target_offset(expose_rect.X().ToFloat(),
expose_rect.Y().ToFloat());
if (is_smooth) {
DCHECK(scroll_type == kProgrammaticScroll);
GetSmoothScrollSequencer()->QueueAnimation(this, target_offset);
} else {
SetScrollOffset(target_offset, scroll_type);
}
}
// Scrolling the LocalFrameView cannot change the input rect's location
// relative to the document.
return rect_in_content;
}
IntRect LocalFrameView::ScrollCornerRect() const {
IntRect corner_rect;
if (HasOverlayScrollbars())
return corner_rect;
if (HorizontalScrollbar() && Width() - HorizontalScrollbar()->Width() > 0) {
corner_rect.Unite(IntRect(ShouldPlaceVerticalScrollbarOnLeft()
? 0
: HorizontalScrollbar()->Width(),
Height() - HorizontalScrollbar()->Height(),
Width() - HorizontalScrollbar()->Width(),
HorizontalScrollbar()->Height()));
}
if (VerticalScrollbar() && Height() - VerticalScrollbar()->Height() > 0) {
corner_rect.Unite(IntRect(ShouldPlaceVerticalScrollbarOnLeft()
? 0
: (Width() - VerticalScrollbar()->Width()),
VerticalScrollbar()->Height(),
VerticalScrollbar()->Width(),
Height() - VerticalScrollbar()->Height()));
}
return corner_rect;
}
bool LocalFrameView::IsScrollCornerVisible() const {
return !ScrollCornerRect().IsEmpty();
}
ScrollBehavior LocalFrameView::ScrollBehaviorStyle() const {
Element* scroll_element = frame_->GetDocument()->scrollingElement();
LayoutObject* layout_object =
scroll_element ? scroll_element->GetLayoutObject() : nullptr;
if (layout_object &&
layout_object->Style()->GetScrollBehavior() == kScrollBehaviorSmooth)
return kScrollBehaviorSmooth;
return kScrollBehaviorInstant;
}
void LocalFrameView::Paint(GraphicsContext& context,
const CullRect& cull_rect) const {
Paint(context, kGlobalPaintNormalPhase, cull_rect);
}
void LocalFrameView::Paint(GraphicsContext& context,
const GlobalPaintFlags global_paint_flags,
const CullRect& cull_rect) const {
FramePainter(*this).Paint(context, global_paint_flags, cull_rect);
}
void LocalFrameView::PaintContents(GraphicsContext& context,
const GlobalPaintFlags global_paint_flags,
const IntRect& damage_rect) const {
FramePainter(*this).PaintContents(context, global_paint_flags, damage_rect);
}
bool LocalFrameView::IsPointInScrollbarCorner(
const IntPoint& point_in_root_frame) {
if (!ScrollbarCornerPresent())
return false;
IntPoint frame_point = ConvertFromRootFrame(point_in_root_frame);
if (HorizontalScrollbar()) {
int horizontal_scrollbar_y_min = HorizontalScrollbar()->FrameRect().Y();
int horizontal_scrollbar_y_max =
HorizontalScrollbar()->FrameRect().Y() +
HorizontalScrollbar()->FrameRect().Height();
int horizontal_scrollbar_x_min = HorizontalScrollbar()->FrameRect().X() +
HorizontalScrollbar()->FrameRect().Width();
return frame_point.Y() > horizontal_scrollbar_y_min &&
frame_point.Y() < horizontal_scrollbar_y_max &&
frame_point.X() > horizontal_scrollbar_x_min;
}
int vertical_scrollbar_x_min = VerticalScrollbar()->FrameRect().X();
int vertical_scrollbar_x_max = VerticalScrollbar()->FrameRect().X() +
VerticalScrollbar()->FrameRect().Width();
int vertical_scrollbar_y_min = VerticalScrollbar()->FrameRect().Y() +
VerticalScrollbar()->FrameRect().Height();
return frame_point.X() > vertical_scrollbar_x_min &&
frame_point.X() < vertical_scrollbar_x_max &&
frame_point.Y() > vertical_scrollbar_y_min;
}
bool LocalFrameView::ScrollbarCornerPresent() const {
return (HorizontalScrollbar() &&
Width() - HorizontalScrollbar()->Width() > 0) ||
(VerticalScrollbar() && Height() - VerticalScrollbar()->Height() > 0);
}
IntRect LocalFrameView::ConvertToRootFrame(const IntRect& local_rect) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntRect parent_rect = ConvertToContainingEmbeddedContentView(local_rect);
return parent->ConvertToRootFrame(parent_rect);
}
return local_rect;
}
IntPoint LocalFrameView::ConvertToRootFrame(const IntPoint& local_point) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntPoint parent_point = ConvertToContainingEmbeddedContentView(local_point);
return parent->ConvertToRootFrame(parent_point);
}
return local_point;
}
IntRect LocalFrameView::ConvertFromRootFrame(
const IntRect& rect_in_root_frame) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntRect parent_rect = parent->ConvertFromRootFrame(rect_in_root_frame);
return ConvertFromContainingEmbeddedContentView(parent_rect);
}
return rect_in_root_frame;
}
IntPoint LocalFrameView::ConvertFromRootFrame(
const IntPoint& point_in_root_frame) const {
if (LocalFrameView* parent = ParentFrameView()) {
IntPoint parent_point = parent->ConvertFromRootFrame(point_in_root_frame);
return ConvertFromContainingEmbeddedContentView(parent_point);
}
return point_in_root_frame;
}
FloatPoint LocalFrameView::ConvertFromRootFrame(
const FloatPoint& point_in_root_frame) const {
// FrameViews / windows are required to be IntPoint aligned, but we may
// need to convert FloatPoint values within them (eg. for event
// co-ordinates).
IntPoint floored_point = FlooredIntPoint(point_in_root_frame);
FloatPoint parent_point = ConvertFromRootFrame(floored_point);
FloatSize window_fraction = point_in_root_frame - floored_point;
// Use linear interpolation handle any fractional value (eg. for iframes
// subject to a transform beyond just a simple translation).
// FIXME: Add FloatPoint variants of all co-ordinate space conversion APIs.
if (!window_fraction.IsEmpty()) {
const int kFactor = 1000;
IntPoint parent_line_end = ConvertFromRootFrame(
floored_point + RoundedIntSize(window_fraction.ScaledBy(kFactor)));
FloatSize parent_fraction =
(parent_line_end - parent_point).ScaledBy(1.0f / kFactor);
parent_point.Move(parent_fraction);
}
return parent_point;
}
IntPoint LocalFrameView::ConvertFromContainingEmbeddedContentViewToScrollbar(
const Scrollbar& scrollbar,
const IntPoint& parent_point) const {
IntPoint new_point = parent_point;
// Scrollbars won't be transformed within us
new_point.MoveBy(-scrollbar.Location());
return new_point;
}
void LocalFrameView::SetParentVisible(bool visible) {
if (IsParentVisible() == visible)
return;
// As parent visibility changes, we may need to recomposite this frame view
// and potentially child frame views.
SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
parent_visible_ = visible;
if (!IsSelfVisible())
return;
ForAllChildViewsAndPlugins(
[visible](EmbeddedContentView& embedded_content_view) {
embedded_content_view.SetParentVisible(visible);
});
}
void LocalFrameView::Show() {
if (!IsSelfVisible()) {
SetSelfVisible(true);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->FrameViewVisibilityDidChange();
SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
UpdateParentScrollableAreaSet();
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// The existance of scrolling properties depends on visibility through
// isScrollable() so ensure properties are updated if visibility changes.
SetNeedsPaintPropertyUpdate();
}
if (IsParentVisible()) {
ForAllChildViewsAndPlugins(
[](EmbeddedContentView& embedded_content_view) {
embedded_content_view.SetParentVisible(true);
});
}
}
}
void LocalFrameView::Hide() {
if (IsSelfVisible()) {
if (IsParentVisible()) {
ForAllChildViewsAndPlugins(
[](EmbeddedContentView& embedded_content_view) {
embedded_content_view.SetParentVisible(false);
});
}
SetSelfVisible(false);
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator())
scrolling_coordinator->FrameViewVisibilityDidChange();
SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
UpdateParentScrollableAreaSet();
if (RuntimeEnabledFeatures::SlimmingPaintInvalidationEnabled() &&
!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
// The existance of scrolling properties depends on visibility through
// isScrollable() so ensure properties are updated if visibility changes.
SetNeedsPaintPropertyUpdate();
}
}
}
int LocalFrameView::ViewportWidth() const {
int viewport_width = GetLayoutSize(kIncludeScrollbars).Width();
return AdjustForAbsoluteZoom(viewport_width, GetLayoutView());
}
ScrollableArea* LocalFrameView::GetScrollableArea() {
if (viewport_scrollable_area_)
return viewport_scrollable_area_.Get();
return LayoutViewportScrollableArea();
}
ScrollableArea* LocalFrameView::LayoutViewportScrollableArea() {
if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled())
return this;
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
return layout_view_item.IsNull() ? nullptr
: layout_view_item.GetScrollableArea();
}
RootFrameViewport* LocalFrameView::GetRootFrameViewport() {
return viewport_scrollable_area_.Get();
}
LayoutObject* LocalFrameView::ViewportLayoutObject() const {
if (Document* document = GetFrame().GetDocument()) {
if (Element* element = document->ViewportDefiningElement())
return element->GetLayoutObject();
}
return nullptr;
}
void LocalFrameView::CollectAnnotatedRegions(
LayoutObject& layout_object,
Vector<AnnotatedRegionValue>& regions) const {
// LayoutTexts don't have their own style, they just use their parent's style,
// so we don't want to include them.
if (layout_object.IsText())
return;
layout_object.AddAnnotatedRegions(regions);
for (LayoutObject* curr = layout_object.SlowFirstChild(); curr;
curr = curr->NextSibling())
CollectAnnotatedRegions(*curr, regions);
}
void LocalFrameView::UpdateViewportIntersectionsForSubtree(
DocumentLifecycle::LifecycleState target_state) {
// TODO(dcheng): Since LocalFrameView tree updates are deferred, FrameViews
// might still be in the LocalFrameView hierarchy even though the associated
// Document is already detached. Investigate if this check and a similar check
// in lifecycle updates are still needed when there are no more deferred
// LocalFrameView updates: https://crbug.com/561683
if (!GetFrame().GetDocument()->IsActive())
return;
if (target_state == DocumentLifecycle::kPaintClean) {
RecordDeferredLoadingStats();
if (!NeedsLayout()) {
// Notify javascript IntersectionObservers
if (GetFrame().GetDocument()->GetIntersectionObserverController()) {
GetFrame()
.GetDocument()
->GetIntersectionObserverController()
->ComputeTrackedIntersectionObservations();
}
}
}
// Don't throttle display:none frames (see updateRenderThrottlingStatus).
HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
if (hidden_for_throttling_ && owner_element &&
!owner_element->GetLayoutObject()) {
// No need to notify children because descendants of display:none frames
// should remain throttled.
UpdateRenderThrottlingStatus(hidden_for_throttling_, subtree_throttled_,
kDontForceThrottlingInvalidation,
kDontNotifyChildren);
}
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
child->View()->UpdateViewportIntersectionsForSubtree(target_state);
}
}
void LocalFrameView::UpdateRenderThrottlingStatusForTesting() {
visibility_observer_->DeliverObservationsForTesting();
}
void LocalFrameView::CrossOriginStatusChanged() {
// Cross-domain status is not stored as a dirty bit within LocalFrameView,
// so force-invalidate throttling status when it changes regardless of
// previous or new value.
UpdateRenderThrottlingStatus(hidden_for_throttling_, subtree_throttled_,
kForceThrottlingInvalidation);
}
void LocalFrameView::UpdateRenderThrottlingStatus(
bool hidden,
bool subtree_throttled,
ForceThrottlingInvalidationBehavior force_throttling_invalidation_behavior,
NotifyChildrenBehavior notify_children_behavior) {
TRACE_EVENT0("blink", "LocalFrameView::updateRenderThrottlingStatus");
DCHECK(!IsInPerformLayout());
DCHECK(!frame_->GetDocument() || !frame_->GetDocument()->InStyleRecalc());
bool was_throttled = CanThrottleRendering();
// Note that we disallow throttling of 0x0 and display:none frames because
// some sites use them to drive UI logic.
HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
hidden_for_throttling_ = hidden && !FrameRect().IsEmpty() &&
(owner_element && owner_element->GetLayoutObject());
subtree_throttled_ = subtree_throttled;
bool is_throttled = CanThrottleRendering();
bool became_unthrottled = was_throttled && !is_throttled;
// If this LocalFrameView became unthrottled or throttled, we must make sure
// all its children are notified synchronously. Otherwise we 1) might attempt
// to paint one of the children with an out-of-date layout before
// |updateRenderThrottlingStatus| has made it throttled or 2) fail to
// unthrottle a child whose parent is unthrottled by a later notification.
if (notify_children_behavior == kNotifyChildren &&
(was_throttled != is_throttled ||
force_throttling_invalidation_behavior ==
kForceThrottlingInvalidation)) {
ForAllChildLocalFrameViews([is_throttled](LocalFrameView& frame_view) {
frame_view.UpdateRenderThrottlingStatus(frame_view.hidden_for_throttling_,
is_throttled);
});
}
ScrollingCoordinator* scrolling_coordinator = this->GetScrollingCoordinator();
if (became_unthrottled ||
force_throttling_invalidation_behavior == kForceThrottlingInvalidation) {
// ScrollingCoordinator needs to update according to the new throttling
// status.
if (scrolling_coordinator)
scrolling_coordinator->NotifyGeometryChanged();
// Start ticking animation frames again if necessary.
if (GetPage())
GetPage()->Animator().ScheduleVisualUpdate(frame_.Get());
// Force a full repaint of this frame to ensure we are not left with a
// partially painted version of this frame's contents if we skipped
// painting them while the frame was throttled.
LayoutViewItem layout_view_item = this->GetLayoutViewItem();
if (!layout_view_item.IsNull())
layout_view_item.InvalidatePaintForViewAndCompositedLayers();
// Also need to update all paint properties that might be skipped while
// the frame was throttled.
SetSubtreeNeedsPaintPropertyUpdate();
}
bool has_handlers =
frame_->GetPage() &&
frame_->GetPage()->GetEventHandlerRegistry().HasEventHandlers(
EventHandlerRegistry::kTouchStartOrMoveEventBlocking);
if (was_throttled != CanThrottleRendering() && scrolling_coordinator &&
has_handlers)
scrolling_coordinator->TouchEventTargetRectsDidChange();
if (frame_->FrameScheduler()) {
frame_->FrameScheduler()->SetFrameVisible(!hidden_for_throttling_);
frame_->FrameScheduler()->SetCrossOrigin(frame_->IsCrossOriginSubframe());
}
#if DCHECK_IS_ON()
// Make sure we never have an unthrottled frame inside a throttled one.
LocalFrameView* parent = ParentFrameView();
while (parent) {
DCHECK(CanThrottleRendering() || !parent->CanThrottleRendering());
parent = parent->ParentFrameView();
}
#endif
}
void LocalFrameView::RecordDeferredLoadingStats() {
if (!GetFrame().GetDocument()->GetFrame() ||
!GetFrame().IsCrossOriginSubframe())
return;
LocalFrameView* parent = ParentFrameView();
if (!parent) {
HTMLFrameOwnerElement* element = GetFrame().DeprecatedLocalOwner();
// We would fall into an else block on some teardowns and other weird cases.
if (!element || !element->GetLayoutObject()) {
GetFrame().GetDocument()->RecordDeferredLoadReason(
WouldLoadReason::kNoParent);
}
return;
}
// Small inaccuracy: frames with origins that match the top level might be
// nested in a cross-origin frame. To keep code simpler, count such frames as
// WouldLoadVisible, even when their parent is offscreen.
WouldLoadReason why_parent_loaded = WouldLoadReason::kVisible;
if (parent->ParentFrameView() && parent->GetFrame().IsCrossOriginSubframe())
why_parent_loaded = parent->GetFrame().GetDocument()->DeferredLoadReason();
// If the parent wasn't loaded, the children won't be either.
if (why_parent_loaded == WouldLoadReason::kCreated)
return;
// These frames are never meant to be seen so we will need to load them.
if (FrameRect().IsEmpty() || FrameRect().MaxY() < 0 ||
FrameRect().MaxX() < 0) {
GetFrame().GetDocument()->RecordDeferredLoadReason(why_parent_loaded);
return;
}
IntRect parent_rect = parent->FrameRect();
// First clause: for this rough data collection we assume the user never
// scrolls right.
if (FrameRect().X() >= parent_rect.Width() || parent_rect.Height() <= 0)
return;
int this_frame_screens_away = 0;
// If an frame is created above the current scoll position, this logic counts
// it as visible.
if (FrameRect().Y() > parent->GetScrollOffset().Height()) {
this_frame_screens_away =
(FrameRect().Y() - parent->GetScrollOffset().Height()) /
parent_rect.Height();
}
DCHECK_GE(this_frame_screens_away, 0);
int parent_screens_away = 0;
if (why_parent_loaded <= WouldLoadReason::kVisible) {
parent_screens_away = static_cast<int>(WouldLoadReason::kVisible) -
static_cast<int>(why_parent_loaded);
}
int total_screens_away = this_frame_screens_away + parent_screens_away;
// We're collecting data for frames that are at most 3 screens away.
if (total_screens_away > 3)
return;
GetFrame().GetDocument()->RecordDeferredLoadReason(
static_cast<WouldLoadReason>(static_cast<int>(WouldLoadReason::kVisible) -
total_screens_away));
}
bool LocalFrameView::ShouldThrottleRendering() const {
return CanThrottleRendering() && frame_->GetDocument() &&
Lifecycle().ThrottlingAllowed();
}
bool LocalFrameView::CanThrottleRendering() const {
if (lifecycle_updates_throttled_)
return true;
if (!RuntimeEnabledFeatures::RenderingPipelineThrottlingEnabled())
return false;
if (subtree_throttled_)
return true;
// We only throttle hidden cross-origin frames. This is to avoid a situation
// where an ancestor frame directly depends on the pipeline timing of a
// descendant and breaks as a result of throttling. The rationale is that
// cross-origin frames must already communicate with asynchronous messages,
// so they should be able to tolerate some delay in receiving replies from a
// throttled peer.
return hidden_for_throttling_ && frame_->IsCrossOriginSubframe();
}
void LocalFrameView::BeginLifecycleUpdates() {
// Avoid pumping frames for the initially empty document.
if (!GetFrame().Loader().StateMachine()->CommittedFirstRealDocumentLoad())
return;
lifecycle_updates_throttled_ = false;
if (auto owner = GetFrame().OwnerLayoutItem())
owner.SetMayNeedPaintInvalidation();
SetupRenderThrottling();
UpdateRenderThrottlingStatus(hidden_for_throttling_, subtree_throttled_);
// The compositor will "defer commits" for the main frame until we
// explicitly request them.
if (GetFrame().IsMainFrame())
GetFrame().GetPage()->GetChromeClient().BeginLifecycleUpdates();
}
void LocalFrameView::SetInitialViewportSize(const IntSize& viewport_size) {
if (viewport_size == initial_viewport_size_)
return;
initial_viewport_size_ = viewport_size;
if (Document* document = frame_->GetDocument())
document->GetStyleEngine().InitialViewportChanged();
}
int LocalFrameView::InitialViewportWidth() const {
DCHECK(frame_->IsMainFrame());
return initial_viewport_size_.Width();
}
int LocalFrameView::InitialViewportHeight() const {
DCHECK(frame_->IsMainFrame());
return initial_viewport_size_.Height();
}
bool LocalFrameView::HasVisibleSlowRepaintViewportConstrainedObjects() const {
if (!ViewportConstrainedObjects())
return false;
for (const LayoutObject* layout_object : *ViewportConstrainedObjects()) {
DCHECK(layout_object->IsBoxModelObject() && layout_object->HasLayer());
DCHECK(layout_object->Style()->GetPosition() == EPosition::kFixed ||
layout_object->Style()->GetPosition() == EPosition::kSticky);
PaintLayer* layer = ToLayoutBoxModelObject(layout_object)->Layer();
// Whether the Layer sticks to the viewport is a tree-depenent
// property and our viewportConstrainedObjects collection is maintained
// with only LayoutObject-level information.
if (!layer->FixedToViewport() && !layer->SticksToScroller())
continue;
// If the whole subtree is invisible, there's no reason to scroll on
// the main thread because we don't need to generate invalidations
// for invisible content.
if (layer->SubtreeIsInvisible())
continue;
// We're only smart enough to scroll viewport-constrainted objects
// in the compositor if they have their own backing or they paint
// into a grouped back (which necessarily all have the same viewport
// constraints).
CompositingState compositing_state = layer->GetCompositingState();
if (compositing_state != kPaintsIntoOwnBacking &&
compositing_state != kPaintsIntoGroupedBacking)
return true;
}
return false;
}
void LocalFrameView::UpdateSubFrameScrollOnMainReason(
const Frame& frame,
MainThreadScrollingReasons parent_reason) {
MainThreadScrollingReasons reasons = parent_reason;
if (!GetPage()->GetSettings().GetThreadedScrollingEnabled())
reasons |= MainThreadScrollingReason::kThreadedScrollingDisabled;
if (!frame.IsLocalFrame())
return;
LocalFrameView& frame_view = *ToLocalFrame(frame).View();
if (frame_view.ShouldThrottleRendering())
return;
reasons |= frame_view.MainThreadScrollingReasonsPerFrame();
if (GraphicsLayer* layer_for_scrolling = ToLocalFrame(frame)
.View()
->LayoutViewportScrollableArea()
->LayerForScrolling()) {
if (WebLayer* platform_layer_for_scrolling =
layer_for_scrolling->PlatformLayer()) {
if (reasons) {
platform_layer_for_scrolling->AddMainThreadScrollingReasons(reasons);
} else {
// Clear all main thread scrolling reasons except the one that's set
// if there is a running scroll animation.
platform_layer_for_scrolling->ClearMainThreadScrollingReasons(
~MainThreadScrollingReason::kHandlingScrollFromMainThread);
}
}
}
Frame* child = frame.Tree().FirstChild();
while (child) {
UpdateSubFrameScrollOnMainReason(*child, reasons);
child = child->Tree().NextSibling();
}
if (frame.IsMainFrame())
main_thread_scrolling_reasons_ = reasons;
DCHECK(!MainThreadScrollingReason::HasNonCompositedScrollReasons(
main_thread_scrolling_reasons_));
}
MainThreadScrollingReasons LocalFrameView::MainThreadScrollingReasonsPerFrame()
const {
MainThreadScrollingReasons reasons =
static_cast<MainThreadScrollingReasons>(0);
if (ShouldThrottleRendering())
return reasons;
if (HasBackgroundAttachmentFixedObjects())
reasons |= MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
ScrollingReasons scrolling_reasons = GetScrollingReasons();
const bool may_be_scrolled_by_input = (scrolling_reasons == kScrollable);
const bool may_be_scrolled_by_script =
may_be_scrolled_by_input ||
(scrolling_reasons == kNotScrollableExplicitlyDisabled);
// TODO(awoloszyn) Currently crbug.com/304810 will let certain
// overflow:hidden elements scroll on the compositor thread, so we should
// not let this move there path as an optimization, when we have
// slow-repaint elements.
if (may_be_scrolled_by_script &&
HasVisibleSlowRepaintViewportConstrainedObjects()) {
reasons |=
MainThreadScrollingReason::kHasNonLayerViewportConstrainedObjects;
}
return reasons;
}
MainThreadScrollingReasons LocalFrameView::GetMainThreadScrollingReasons()
const {
MainThreadScrollingReasons reasons =
static_cast<MainThreadScrollingReasons>(0);
if (!GetPage()->GetSettings().GetThreadedScrollingEnabled())
reasons |= MainThreadScrollingReason::kThreadedScrollingDisabled;
if (!GetPage()->MainFrame()->IsLocalFrame())
return reasons;
// TODO(alexmos,kenrb): For OOPIF, local roots that are different from
// the main frame can't be used in the calculation, since they use
// different compositors with unrelated state, which breaks some of the
// calculations below.
if (&frame_->LocalFrameRoot() != GetPage()->MainFrame())
return reasons;
// Walk the tree to the root. Use the gathered reasons to determine
// whether the target frame should be scrolled on main thread regardless
// other subframes on the same page.
for (Frame* frame = frame_; frame; frame = frame->Tree().Parent()) {
if (!frame->IsLocalFrame())
continue;
reasons |=
ToLocalFrame(frame)->View()->MainThreadScrollingReasonsPerFrame();
}
DCHECK(!MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons));
return reasons;
}
String LocalFrameView::MainThreadScrollingReasonsAsText() const {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
DCHECK(Lifecycle().GetState() >= DocumentLifecycle::kPrePaintClean);
// Slimming paint v2 stores main thread scrolling reasons on property
// trees instead of in |m_mainThreadScrollingReasons|.
MainThreadScrollingReasons reasons = 0;
if (const auto* scroll_translation = this->ScrollTranslation()) {
reasons |=
scroll_translation->ScrollNode()->GetMainThreadScrollingReasons();
}
return String(
MainThreadScrollingReason::mainThreadScrollingReasonsAsText(reasons)
.c_str());
}
DCHECK(Lifecycle().GetState() >= DocumentLifecycle::kCompositingClean);
if (LayerForScrolling() && LayerForScrolling()->PlatformLayer()) {
String result(
MainThreadScrollingReason::mainThreadScrollingReasonsAsText(
LayerForScrolling()->PlatformLayer()->MainThreadScrollingReasons())
.c_str());
return result;
}
String result(MainThreadScrollingReason::mainThreadScrollingReasonsAsText(
main_thread_scrolling_reasons_)
.c_str());
return result;
}
IntRect LocalFrameView::RemoteViewportIntersection() {
IntRect intersection(GetFrame().RemoteViewportIntersection());
intersection.Move(ScrollOffsetInt());
return intersection;
}
void LocalFrameView::MapQuadToAncestorFrameIncludingScrollOffset(
LayoutRect& rect,
const LayoutObject* descendant,
const LayoutView* ancestor,
MapCoordinatesFlags mode) {
FloatQuad mapped_quad = descendant->LocalToAncestorQuad(
FloatQuad(FloatRect(rect)), ancestor, mode);
rect = LayoutRect(mapped_quad.BoundingBox());
// localToAncestorQuad accounts for scroll offset if it encounters a remote
// frame in the ancestor chain, otherwise it needs to be added explicitly.
if (GetFrame().LocalFrameRoot() == GetFrame().Tree().Top() ||
(ancestor &&
ancestor->GetFrame()->LocalFrameRoot() == GetFrame().LocalFrameRoot())) {
LocalFrameView* ancestor_view =
(ancestor ? ancestor->GetFrameView()
: ToLocalFrame(GetFrame().Tree().Top()).View());
LayoutSize scroll_position = LayoutSize(ancestor_view->GetScrollOffset());
rect.Move(-scroll_position);
}
}
bool LocalFrameView::MapToVisualRectInTopFrameSpace(LayoutRect& rect) {
// This is the top-level frame, so no mapping necessary.
if (frame_->IsMainFrame())
return true;
LayoutRect viewport_intersection_rect(RemoteViewportIntersection());
rect.Intersect(viewport_intersection_rect);
if (rect.IsEmpty())
return false;
return true;
}
void LocalFrameView::ApplyTransformForTopFrameSpace(
TransformState& transform_state) {
// This is the top-level frame, so no mapping necessary.
if (frame_->IsMainFrame())
return;
LayoutRect viewport_intersection_rect(RemoteViewportIntersection());
transform_state.Move(LayoutSize(-viewport_intersection_rect.X(),
-viewport_intersection_rect.Y()));
}
void LocalFrameView::SetAnimationTimeline(
std::unique_ptr<CompositorAnimationTimeline> timeline) {
animation_timeline_ = std::move(timeline);
}
void LocalFrameView::SetAnimationHost(
std::unique_ptr<CompositorAnimationHost> host) {
animation_host_ = std::move(host);
}
LayoutUnit LocalFrameView::CaretWidth() const {
return LayoutUnit(GetChromeClient()->WindowToViewportScalar(1));
}
} // namespace blink