| /* |
| * 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( |
| ¤t_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 |