blob: 318b18e7e568c7c1492dcf4b5991c1d05a8ff22a [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 "third_party/blink/renderer/core/frame/local_frame_view.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/public/mojom/page/page_visibility_state.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_display_item_list.h"
#include "third_party/blink/public/platform/web_rect.h"
#include "third_party/blink/public/platform/web_scroll_into_view_params.h"
#include "third_party/blink/renderer/core/animation/document_animations.h"
#include "third_party/blink/renderer/core/css/font_face_set_document.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/ax_object_cache.h"
#include "third_party/blink/renderer/core/dom/element_visibility_observer.h"
#include "third_party/blink/renderer/core/editing/drag_caret.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/editing/rendered_position.h"
#include "third_party/blink/renderer/core/events/error_event.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/frame/browser_controls.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
#include "third_party/blink/renderer/core/frame/location.h"
#include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
#include "third_party/blink/renderer/core/frame/remote_frame.h"
#include "third_party/blink/renderer/core/frame/remote_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
#include "third_party/blink/renderer/core/html/html_frame_element.h"
#include "third_party/blink/renderer/core/html/html_plugin_element.h"
#include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/inspector/InspectorTraceEvents.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_controller.h"
#include "third_party/blink/renderer/core/intersection_observer/intersection_observer_init.h"
#include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_counter.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
#include "third_party/blink/renderer/core/layout/layout_scrollbar.h"
#include "third_party/blink/renderer/core/layout/layout_scrollbar_part.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/layout/traced_layout_object.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/media_type_names.h"
#include "third_party/blink/renderer/core/page/autoscroll_controller.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/frame_tree.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/print_context.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h"
#include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/paint/block_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_selection.h"
#include "third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/frame_painter.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_timing.h"
#include "third_party/blink/renderer/core/paint/pre_paint_tree_walk.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer_controller.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/svg/svg_svg_element.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/geometry/double_rect.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/geometry/transform_state.h"
#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer_debug_info.h"
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/language.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/platform_chrome_client.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scroll/scroll_alignment.h"
#include "third_party/blink/renderer/platform/scroll/scroll_animator_base.h"
#include "third_party/blink/renderer/platform/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/platform/text/text_stream.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/time.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 blink {
namespace {
// Page dimensions in pixels at 72 DPI.
constexpr int kA4PortraitPageWidth = 595;
constexpr int kA4PortraitPageHeight = 842;
constexpr int kLetterPortraitPageWidth = 612;
constexpr int kLetterPortraitPageHeight = 792;
// Changing these values requires changing the names generated in
// EnsureUkmTimeAggregator().
enum class UkmMetricNames {
kCompositing,
kIntersectionObservation,
kPaint,
kPrePaint,
kStyleAndLayout,
kCount
};
} // namespace
// Defines an UMA and a UKM, recorded in microseconds equal to the duration of
// the current lexical scope after declaration of the macro. Example usage:
//
// void LocalFrameView::DoExpensiveThing() {
// SCOPED_UMA_AND_UKM_TIMER(UmaName, kUkmEnumName);
// // Do computation of expensive thing
//
// }
//
// |uma_name| should be the full name of an UMA defined
// in histograms.xml. |ukm_enum| should be an entry in UkmMetricNames
// (which in turn come from ukm.xml).
#define SCOPED_UMA_AND_UKM_TIMER(uma_name, ukm_enum) \
DEFINE_STATIC_LOCAL_IMPL(CustomCountHistogram, scoped_uma_counter, \
(uma_name, 0, 10000000, 50), false); \
auto scoped_ukm_uma_timer = EnsureUkmTimeAggregator().GetScopedTimer( \
static_cast<size_t>(ukm_enum), &scoped_uma_counter);
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_(frame.GetTaskRunner(TaskType::kUnspecedTimer),
this,
&LocalFrameView::PostLayoutTimerFired),
update_plugins_timer_(frame.GetTaskRunner(TaskType::kUnspecedTimer),
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_(frame.GetTaskRunner(TaskType::kUnspecedTimer),
this,
&LocalFrameView::DidScrollTimerFired),
needs_update_geometries_(false),
horizontal_scrollbar_mode_(kScrollbarAuto),
vertical_scrollbar_mode_(kScrollbarAuto),
scrollbars_suppressed_(false),
root_layer_did_scroll_(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),
past_layout_lifecycle_update_(false),
scroll_anchor_(this),
scrollbar_manager_(*this),
needs_scrollbars_update_(false),
suppress_adjust_view_size_(false),
allows_layout_invalidation_after_layout_clean_(true),
needs_intersection_observation_(false),
needs_forced_compositing_update_(false),
needs_focus_on_fragment_(false),
main_thread_scrolling_reasons_(0),
paint_frame_count_(0),
unique_id_(NewUniqueObjectId()) {
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
}
void LocalFrameView::Trace(blink::Visitor* visitor) {
visitor->Trace(frame_);
visitor->Trace(parent_);
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_ =
base::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();
ukm_time_aggregator_.reset();
}
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.
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::BindRepeating(
[](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();
ukm_time_aggregator_.reset();
#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::SnapAfterScrollbarDragging(
ScrollbarOrientation orientation) {
SnapCoordinator* snap_coordinator =
frame_->GetDocument()->GetSnapCoordinator();
if (!snap_coordinator)
return;
snap_coordinator->PerformSnapping(*GetLayoutBox(),
orientation == kHorizontalScrollbar,
orientation == kVerticalScrollbar);
}
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_;
}
bool LocalFrameView::LifecycleUpdatesActive() const {
return !lifecycle_updates_throttled_;
}
void LocalFrameView::InvalidateRect(const IntRect& rect) {
auto* layout_object = frame_->OwnerLayoutObject();
if (!layout_object)
return;
IntRect paint_invalidation_rect = rect;
paint_invalidation_rect.Move(
(layout_object->BorderLeft() + layout_object->PaddingLeft()).ToInt(),
(layout_object->BorderTop() + layout_object->PaddingTop()).ToInt());
layout_object->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::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 = GetLayoutView())
layout_view->SetMayNeedPaintInvalidation();
if (width_changed || height_changed) {
ViewportSizeChanged(width_changed, height_changed);
if (frame_->IsMainFrame())
frame_->GetPage()->GetVisualViewport().MainFrameDidChangeSize();
GetFrame().Loader().RestoreScrollPositionAndViewState();
}
}
IntPoint LocalFrameView::Location() const {
IntPoint location(frame_rect_.Location());
// As an optimization, we don't include the root layer's scroll offset in the
// frame rect. As a result, we don't need to recalculate the frame rect every
// time the root layer scrolls, but we need to add it in here.
LayoutEmbeddedContent* owner = frame_->OwnerLayoutObject();
if (owner) {
LayoutView* owner_layout_view = owner->View();
DCHECK(owner_layout_view);
if (owner_layout_view->HasOverflowClip())
location.Move(-owner_layout_view->ScrolledContentOffset());
}
return location;
}
Page* LocalFrameView::GetPage() const {
return GetFrame().GetPage();
}
LayoutView* LocalFrameView::GetLayoutView() const {
return GetFrame().ContentLayoutObject();
}
ScrollingCoordinator* LocalFrameView::GetScrollingCoordinator() const {
Page* p = GetPage();
return p ? p->GetScrollingCoordinator() : nullptr;
}
ScrollingCoordinatorContext* LocalFrameView::GetScrollingContext() const {
LocalFrame* root = &GetFrame().LocalFrameRoot();
if (GetFrame() != root)
return root->View()->GetScrollingContext();
if (!scrolling_context_)
scrolling_context_.reset(new ScrollingCoordinatorContext());
return scrolling_context_.get();
}
CompositorAnimationHost* LocalFrameView::GetCompositorAnimationHost() const {
if (GetScrollingContext()->GetCompositorAnimationHost())
return GetScrollingContext()->GetCompositorAnimationHost();
if (!GetFrame().LocalFrameRoot().IsMainFrame())
return nullptr;
// TODO(kenrb): Compositor animation host and timeline for the main frame
// still live on ScrollingCoordinator. https://crbug.com/680606.
ScrollingCoordinator* c = GetScrollingCoordinator();
return c ? c->GetCompositorAnimationHost() : nullptr;
}
CompositorAnimationTimeline* LocalFrameView::GetCompositorAnimationTimeline()
const {
if (GetScrollingContext()->GetCompositorAnimationTimeline())
return GetScrollingContext()->GetCompositorAnimationTimeline();
if (!GetFrame().LocalFrameRoot().IsMainFrame())
return nullptr;
// TODO(kenrb): Compositor animation host and timeline for the main frame
// still live on ScrollingCoordinator. https://crbug.com/680606.
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;
}
scoped_refptr<base::SingleThreadTaskRunner> LocalFrameView::GetTimerTaskRunner()
const {
return frame_->GetTaskRunner(TaskType::kUnspecedTimer);
}
void LocalFrameView::SetCanHaveScrollbars(bool can_have_scrollbars) {
can_have_scrollbars_ = can_have_scrollbars;
ScrollbarMode new_vertical_mode = EffectiveVerticalScrollbarMode();
if (can_have_scrollbars && new_vertical_mode == kScrollbarAlwaysOff)
new_vertical_mode = kScrollbarAuto;
else if (!can_have_scrollbars)
new_vertical_mode = kScrollbarAlwaysOff;
ScrollbarMode new_horizontal_mode = EffectiveHorizontalScrollbarMode();
if (can_have_scrollbars && new_horizontal_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() : nullptr;
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() : nullptr;
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::SetLayoutOverflowSize(const IntSize& size) {
if (size == layout_overflow_size_)
return;
layout_overflow_size_ = size;
needs_scrollbars_update_ = true;
ScrollableArea::ContentsResized();
Page* page = GetFrame().GetPage();
if (!page)
return;
page->GetChromeClient().ContentsSizeChanged(frame_.Get(), size);
}
void LocalFrameView::AdjustViewSize() {
if (suppress_adjust_view_size_)
return;
LayoutView* layout_view = GetLayoutView();
if (!layout_view)
return;
DCHECK_EQ(frame_->View(), this);
const IntRect rect = layout_view->DocumentRect();
const IntSize& size = rect.Size();
if (!RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
const IntPoint origin(-rect.X(), -rect.Y());
if (ScrollOrigin() != origin)
SetScrollOrigin(origin);
}
SetLayoutOverflowSize(size);
}
void LocalFrameView::AdjustViewSizeAndLayout() {
AdjustViewSize();
if (NeedsLayout()) {
AutoReset<bool> suppress_adjust_view_size(&suppress_adjust_view_size_,
true);
UpdateLayout();
}
}
void LocalFrameView::UpdateAcceleratedCompositingSettings() {
if (auto* layout_view = GetLayoutView())
layout_view->Compositor()->UpdateAcceleratedCompositingSettings();
}
void LocalFrameView::RecalcOverflowAfterStyleChange() {
auto* layout_view = this->GetLayoutView();
CHECK(layout_view);
if (!layout_view->NeedsOverflowRecalcAfterStyleChange())
return;
layout_view->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()) {
GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
}
IntRect document_rect = layout_view->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->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();
}
void LocalFrameView::UpdateCountersAfterStyleChange() {
auto* layout_view = GetLayoutView();
DCHECK(layout_view);
layout_view->UpdateCounters();
}
bool LocalFrameView::ShouldScrollOnMainThread() const {
if (GetMainThreadScrollingReasons())
return true;
return ScrollableArea::ShouldScrollOnMainThread();
}
GraphicsLayer* LocalFrameView::LayerForScrolling() const {
auto* layout_view = GetLayoutView();
if (!layout_view)
return nullptr;
return layout_view->Compositor()->FrameScrollLayer();
}
GraphicsLayer* LocalFrameView::LayerForHorizontalScrollbar() const {
auto* layout_view = GetLayoutView();
if (!layout_view)
return nullptr;
return layout_view->Compositor()->LayerForHorizontalScrollbar();
}
GraphicsLayer* LocalFrameView::LayerForVerticalScrollbar() const {
auto* layout_view = GetLayoutView();
if (!layout_view)
return nullptr;
return layout_view->Compositor()->LayerForVerticalScrollbar();
}
GraphicsLayer* LocalFrameView::LayerForScrollCorner() const {
auto* layout_view = GetLayoutView();
if (!layout_view)
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;
auto* frame_owner_layout_object = frame_->OwnerLayoutObject();
return frame_owner_layout_object &&
frame_owner_layout_object->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);
}
}
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() &&
GetLayoutBox()->Style()->OverflowAnchor() != EOverflowAnchor::kNone &&
!frame_->GetDocument()->FinishingOrIsPrinting();
}
void LocalFrameView::LayoutFromRootObject(LayoutObject& root) {
LayoutState layout_state(root);
if (!root.IsBoxModelObject()) {
root.UpdateLayout();
} else {
// Laying out the root may change its visual overflow. If so, that
// visual overflow needs to propagate to its containing block.
LayoutBoxModelObject& box_object = ToLayoutBoxModelObject(root);
LayoutRect previous_visual_overflow_rect = box_object.VisualOverflowRect();
box_object.UpdateLayout();
if (box_object.VisualOverflowRect() != previous_visual_overflow_rect) {
box_object.SetNeedsOverflowRecalcAfterStyleChange();
RecalcOverflowAfterStyleChange();
}
}
}
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_ = std::make_unique<LayoutAnalyzer>();
analyzer_->Reset();
}
std::unique_ptr<TracedValue> LocalFrameView::AnalyzerCounters() {
if (!analyzer_)
return TracedValue::Create();
std::unique_ptr<TracedValue> value = analyzer_->ToTracedValue();
value->SetString("host", GetLayoutView()->GetDocument().location()->host());
value->SetString(
"frame",
String::Format("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(frame_.Get())));
value->SetInteger("contentsHeightAfterLayout",
GetLayoutView()->DocumentRect().Height());
value->SetInteger("visibleHeight", VisibleHeight());
value->SetInteger("approximateBlankCharacterCount",
FontFaceSetDocument::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 = GetLayoutView()->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.
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 (LayoutObject* container = root->Container())
container->SetMayNeedPaintInvalidation();
}
layout_subtree_root_list_.Clear();
} else {
if (HasOrthogonalWritingModeRoots() &&
!RuntimeEnabledFeatures::LayoutNGEnabled())
LayoutOrthogonalWritingModeRoots();
GetLayoutView()->UpdateLayout();
}
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,
GetLayoutView()->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(TimeDelta(), 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");
RUNTIME_CALL_TIMER_SCOPE(V8PerIsolateData::MainThreadIsolate(),
RuntimeCallStats::CounterId::kUpdateLayout);
// The actual call to UpdateGeometries is in PerformPostLayoutTasks.
SetNeedsUpdateGeometries();
if (auto_size_info_)
auto_size_info_->AutoSizeIfNeeded();
has_pending_layout_ = false;
Document* document = frame_->GetDocument();
TRACE_EVENT_BEGIN1("devtools.timeline", "Layout", "beginData",
InspectorLayoutEvent::BeginData(this));
probe::UpdateLayout probe(document);
PerformPreLayoutTasks();
VisualViewport& visual_viewport = frame_->GetPage()->GetVisualViewport();
DoubleSize viewport_size(visual_viewport.VisibleWidthCSSPx(),
visual_viewport.VisibleHeightCSSPx());
// 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_++;
// 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 (GetLayoutView()->NeedsLayout())
ClearLayoutSubtreeRootsAndMarkContainingBlocks();
GetLayoutView()->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 = EffectiveHorizontalScrollbarMode();
ScrollbarMode current_v_mode = EffectiveVerticalScrollbarMode();
if (first_layout_) {
SetScrollbarsSuppressed(true);
first_layout_ = false;
last_viewport_size_ = GetLayoutSize(kIncludeScrollbars);
last_zoom_factor_ = GetLayoutView()->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);
if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
GetLayoutView()
->GetScrollableArea()
->ForceVerticalScrollbarForFirstLayout();
}
}
// 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()
: nullptr;
LayoutBox* body_layout_object = root_layout_object && document->body()
? document->body()->GetLayoutBox()
: nullptr;
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();
DocumentLifecycle::Scope lifecycle_scope(Lifecycle(),
DocumentLifecycle::kLayoutClean);
frame_timing_requests_dirty_ = true;
// FIXME: Could find the common ancestor layer of all dirty subtrees and
// mark from there. crbug.com/462719
GetLayoutView()->EnclosingLayer()->UpdateLayerPositionsAfterLayout();
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"), "LayoutTree",
this, TracedLayoutObject::Create(*GetLayoutView(), true));
GetLayoutView()->Compositor()->DidLayout();
layout_count_++;
if (AXObjectCache* cache = document->GetOrCreateAXObjectCache()) {
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
if (frame_->IsMainFrame() &&
RuntimeEnabledFeatures::VisualViewportAPIEnabled()) {
// Scrollbars changing state can cause a visual viewport size change.
DoubleSize new_viewport_size(visual_viewport.VisibleWidthCSSPx(),
visual_viewport.VisibleHeightCSSPx());
if (new_viewport_size != viewport_size)
frame_->GetDocument()->EnqueueVisualViewportResizeEvent();
}
} // ScriptForbiddenScope
GetFrame().GetDocument()->LayoutUpdated();
CheckDoesNotNeedLayout();
}
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();
if (auto* layout_view = GetLayoutView())
layout_view->SetSubtreeNeedsPaintPropertyUpdate();
}
FloatSize LocalFrameView::ViewportSizeForViewportUnits() const {
float zoom = 1;
if (!frame_->GetDocument() || !frame_->GetDocument()->Printing())
zoom = GetFrame().PageZoomFactor();
auto* layout_view = GetLayoutView();
if (!layout_view)
return FloatSize();
FloatSize layout_size;
layout_size.SetWidth(layout_view->ViewWidth(kIncludeScrollbars) / zoom);
layout_size.SetHeight(layout_view->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.TotalHeight() / 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 {
auto* layout_view = this->GetLayoutView();
if (!layout_view)
return nullptr;
LayoutObject* first_child = layout_view->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;
}
bool LocalFrameView::GetIntrinsicSizingInfo(
IntrinsicSizingInfo& intrinsic_sizing_info) const {
if (LayoutReplaced* content_layout_object = EmbeddedReplacedContent()) {
content_layout_object->ComputeIntrinsicSizingInfo(intrinsic_sizing_info);
return true;
}
return false;
}
bool LocalFrameView::HasIntrinsicSizingInfo() const {
return EmbeddedReplacedContent();
}
void LocalFrameView::UpdateGeometry() {
LayoutEmbeddedContent* layout = frame_->OwnerLayoutObject();
if (!layout)
return;
bool did_need_layout = NeedsLayout();
LayoutRect new_frame = layout->ReplacedContentRect();
DCHECK(new_frame.Size() == RoundedIntSize(new_frame.Size()));
bool bounds_will_change = LayoutSize(Size()) != new_frame.Size();
// If frame bounds are changing mark the view for layout. Also check the
// frame's page to make sure that the frame isn't in the process of being
// destroyed. If iframe scrollbars needs reconstruction from native to custom
// scrollbar, then also we need to layout the frameview.
if (bounds_will_change || NeedsScrollbarReconstruction())
SetNeedsLayout();
layout->UpdateGeometry(*this);
// If view needs layout, either because bounds have changed or possibly
// indicating content size is wrong, we have to do a layout to set the right
// LocalFrameView size.
if (NeedsLayout())
UpdateLayout();
if (!did_need_layout && !ShouldThrottleRendering())
CheckDoesNotNeedLayout();
}
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 {
auto* layout_view = this->GetLayoutView();
return layout_view &&
layout_view->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.
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.
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_ =
std::make_unique<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());
if (frame_->GetDocument() &&
frame_->GetDocument()->Lifecycle().LifecyclePostponed())
return;
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->Layer()->UpdateSize();
layout_view->GetScrollableArea()
->ClampScrollOffsetAfterOverflowChange();
} else {
AdjustScrollOffsetFromUpdateScrollbars();
}
}
if (layout_view->UsesCompositing()) {
if (root_layer_scrolling_enabled) {
layout_view->Layer()->SetNeedsCompositingInputsUpdate();
SetNeedsPaintPropertyUpdate();
} else {
layout_view->Compositor()->FrameViewDidChangeSize();
}
}
}
if (GetFrame().GetDocument())
GetFrame().GetDocument()->GetRootScrollerController().DidResizeFrameView();
ShowOverlayScrollbars();
if (GetLayoutView() && frame_->IsMainFrame() &&
frame_->GetPage()->GetBrowserControls().TotalHeight()) {
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.
if (GetLayoutView()->Compositor()->NeedsFixedRootBackgroundLayer()) {
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.
auto* layout_view = GetLayoutView();
DCHECK(layout_view);
if (layout_view->DocumentRect().Height() <
layout_view->ViewRect().Height()) {
layout_view->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() != mojom::PageVisibilityState::kHidden &&
!frame_->GetEventHandler().IsMousePositionUnknown() &&
page->GetFocusController().IsActive();
}
void LocalFrameView::NotifyFrameRectsChangedIfNeededRecursive() {
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
frame_view.NotifyFrameRectsChangedIfNeeded();
});
}
void LocalFrameView::ScrollContentsIfNeededRecursive() {
ForAllNonThrottledLocalFrameViews(
[](LocalFrameView& frame_view) { frame_view.ScrollContentsIfNeeded(); });
}
void LocalFrameView::InvalidateBackgroundAttachmentFixedDescendants(
const LayoutObject& object) {
for (const auto& layout_object : background_attachment_fixed_objects_) {
if (object != GetLayoutView() && !layout_object->IsDescendantOf(&object))
continue;
bool needs_scrolling_contents_layer_invalidation = false;
if (layout_object->HasLayer()) {
PaintLayer* layer = ToLayoutBoxModelObject(layout_object)->Layer();
if (layer->GetBackgroundPaintLocation() ==
kBackgroundPaintInScrollingContents) {
needs_scrolling_contents_layer_invalidation = true;
}
}
if (needs_scrolling_contents_layer_invalidation) {
// BoxPaintInvalidator doesn't want to invalidate scrolling contents layer
// whenever the LayoutObject is marked ShouldDoFullPaintInvalidation() -
// see crrev.com/433093. (LayoutObject doesn't track full-invalidation
// reasons independently, so it's not safe for BoxPaintInvalidator to have
// special handling of kBackground.)
layout_object->SetBackgroundChangedSinceLastPaintInvalidation();
} else {
layout_object->SetShouldDoFullPaintInvalidation(
PaintInvalidationReason::kBackground);
}
}
}
bool LocalFrameView::HasBackgroundAttachmentFixedDescendants(
const LayoutObject& object) const {
if (object == GetLayoutView())
return !background_attachment_fixed_objects_.IsEmpty();
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;
DCHECK(layout_object->Style()->HasViewportConstrainedPosition() ||
layout_object->Style()->HasStickyConstrainedPosition());
DCHECK(layout_object->HasLayer());
PaintLayer* layer = ToLayoutBoxModelObject(layout_object)->Layer();
if (layer->IsPaintInvalidationContainer())
continue;
// If the layer has no visible content, then we shouldn't invalidate; but
// if we're not compositing-inputs-clean, then we can't query
// layer->SubtreeIsInvisible() here.
layout_object->SetMayNeedPaintInvalidationSubtree();
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled() &&
RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
!layer->NeedsRepaint()) {
// Paint properties of the layer relative to its containing graphics
// layer may change if the paint properties escape the graphics layer's
// property state. Need to check raster invalidation for relative paint
// property changes.
if (auto* paint_invalidation_layer =
layer->EnclosingLayerForPaintInvalidation()) {
auto* mapping = paint_invalidation_layer->GetCompositedLayerMapping();
if (!mapping)
mapping = paint_invalidation_layer->GroupedMapping();
if (mapping)
mapping->SetNeedsCheckRasterInvalidation();
}
}
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;
InvalidateBackgroundAttachmentFixedDescendants(*GetLayoutView());
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.
auto* layout_view = GetLayoutView();
DCHECK(layout_view);
if (ContentsInCompositedLayer()) {
layout_view->Layer()->GetCompositedLayerMapping()->SetContentsNeedDisplay();
} else {
layout_view
->SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
}
if (ContentsInCompositedLayer()) {
IntRect update_rect = VisibleContentRect();
layout_view->InvalidatePaintRectangle(LayoutRect(update_rect));
}
}
void LocalFrameView::RestoreScrollbar() {
SetScrollbarsSuppressed(false);
}
bool LocalFrameView::RestoreScrollAnchor(
const SerializedAnchor& serialized_anchor) {
return ShouldPerformScrollAnchoring() &&
scroll_anchor_.RestoreAnchor(serialized_anchor);
}
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;
UseCounter::Count(&GetFrame(), WebFeature::kScrollToFragmentRequested);
// Try the raw fragment for HTML documents, but skip it for `svgView()`:
String fragment_identifier = url.FragmentIdentifier();
if (!frame_->GetDocument()->IsSVGDocument() &&
ProcessUrlFragmentHelper(fragment_identifier, behavior)) {
UseCounter::Count(&GetFrame(), WebFeature::kScrollToFragmentSucceedWithRaw);
return;
}
// Try again after decoding the fragment.
if (frame_->GetDocument()->Encoding().IsValid()) {
DecodeURLResult decode_result;
if (ProcessUrlFragmentHelper(
DecodeURLEscapeSequences(fragment_identifier, &decode_result),
behavior)) {
switch (decode_result) {
case DecodeURLResult::kAsciiOnly:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentSucceedWithASCII);
break;
case DecodeURLResult::kUTF8:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentSucceedWithUTF8);
break;
case DecodeURLResult::kIsomorphic:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentSucceedWithIsomorphic);
break;
case DecodeURLResult::kMixed:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentSucceedWithMixed);
break;
}
} else {
switch (decode_result) {
case DecodeURLResult::kAsciiOnly:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentFailWithASCII);
break;
case DecodeURLResult::kUTF8:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentFailWithUTF8);
break;
case DecodeURLResult::kIsomorphic:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentFailWithIsomorphic);
break;
case DecodeURLResult::kMixed:
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentFailWithMixed);
break;
}
}
} else {
UseCounter::Count(&GetFrame(),
WebFeature::kScrollToFragmentFailWithInvalidEncoding);
}
}
bool LocalFrameView::ProcessUrlFragmentHelper(const String& name,
UrlFragmentBehavior behavior) {
DCHECK(frame_->GetDocument());
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 =
ToSVGSVGElementOrNull(frame_->GetDocument()->documentElement())) {
svg->SetupInitialView(name, anchor_node);
if (!anchor_node)
return false;
}
// If this is not the top-level frame, then don't scroll to the
// anchor position.
if (!frame_->IsMainFrame())
return false;
}
// 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 == kUrlFragmentDontScroll)
return true;
if (!anchor_node) {
fragment_anchor_ = frame_->GetDocument();
needs_focus_on_fragment_ = false;
} else {
fragment_anchor_ = anchor_node;
needs_focus_on_fragment_ = true;
}
// If rendering is blocked, we'll necessarily have a layout to kick off the
// scroll and focus.
if (frame_->GetDocument()->IsRenderingReady()) {
frame_->GetDocument()->UpdateStyleAndLayoutTree();
// If layout is needed, we will scroll in performPostLayoutTasks. Otherwise,
// scroll and focus immediately.
if (NeedsLayout())
UpdateLayout();
else
ScrollAndFocusFragmentAnchor();
}
return true;
}
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());
if (frame_->GetDocument() &&
frame_->GetDocument()->Lifecycle().LifecyclePostponed())
return;
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()->GetLayoutView())
frame_->GetDocument()->Fetcher()->UpdateAllImageResourcePriorities();
GetFrame().Loader().SaveScrollAnchor();
}
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;
const 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();
SetNeedsUpdateGeometries();
}
}
// 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_) {
UpdateGeometriesIfNeeded();
if (auto* layout_view = this->GetLayoutView())
layout_view->Layer()->SetNeedsCompositingInputsUpdate();
}
}
static CompositedSelection ComputeCompositedSelection(LocalFrame& frame) {
if (!frame.View() || frame.View()->ShouldThrottleRendering())
return {};
return RenderedPosition::ComputeCompositedSelection(frame.Selection());
}
void LocalFrameView::UpdateCompositedSelectionIfNeeded() {
if (!RuntimeEnabledFeatures::CompositedSelectionUpdateEnabled())
return;
TRACE_EVENT0("blink", "LocalFrameView::updateCompositedSelectionIfNeeded");
Page* page = GetFrame().GetPage();
DCHECK(page);
LocalFrame* focused_frame = page->GetFocusController().FocusedFrame();
LocalFrame* local_frame =
(focused_frame &&
(focused_frame->LocalFrameRoot() == frame_->LocalFrameRoot()))
? focused_frame
: nullptr;
if (local_frame) {
const CompositedSelection& selection =
ComputeCompositedSelection(*local_frame);
if (selection.type != kNoSelection) {
page->GetChromeClient().UpdateCompositedSelection(local_frame, selection);
return;
}
}
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();
}
DCHECK(local_frame);
page->GetChromeClient().ClearCompositedSelection(local_frame);
}
void LocalFrameView::SetNeedsCompositingUpdate(
CompositingUpdateType update_type) {
if (auto* layout_view = GetLayoutView()) {
if (frame_->GetDocument()->IsActive())
layout_view->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::ScrollbarExistenceMaybeChanged() {
// 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 =
GetPageScrollbarTheme().UsesOverlayScrollbars() &&
!ShouldUseCustomScrollbars(custom_scrollbar_element);
if (!uses_overlay_scrollbars && NeedsLayout())
UpdateLayout();
auto* layout_view = GetLayoutView();
if (layout_view && layout_view->UsesCompositing()) {
layout_view->Compositor()->FrameViewScrollbarsExistenceDidChange();
if (!uses_overlay_scrollbars)
layout_view->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();
}
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() || root.IsTablePart())
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.
auto* layout_view = GetLayoutView();
return LayoutPending() || (layout_view && layout_view->NeedsLayout()) ||
IsSubtreeLayout();
}
NOINLINE bool LocalFrameView::CheckDoesNotNeedLayout() const {
CHECK_FOR_DIRTY_LAYOUT(!LayoutPending());
CHECK_FOR_DIRTY_LAYOUT(!GetLayoutView() || !GetLayoutView()->NeedsLayout());
CHECK_FOR_DIRTY_LAYOUT(!IsSubtreeLayout());
return true;
}
void LocalFrameView::SetNeedsLayout() {
auto* layout_view = GetLayoutView();
if (!layout_view)
return;
// TODO(crbug.com/590856): It's still broken if we choose not to crash when
// the check fails.
if (!CheckLayoutInvalidationIsAllowed())
return;
layout_view->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 (auto* layout_view = GetLayoutView()) {
if (layout_view->Layer()->HasCompositedLayerMapping()) {
CompositedLayerMapping* composited_layer_mapping =
layout_view->Layer()->GetCompositedLayerMapping();
composited_layer_mapping->UpdateContentsOpaque();
if (composited_layer_mapping->MainGraphicsLayer())
composited_layer_mapping->MainGraphicsLayer()->SetNeedsDisplay();
if (composited_layer_mapping->ScrollingContentsLayer())
composited_layer_mapping->ScrollingContentsLayer()->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::ScrollAndFocusFragmentAnchor() {
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->BoundingBoxForScrollIntoView();
} else if (RuntimeEnabledFeatures::RootLayerScrollingEnabled()) {
if (Element* document_element = frame_->GetDocument()->documentElement())
rect = document_element->BoundingBoxForScrollIntoView();
}
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, WebScrollIntoViewParams(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);
// 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 (needs_focus_on_fragment_) {
if (anchor_node->IsElementNode() &&
ToElement(anchor_node)->IsFocusable()) {
ToElement(anchor_node)->focus();
} else {
frame_->GetDocument()->SetSequentialFocusNavigationStartingPoint(
anchor_node);
frame_->GetDocument()->ClearFocusedElement();
}
needs_focus_on_fragment_ = false;
}
}
// 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();
if (EmbeddedContentView* view = element->OwnedEmbeddedContentView())
view->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(TimeDelta(), 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());
FontFaceSetDocument::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.
// Fire a fake a mouse move event to update hover state and mouse cursor, and
// send the right mouse out/over events.
if (RuntimeEnabledFeatures::UpdateHoverPostLayoutEnabled()) {
frame_->GetEventHandler().DispatchFakeMouseMoveEventSoon(
MouseEventManager::FakeMouseMoveReason::kPerFrame);
} else {
GetFrame().LocalFrameRoot().GetEventHandler().ScheduleCursorUpdate();
}
UpdateGeometriesIfNeeded();
// Plugins could have torn down the page inside updateGeometries().
if (!GetLayoutView())
return;
ScheduleUpdatePluginsIfNecessary();
if (ScrollingCoordinator* scrolling_coordinator =
this->GetScrollingCoordinator()) {
scrolling_coordinator->NotifyGeometryChanged(this);
}
if (SnapCoordinator* snap_coordinator =
frame_->GetDocument()->GetSnapCoordinator())
snap_coordinator->UpdateAllSnapContainerData();
SendResizeEventIfNeeded();
}
bool LocalFrameView::WasViewportResized() {
DCHECK(frame_);
auto* layout_view = GetLayoutView();
if (!layout_view)
return false;
return (GetLayoutSize(kIncludeScrollbars) != last_viewport_size_ ||
layout_view->StyleRef().Zoom() != last_zoom_factor_);
}
void LocalFrameView::SendResizeEventIfNeeded() {
DCHECK(frame_);
auto* layout_view = GetLayoutView();
if (!layout_view || layout_view->GetDocument().Printing())
return;
if (!WasViewportResized())
return;
last_viewport_size_ = GetLayoutSize(kIncludeScrollbars);
last_zoom_factor_ = layout_view->StyleRef().Zoom();
if (RuntimeEnabledFeatures::VisualViewportAPIEnabled())
frame_->GetDocument()->EnqueueVisualViewportResizeEvent();
frame_->GetDocument()->EnqueueResizeEvent();
if (frame_->IsMainFrame())
probe::didResizeMainFrame(frame_.Get());
}
void LocalFrameView::PostLayoutTimerFired(TimerBase*) {
PerformPostLayoutTasks();
}
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() {
ScrollableArea* scrollable_area = LayoutViewportScrollableArea();
if (!scrollable_area)
return;
if (Scrollbar* scrollbar = scrollable_area->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::SetInputEventsScaleForEmulation(
float content_scale_factor) {
input_events_scale_factor_for_emulation_ = content_scale_factor;
}
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();
if (auto* layout_view = GetLayoutView())
layout_view->ClearHitTestCache();
}
void LocalFrameView::ScrollbarFrameRectChanged() {
SetNeedsPaintPropertyUpdate();
}
IntRect LocalFrameView::ScrollableAreaBoundingBox() const {
auto* owner_layout_object = GetFrame().OwnerLayoutObject();
if (!owner_layout_object)
return FrameRect();
LocalFrameView* local_root = GetFrame().LocalFrameRoot().View();
return local_root->RootFrameToDocument(local_root->AbsoluteToRootFrame(
owner_layout_object->AbsoluteContentQuad(kTraverseDocumentBoundaries)
.EnclosingBoundingBox()));
}
bool LocalFrameView::IsScrollable() const {
return GetScrollingReasons() == kScrollable;
}
bool LocalFrameView::IsProgrammaticallyScrollable() {
return !in_update_scrollbars_;
}
LocalFrameView::ScrollingReasons LocalFrameView::GetScrollingReasons() const {