blob: 380e5cc24d0e4be6cde2c90e5f3af5820bcf25d2 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/page_scale_constraints_set.h"
#include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/scrolling/overscroll_controller.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_util.h"
#include "third_party/blink/renderer/core/page/scrolling/viewport_scroll_callback.h"
#include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/scroll/scrollable_area.h"
namespace blink {
// static
TopDocumentRootScrollerController* TopDocumentRootScrollerController::Create(
Page& page) {
return new TopDocumentRootScrollerController(page);
}
TopDocumentRootScrollerController::TopDocumentRootScrollerController(Page& page)
: page_(&page) {}
void TopDocumentRootScrollerController::Trace(blink::Visitor* visitor) {
visitor->Trace(viewport_apply_scroll_);
visitor->Trace(global_root_scroller_);
visitor->Trace(page_);
}
void TopDocumentRootScrollerController::DidChangeRootScroller() {
RecomputeGlobalRootScroller();
}
void TopDocumentRootScrollerController::DidResizeViewport() {
if (!GlobalRootScroller())
return;
// Top controls can resize the viewport without invalidating compositing or
// paint so we need to do that manually here.
GlobalRootScroller()->SetNeedsCompositingUpdate();
if (GlobalRootScroller()->GetLayoutObject())
GlobalRootScroller()->GetLayoutObject()->SetNeedsPaintPropertyUpdate();
}
ScrollableArea* TopDocumentRootScrollerController::RootScrollerArea() const {
return RootScrollerUtil::ScrollableAreaForRootScroller(GlobalRootScroller());
}
IntSize TopDocumentRootScrollerController::RootScrollerVisibleArea() const {
if (!TopDocument() || !TopDocument()->View())
return IntSize();
float minimum_page_scale =
page_->GetPageScaleConstraintsSet().FinalConstraints().minimum_scale;
int browser_controls_adjustment =
ceilf(page_->GetVisualViewport().BrowserControlsAdjustment() /
minimum_page_scale);
return TopDocument()
->View()
->LayoutViewport()
->VisibleContentRect(kExcludeScrollbars)
.Size() +
IntSize(0, browser_controls_adjustment);
}
Element* TopDocumentRootScrollerController::FindGlobalRootScrollerElement() {
if (!TopDocument())
return nullptr;
Node* effective_root_scroller =
&TopDocument()->GetRootScrollerController().EffectiveRootScroller();
if (effective_root_scroller->IsDocumentNode())
return TopDocument()->documentElement();
DCHECK(effective_root_scroller->IsElementNode());
Element* element = ToElement(effective_root_scroller);
while (element && element->IsFrameOwnerElement()) {
HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(element);
DCHECK(frame_owner);
Document* iframe_document = frame_owner->contentDocument();
if (!iframe_document)
return element;
effective_root_scroller =
&iframe_document->GetRootScrollerController().EffectiveRootScroller();
if (effective_root_scroller->IsDocumentNode())
return iframe_document->documentElement();
element = ToElement(effective_root_scroller);
}
return element;
}
void SetNeedsCompositingUpdateOnAncestors(Element* element) {
if (!element || !element->GetDocument().IsActive())
return;
ScrollableArea* area =
RootScrollerUtil::ScrollableAreaForRootScroller(element);
if (!area || !area->Layer())
return;
Frame* frame = area->Layer()->GetLayoutObject().GetFrame();
for (; frame; frame = frame->Tree().Parent()) {
if (!frame->IsLocalFrame())
continue;
LayoutView* layout_view = ToLocalFrame(frame)->View()->GetLayoutView();
PaintLayer* frame_root_layer = layout_view->Layer();
DCHECK(frame_root_layer);
frame_root_layer->SetNeedsCompositingInputsUpdate();
}
}
void TopDocumentRootScrollerController::RecomputeGlobalRootScroller() {
if (!viewport_apply_scroll_)
return;
Element* target = FindGlobalRootScrollerElement();
if (target == global_root_scroller_)
return;
ScrollableArea* target_scroller =
RootScrollerUtil::ScrollableAreaForRootScroller(target);
if (!target_scroller)
return;
if (global_root_scroller_)
global_root_scroller_->RemoveApplyScroll();
// Use disable-native-scroll since the ViewportScrollCallback needs to
// apply scroll actions both before (BrowserControls) and after (overscroll)
// scrolling the element so it will apply scroll to the element itself.
target->SetApplyScroll(viewport_apply_scroll_);
Element* old_root_scroller = global_root_scroller_;
global_root_scroller_ = target;
// Ideally, scroll customization would pass the current element to scroll to
// the apply scroll callback but this doesn't happen today so we set it
// through a back door here. This is also needed by the
// ViewportScrollCallback to swap the target into the layout viewport
// in RootFrameViewport.
viewport_apply_scroll_->SetScroller(target_scroller);
SetNeedsCompositingUpdateOnAncestors(old_root_scroller);
SetNeedsCompositingUpdateOnAncestors(target);
if (ScrollableArea* area =
RootScrollerUtil::ScrollableAreaForRootScroller(old_root_scroller)) {
if (old_root_scroller->GetDocument().IsActive())
area->DidChangeGlobalRootScroller();
}
target_scroller->DidChangeGlobalRootScroller();
}
Document* TopDocumentRootScrollerController::TopDocument() const {
if (!page_ || !page_->MainFrame() || !page_->MainFrame()->IsLocalFrame())
return nullptr;
return ToLocalFrame(page_->MainFrame())->GetDocument();
}
void TopDocumentRootScrollerController::DidUpdateCompositing(
const LocalFrameView& frame_view) {
if (!page_)
return;
// The only other way to get here is from a local root OOPIF but we ignore
// that case since the global root can't cross remote frames today.
if (!frame_view.GetFrame().IsMainFrame())
return;
// Let the compositor-side counterpart know about this change.
page_->GetChromeClient().RegisterViewportLayers();
}
void TopDocumentRootScrollerController::DidDisposeScrollableArea(
ScrollableArea& area) {
if (!TopDocument() || !TopDocument()->View())
return;
// If the document is tearing down, we may no longer have a layoutViewport to
// fallback to.
if (TopDocument()->Lifecycle().GetState() >= DocumentLifecycle::kStopping)
return;
LocalFrameView* frame_view = TopDocument()->View();
RootFrameViewport* rfv = frame_view->GetRootFrameViewport();
if (rfv && &area == &rfv->LayoutViewport()) {
DCHECK(frame_view->LayoutViewport());
rfv->SetLayoutViewport(*frame_view->LayoutViewport());
}
}
void TopDocumentRootScrollerController::InitializeViewportScrollCallback(
RootFrameViewport& root_frame_viewport) {
DCHECK(page_);
viewport_apply_scroll_ = ViewportScrollCallback::Create(
&page_->GetBrowserControls(), &page_->GetOverscrollController(),
root_frame_viewport);
RecomputeGlobalRootScroller();
}
bool TopDocumentRootScrollerController::IsViewportScrollCallback(
const ScrollStateCallback* callback) const {
if (!callback)
return false;
return callback == viewport_apply_scroll_.Get();
}
GraphicsLayer* TopDocumentRootScrollerController::RootScrollerLayer() const {
ScrollableArea* area =
RootScrollerUtil::ScrollableAreaForRootScroller(global_root_scroller_);
if (!area)
return nullptr;
GraphicsLayer* graphics_layer = area->LayerForScrolling();
// TODO(bokan): We should assert graphicsLayer here and
// RootScrollerController should do whatever needs to happen to ensure
// the root scroller gets composited.
return graphics_layer;
}
GraphicsLayer* TopDocumentRootScrollerController::RootContainerLayer() const {
ScrollableArea* area =
RootScrollerUtil::ScrollableAreaForRootScroller(global_root_scroller_);
return area ? area->LayerForContainer() : nullptr;
}
PaintLayer* TopDocumentRootScrollerController::RootScrollerPaintLayer() const {
return RootScrollerUtil::PaintLayerForRootScroller(global_root_scroller_);
}
Element* TopDocumentRootScrollerController::GlobalRootScroller() const {
return global_root_scroller_.Get();
}
} // namespace blink