blob: 91dad989e6949afcab12018594b313ce439def5c [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/frame/VisualViewport.h"
#include <memory>
#include "core/dom/TaskRunnerHelper.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/PageScaleConstraints.h"
#include "core/frame/PageScaleConstraintsSet.h"
#include "core/frame/RootFrameViewport.h"
#include "core/frame/Settings.h"
#include "core/fullscreen/Fullscreen.h"
#include "core/layout/TextAutosizer.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
#include "core/paint/compositing/PaintLayerCompositor.h"
#include "core/probe/CoreProbes.h"
#include "core/style/ComputedStyle.h"
#include "platform/Histogram.h"
#include "platform/geometry/DoubleRect.h"
#include "platform/geometry/FloatSize.h"
#include "platform/graphics/CompositorMutableProperties.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/scroll/Scrollbar.h"
#include "platform/scroll/ScrollbarThemeOverlay.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebScrollbar.h"
#include "public/platform/WebScrollbarLayer.h"
namespace blink {
VisualViewport::VisualViewport(Page& owner)
: page_(&owner),
scale_(1),
browser_controls_adjustment_(0),
max_page_scale_(-1),
track_pinch_zoom_stats_for_page_(false),
unique_id_(NewUniqueObjectId()) {
Reset();
}
VisualViewport::~VisualViewport() {
SendUMAMetrics();
}
DEFINE_TRACE(VisualViewport) {
visitor->Trace(page_);
ScrollableArea::Trace(visitor);
}
void VisualViewport::UpdateStyleAndLayoutIgnorePendingStylesheets() const {
if (!MainFrame())
return;
if (Document* document = MainFrame()->GetDocument())
document->UpdateStyleAndLayoutIgnorePendingStylesheets();
}
void VisualViewport::EnqueueScrollEvent() {
if (!RuntimeEnabledFeatures::VisualViewportAPIEnabled())
return;
if (Document* document = MainFrame()->GetDocument())
document->EnqueueVisualViewportScrollEvent();
}
void VisualViewport::EnqueueResizeEvent() {
if (!RuntimeEnabledFeatures::VisualViewportAPIEnabled())
return;
if (Document* document = MainFrame()->GetDocument())
document->EnqueueVisualViewportResizeEvent();
}
void VisualViewport::SetSize(const IntSize& size) {
if (size_ == size)
return;
TRACE_EVENT2("blink", "VisualViewport::setSize", "width", size.Width(),
"height", size.Height());
bool width_did_change = size.Width() != size_.Width();
size_ = size;
if (inner_viewport_container_layer_) {
inner_viewport_container_layer_->SetSize(FloatSize(size_));
inner_viewport_scroll_layer_->PlatformLayer()->SetScrollable(size_);
// Need to re-compute sizes for the overlay scrollbars.
InitializeScrollbars();
}
if (!MainFrame())
return;
EnqueueResizeEvent();
bool autosizer_needs_updating =
width_did_change && MainFrame()->GetSettings() &&
MainFrame()->GetSettings()->TextAutosizingEnabled();
if (autosizer_needs_updating) {
// This needs to happen after setting the m_size member since it'll be read
// in the update call.
if (TextAutosizer* text_autosizer =
MainFrame()->GetDocument()->GetTextAutosizer())
text_autosizer->UpdatePageInfoInAllFrames();
}
}
void VisualViewport::Reset() {
SetScaleAndLocation(1, FloatPoint());
}
void VisualViewport::MainFrameDidChangeSize() {
TRACE_EVENT0("blink", "VisualViewport::mainFrameDidChangeSize");
// In unit tests we may not have initialized the layer tree.
if (inner_viewport_scroll_layer_)
inner_viewport_scroll_layer_->SetSize(FloatSize(ContentsSize()));
ClampToBoundaries();
}
FloatSize VisualViewport::VisibleSize() const {
FloatSize scaled_size(size_);
scaled_size.Expand(0, browser_controls_adjustment_);
scaled_size.Scale(1 / scale_);
return scaled_size;
}
FloatRect VisualViewport::VisibleRect() const {
return FloatRect(FloatPoint(GetScrollOffset()), VisibleSize());
}
FloatRect VisualViewport::VisibleRectInDocument() const {
if (!MainFrame() || !MainFrame()->View())
return FloatRect();
FloatPoint view_location =
FloatPoint(MainFrame()->View()->GetScrollableArea()->GetScrollOffset());
return FloatRect(view_location, VisibleSize());
}
FloatPoint VisualViewport::ViewportCSSPixelsToRootFrame(
const FloatPoint& point) const {
// Note, this is in CSS Pixels so we don't apply scale.
FloatPoint point_in_root_frame = point;
point_in_root_frame.Move(GetScrollOffset());
return point_in_root_frame;
}
void VisualViewport::SetLocation(const FloatPoint& new_location) {
SetScaleAndLocation(scale_, new_location);
}
void VisualViewport::Move(const ScrollOffset& delta) {
SetLocation(FloatPoint(offset_ + delta));
}
void VisualViewport::SetScale(float scale) {
SetScaleAndLocation(scale, FloatPoint(offset_));
}
double VisualViewport::OffsetLeft() const {
if (!MainFrame())
return 0;
UpdateStyleAndLayoutIgnorePendingStylesheets();
return AdjustScrollForAbsoluteZoom(VisibleRect().X(),
MainFrame()->PageZoomFactor());
}
double VisualViewport::OffsetTop() const {
if (!MainFrame())
return 0;
UpdateStyleAndLayoutIgnorePendingStylesheets();
return AdjustScrollForAbsoluteZoom(VisibleRect().Y(),
MainFrame()->PageZoomFactor());
}
double VisualViewport::Width() const {
UpdateStyleAndLayoutIgnorePendingStylesheets();
return VisibleWidthCSSPx();
}
double VisualViewport::Height() const {
UpdateStyleAndLayoutIgnorePendingStylesheets();
return VisibleHeightCSSPx();
}
double VisualViewport::ScaleForVisualViewport() const {
return Scale();
}
void VisualViewport::SetScaleAndLocation(float scale,
const FloatPoint& location) {
if (DidSetScaleOrLocation(scale, location))
NotifyRootFrameViewport();
}
double VisualViewport::VisibleWidthCSSPx() const {
if (!MainFrame())
return 0;
float zoom = MainFrame()->PageZoomFactor();
float width_css_px = AdjustScrollForAbsoluteZoom(VisibleSize().Width(), zoom);
float scrollbar_thickness_css_px =
MainFrame()->View()->VerticalScrollbarWidth() / (zoom * scale_);
return width_css_px - scrollbar_thickness_css_px;
}
double VisualViewport::VisibleHeightCSSPx() const {
if (!MainFrame())
return 0;
float zoom = MainFrame()->PageZoomFactor();
float height_css_px =
AdjustScrollForAbsoluteZoom(VisibleSize().Height(), zoom);
float scrollbar_thickness_css_px =
MainFrame()->View()->HorizontalScrollbarHeight() / (zoom * scale_);
return height_css_px - scrollbar_thickness_css_px;
}
bool VisualViewport::DidSetScaleOrLocation(float scale,
const FloatPoint& location) {
if (!MainFrame())
return false;
bool values_changed = false;
if (scale != scale_ && !std::isnan(scale) && !std::isinf(scale)) {
scale_ = scale;
values_changed = true;
GetPage().GetChromeClient().PageScaleFactorChanged();
EnqueueResizeEvent();
}
ScrollOffset clamped_offset = ClampScrollOffset(ToScrollOffset(location));
// TODO(bokan): If the offset is invalid, we might end up in an infinite
// recursion as we reenter this function on clamping. It would be cleaner to
// avoid reentrancy but for now just prevent the stack overflow.
// crbug.com/702771.
if (std::isnan(clamped_offset.Width()) ||
std::isnan(clamped_offset.Height()) ||
std::isinf(clamped_offset.Width()) || std::isinf(clamped_offset.Height()))
return false;
if (clamped_offset != offset_) {
offset_ = clamped_offset;
GetScrollAnimator().SetCurrentOffset(offset_);
// SVG runs with accelerated compositing disabled so no
// ScrollingCoordinator.
if (ScrollingCoordinator* coordinator = GetPage().GetScrollingCoordinator())
coordinator->ScrollableAreaScrollLayerDidChange(this);
if (!GetPage().GetSettings().GetInertVisualViewport()) {
if (Document* document = MainFrame()->GetDocument())
document->EnqueueScrollEventForNode(document);
}
EnqueueScrollEvent();
MainFrame()->View()->DidChangeScrollOffset();
values_changed = true;
}
if (!values_changed)
return false;
probe::didChangeViewport(MainFrame());
MainFrame()->Loader().SaveScrollState();
ClampToBoundaries();
return true;
}
bool VisualViewport::MagnifyScaleAroundAnchor(float magnify_delta,
const FloatPoint& anchor) {
const float old_page_scale = Scale();
const float new_page_scale =
GetPage().GetChromeClient().ClampPageScaleFactorToLimits(magnify_delta *
old_page_scale);
if (new_page_scale == old_page_scale)
return false;
if (!MainFrame() || !MainFrame()->View())
return false;
// Keep the center-of-pinch anchor in a stable position over the course
// of the magnify.
// TODO(bokan): Looks like we call into setScaleAndLocation with infinity for
// the location so it seems either old or newPageScale is invalid.
// crbug.com/702771.
FloatPoint anchor_at_old_scale = anchor.ScaledBy(1.f / old_page_scale);
FloatPoint anchor_at_new_scale = anchor.ScaledBy(1.f / new_page_scale);
FloatSize anchor_delta = anchor_at_old_scale - anchor_at_new_scale;
// First try to use the anchor's delta to scroll the LocalFrameView.
FloatSize anchor_delta_unused_by_scroll = anchor_delta;
// Manually bubble any remaining anchor delta up to the visual viewport.
FloatPoint new_location(FloatPoint(GetScrollOffset()) +
anchor_delta_unused_by_scroll);
SetScaleAndLocation(new_page_scale, new_location);
return true;
}
void VisualViewport::CreateLayerTree() {
if (inner_viewport_scroll_layer_)
return;
DCHECK(!overlay_scrollbar_horizontal_ && !overlay_scrollbar_vertical_ &&
!overscroll_elasticity_layer_ && !page_scale_layer_ &&
!inner_viewport_container_layer_);
// FIXME: The root transform layer should only be created on demand.
root_transform_layer_ = GraphicsLayer::Create(this);
inner_viewport_container_layer_ = GraphicsLayer::Create(this);
overscroll_elasticity_layer_ = GraphicsLayer::Create(this);
page_scale_layer_ = GraphicsLayer::Create(this);
inner_viewport_scroll_layer_ = GraphicsLayer::Create(this);
overlay_scrollbar_horizontal_ = GraphicsLayer::Create(this);
overlay_scrollbar_vertical_ = GraphicsLayer::Create(this);
ScrollingCoordinator* coordinator = GetPage().GetScrollingCoordinator();
DCHECK(coordinator);
coordinator->SetLayerIsContainerForFixedPositionLayers(
inner_viewport_scroll_layer_.get(), true);
coordinator->UpdateUserInputScrollable(this);
// Set masks to bounds so the compositor doesn't clobber a manually
// set inner viewport container layer size.
inner_viewport_container_layer_->SetMasksToBounds(
GetPage().GetSettings().GetMainFrameClipsContent());
inner_viewport_container_layer_->SetSize(FloatSize(size_));
inner_viewport_scroll_layer_->PlatformLayer()->SetScrollable(size_);
DCHECK(MainFrame());
DCHECK(MainFrame()->GetDocument());
inner_viewport_scroll_layer_->SetElementId(
CompositorElementIdFromUniqueObjectId(unique_id_));
root_transform_layer_->AddChild(inner_viewport_container_layer_.get());
inner_viewport_container_layer_->AddChild(overscroll_elasticity_layer_.get());
overscroll_elasticity_layer_->AddChild(page_scale_layer_.get());
page_scale_layer_->AddChild(inner_viewport_scroll_layer_.get());
// Ensure this class is set as the scroll layer's ScrollableArea.
coordinator->ScrollableAreaScrollLayerDidChange(this);
InitializeScrollbars();
}
void VisualViewport::AttachLayerTree(GraphicsLayer* current_layer_tree_root) {
TRACE_EVENT1("blink", "VisualViewport::attachLayerTree",
"currentLayerTreeRoot", (bool)current_layer_tree_root);
if (!current_layer_tree_root) {
if (inner_viewport_scroll_layer_)
inner_viewport_scroll_layer_->RemoveAllChildren();
return;
}
if (current_layer_tree_root->Parent() &&
current_layer_tree_root->Parent() == inner_viewport_scroll_layer_.get())
return;
DCHECK(inner_viewport_scroll_layer_);
inner_viewport_scroll_layer_->RemoveAllChildren();
inner_viewport_scroll_layer_->AddChild(current_layer_tree_root);
}
void VisualViewport::InitializeScrollbars() {
// Do nothing if not attached to layer tree yet - will initialize upon attach.
if (!inner_viewport_container_layer_)
return;
if (VisualViewportSuppliesScrollbars() &&
!GetPage().GetSettings().GetHideScrollbars()) {
if (!overlay_scrollbar_horizontal_->Parent())
inner_viewport_container_layer_->AddChild(
overlay_scrollbar_horizontal_.get());
if (!overlay_scrollbar_vertical_->Parent())
inner_viewport_container_layer_->AddChild(
overlay_scrollbar_vertical_.get());
} else {
overlay_scrollbar_horizontal_->RemoveFromParent();
overlay_scrollbar_vertical_->RemoveFromParent();
}
SetupScrollbar(WebScrollbar::kHorizontal);
SetupScrollbar(WebScrollbar::kVertical);
// Ensure existing LocalFrameView scrollbars are removed if the visual
// viewport scrollbars are now supplied, or created if the visual viewport no
// longer supplies scrollbars.
LocalFrame* frame = MainFrame();
if (frame && frame->View())
frame->View()->VisualViewportScrollbarsChanged();
}
void VisualViewport::SetupScrollbar(WebScrollbar::Orientation orientation) {
bool is_horizontal = orientation == WebScrollbar::kHorizontal;
GraphicsLayer* scrollbar_graphics_layer =
is_horizontal ? overlay_scrollbar_horizontal_.get()
: overlay_scrollbar_vertical_.get();
std::unique_ptr<WebScrollbarLayer>& web_scrollbar_layer =
is_horizontal ? web_overlay_scrollbar_horizontal_
: web_overlay_scrollbar_vertical_;
ScrollbarThemeOverlay& theme = ScrollbarThemeOverlay::MobileTheme();
int thumb_thickness = clampTo<int>(
std::floor(GetPage().GetChromeClient().WindowToViewportScalar(
theme.ThumbThickness())));
int scrollbar_thickness = clampTo<int>(
std::floor(GetPage().GetChromeClient().WindowToViewportScalar(
theme.ScrollbarThickness(kRegularScrollbar))));
int scrollbar_margin = clampTo<int>(
std::floor(GetPage().GetChromeClient().WindowToViewportScalar(
theme.ScrollbarMargin())));
if (!web_scrollbar_layer) {
ScrollingCoordinator* coordinator = GetPage().GetScrollingCoordinator();
DCHECK(coordinator);
ScrollbarOrientation webcore_orientation =
is_horizontal ? kHorizontalScrollbar : kVerticalScrollbar;
web_scrollbar_layer = coordinator->CreateSolidColorScrollbarLayer(
webcore_orientation, thumb_thickness, scrollbar_margin, false);
// The compositor will control the scrollbar's visibility. Set to invisible
// by default so scrollbars don't show up in layout tests.
web_scrollbar_layer->Layer()->SetOpacity(0);
scrollbar_graphics_layer->SetContentsToPlatformLayer(
web_scrollbar_layer->Layer());
scrollbar_graphics_layer->SetDrawsContent(false);
}
int x_position = is_horizontal
? 0
: inner_viewport_container_layer_->Size().Width() -
scrollbar_thickness;
int y_position = is_horizontal
? inner_viewport_container_layer_->Size().Height() -
scrollbar_thickness
: 0;
int width = is_horizontal ? inner_viewport_container_layer_->Size().Width() -
scrollbar_thickness
: scrollbar_thickness;
int height = is_horizontal
? scrollbar_thickness
: inner_viewport_container_layer_->Size().Height() -
scrollbar_thickness;
// Use the GraphicsLayer to position the scrollbars.
scrollbar_graphics_layer->SetPosition(IntPoint(x_position, y_position));
scrollbar_graphics_layer->SetSize(FloatSize(width, height));
scrollbar_graphics_layer->SetContentsRect(IntRect(0, 0, width, height));
}
void VisualViewport::SetScrollLayerOnScrollbars(WebLayer* scroll_layer) const {
// TODO(bokan): This is currently done while registering viewport layers
// with the compositor but could it actually be done earlier, like in
// setupScrollbars? Then we wouldn't need this method.
web_overlay_scrollbar_horizontal_->SetScrollLayer(scroll_layer);
web_overlay_scrollbar_vertical_->SetScrollLayer(scroll_layer);
}
bool VisualViewport::VisualViewportSuppliesScrollbars() const {
return GetPage().GetSettings().GetViewportEnabled();
}
CompositorElementId VisualViewport::GetCompositorElementId() const {
// TODO(chrishtr): Implement http://crbug.com/638473.
return CompositorElementId();
}
bool VisualViewport::ScrollAnimatorEnabled() const {
return GetPage().GetSettings().GetScrollAnimatorEnabled();
}
PlatformChromeClient* VisualViewport::GetChromeClient() const {
return &GetPage().GetChromeClient();
}
bool VisualViewport::ShouldUseIntegerScrollOffset() const {
LocalFrame* frame = MainFrame();
if (frame && frame->GetSettings() &&
!frame->GetSettings()->GetPreferCompositingToLCDTextEnabled())
return true;
return ScrollableArea::ShouldUseIntegerScrollOffset();
}
void VisualViewport::SetScrollOffset(const ScrollOffset& offset,
ScrollType scroll_type,
ScrollBehavior scroll_behavior) {
// We clamp the offset here, because the ScrollAnimator may otherwise be
// set to a non-clamped offset by ScrollableArea::setScrollOffset,
// which may lead to incorrect scrolling behavior in RootFrameViewport down
// the line.
// TODO(eseckler): Solve this instead by ensuring that ScrollableArea and
// ScrollAnimator are kept in sync. This requires that ScrollableArea always
// stores fractional offsets and that truncation happens elsewhere, see
// crbug.com/626315.
ScrollOffset new_scroll_offset = ClampScrollOffset(offset);
ScrollableArea::SetScrollOffset(new_scroll_offset, scroll_type,
scroll_behavior);
}
int VisualViewport::ScrollSize(ScrollbarOrientation orientation) const {
IntSize scroll_dimensions =
MaximumScrollOffsetInt() - MinimumScrollOffsetInt();
return (orientation == kHorizontalScrollbar) ? scroll_dimensions.Width()
: scroll_dimensions.Height();
}
IntSize VisualViewport::MinimumScrollOffsetInt() const {
return IntSize();
}
IntSize VisualViewport::MaximumScrollOffsetInt() const {
return FlooredIntSize(MaximumScrollOffset());
}
ScrollOffset VisualViewport::MaximumScrollOffset() const {
if (!MainFrame())
return ScrollOffset();
// TODO(bokan): We probably shouldn't be storing the bounds in a float.
// crbug.com/470718.
FloatSize frame_view_size(ContentsSize());
if (browser_controls_adjustment_) {
float min_scale =
GetPage().GetPageScaleConstraintsSet().FinalConstraints().minimum_scale;
frame_view_size.Expand(0, browser_controls_adjustment_ / min_scale);
}
frame_view_size.Scale(scale_);
frame_view_size = FloatSize(FlooredIntSize(frame_view_size));
FloatSize viewport_size(size_);
viewport_size.Expand(0, ceilf(browser_controls_adjustment_));
FloatSize max_position = frame_view_size - viewport_size;
max_position.Scale(1 / scale_);
return ScrollOffset(max_position);
}
IntPoint VisualViewport::ClampDocumentOffsetAtScale(const IntPoint& offset,
float scale) {
if (!MainFrame() || !MainFrame()->View())
return IntPoint();
LocalFrameView* view = MainFrame()->View();
FloatSize scaled_size(size_);
scaled_size.Scale(1 / scale);
IntSize visual_viewport_max =
FlooredIntSize(FloatSize(ContentsSize()) - scaled_size);
IntSize max = view->LayoutViewportScrollableArea()->MaximumScrollOffsetInt() +
visual_viewport_max;
IntSize min =
view->LayoutViewportScrollableArea()
->MinimumScrollOffsetInt(); // VisualViewportMin should be (0, 0)
IntSize clamped = ToIntSize(offset);
clamped = clamped.ShrunkTo(max);
clamped = clamped.ExpandedTo(min);
return IntPoint(clamped);
}
void VisualViewport::SetBrowserControlsAdjustment(float adjustment) {
if (browser_controls_adjustment_ == adjustment)
return;
browser_controls_adjustment_ = adjustment;
EnqueueResizeEvent();
}
float VisualViewport::BrowserControlsAdjustment() const {
return browser_controls_adjustment_;
}
IntRect VisualViewport::ScrollableAreaBoundingBox() const {
// This method should return the bounding box in the top-level
// LocalFrameView's coordinate space; however, VisualViewport technically
// isn't a child of any Frames. Nonetheless, the VisualViewport always
// occupies the entire main frame so just return that.
LocalFrame* frame = MainFrame();
if (!frame || !frame->View())
return IntRect();
return frame->View()->FrameRect();
}
bool VisualViewport::UserInputScrollable(ScrollbarOrientation) const {
// If there is a non-root fullscreen element, prevent the viewport from
// scrolling.
Document* main_document = MainFrame() ? MainFrame()->GetDocument() : nullptr;
if (main_document) {
Element* fullscreen_element =
Fullscreen::FullscreenElementFrom(*main_document);
if (fullscreen_element)
return false;
}
return true;
}
IntSize VisualViewport::ContentsSize() const {
LocalFrame* frame = MainFrame();
if (!frame || !frame->View())
return IntSize();
// TODO(bokan): This should be the layout viewport rather than main
// LocalFrameView.
return frame->View()->VisibleContentRect(kIncludeScrollbars).Size();
}
IntRect VisualViewport::VisibleContentRect(
IncludeScrollbarsInRect scrollbar_inclusion) const {
// TODO(ymalik): We're losing precision here and below. visibleRect should
// be replaced with visibleContentRect.
IntRect rect = IntRect(VisibleRect());
if (scrollbar_inclusion == kExcludeScrollbars) {
RootFrameViewport* root_frame_viewport =
MainFrame()->View()->GetRootFrameViewport();
DCHECK(root_frame_viewport);
rect.Contract(root_frame_viewport->VerticalScrollbarWidth() / scale_,
root_frame_viewport->HorizontalScrollbarHeight() / scale_);
}
return rect;
}
RefPtr<WebTaskRunner> VisualViewport::GetTimerTaskRunner() const {
return TaskRunnerHelper::Get(TaskType::kUnspecedTimer, MainFrame());
}
void VisualViewport::UpdateScrollOffset(const ScrollOffset& position,
ScrollType scroll_type) {
if (!DidSetScaleOrLocation(scale_, FloatPoint(position)))
return;
if (IsExplicitScrollType(scroll_type)) {
NotifyRootFrameViewport();
if (scroll_type != kCompositorScroll && LayerForScrolling())
LayerForScrolling()->PlatformLayer()->ShowScrollbars();
}
}
GraphicsLayer* VisualViewport::LayerForContainer() const {
return inner_viewport_container_layer_.get();
}
GraphicsLayer* VisualViewport::LayerForScrolling() const {
return inner_viewport_scroll_layer_.get();
}
GraphicsLayer* VisualViewport::LayerForHorizontalScrollbar() const {
return overlay_scrollbar_horizontal_.get();
}
GraphicsLayer* VisualViewport::LayerForVerticalScrollbar() const {
return overlay_scrollbar_vertical_.get();
}
IntRect VisualViewport::ComputeInterestRect(const GraphicsLayer*,
const IntRect&) const {
return IntRect();
}
void VisualViewport::PaintContents(const GraphicsLayer*,
GraphicsContext&,
GraphicsLayerPaintingPhase,
const IntRect&) const {}
LocalFrame* VisualViewport::MainFrame() const {
return GetPage().MainFrame() && GetPage().MainFrame()->IsLocalFrame()
? GetPage().DeprecatedLocalMainFrame()
: nullptr;
}
bool VisualViewport::ScheduleAnimation() {
if (PlatformChromeClient* client = GetChromeClient()) {
client->ScheduleAnimation(MainFrame()->View());
return true;
}
return false;
}
void VisualViewport::ClampToBoundaries() {
SetLocation(FloatPoint(offset_));
}
FloatRect VisualViewport::ViewportToRootFrame(
const FloatRect& rect_in_viewport) const {
FloatRect rect_in_root_frame = rect_in_viewport;
rect_in_root_frame.Scale(1 / Scale());
rect_in_root_frame.Move(GetScrollOffset());
return rect_in_root_frame;
}
IntRect VisualViewport::ViewportToRootFrame(
const IntRect& rect_in_viewport) const {
// FIXME: How to snap to pixels?
return EnclosingIntRect(ViewportToRootFrame(FloatRect(rect_in_viewport)));
}
FloatRect VisualViewport::RootFrameToViewport(
const FloatRect& rect_in_root_frame) const {
FloatRect rect_in_viewport = rect_in_root_frame;
rect_in_viewport.Move(-GetScrollOffset());
rect_in_viewport.Scale(Scale());
return rect_in_viewport;
}
IntRect VisualViewport::RootFrameToViewport(
const IntRect& rect_in_root_frame) const {
// FIXME: How to snap to pixels?
return EnclosingIntRect(RootFrameToViewport(FloatRect(rect_in_root_frame)));
}
FloatPoint VisualViewport::ViewportToRootFrame(
const FloatPoint& point_in_viewport) const {
FloatPoint point_in_root_frame = point_in_viewport;
point_in_root_frame.Scale(1 / Scale(), 1 / Scale());
point_in_root_frame.Move(GetScrollOffset());
return point_in_root_frame;
}
FloatPoint VisualViewport::RootFrameToViewport(
const FloatPoint& point_in_root_frame) const {
FloatPoint point_in_viewport = point_in_root_frame;
point_in_viewport.Move(-GetScrollOffset());
point_in_viewport.Scale(Scale(), Scale());
return point_in_viewport;
}
IntPoint VisualViewport::ViewportToRootFrame(
const IntPoint& point_in_viewport) const {
// FIXME: How to snap to pixels?
return FlooredIntPoint(
FloatPoint(ViewportToRootFrame(FloatPoint(point_in_viewport))));
}
IntPoint VisualViewport::RootFrameToViewport(
const IntPoint& point_in_root_frame) const {
// FIXME: How to snap to pixels?
return FlooredIntPoint(
FloatPoint(RootFrameToViewport(FloatPoint(point_in_root_frame))));
}
void VisualViewport::StartTrackingPinchStats() {
if (!MainFrame())
return;
Document* document = MainFrame()->GetDocument();
if (!document)
return;
if (!document->Url().ProtocolIsInHTTPFamily())
return;
track_pinch_zoom_stats_for_page_ = !ShouldDisableDesktopWorkarounds();
}
void VisualViewport::UserDidChangeScale() {
if (!track_pinch_zoom_stats_for_page_)
return;
max_page_scale_ = std::max(max_page_scale_, scale_);
}
void VisualViewport::SendUMAMetrics() {
if (track_pinch_zoom_stats_for_page_) {
bool did_scale = max_page_scale_ > 0;
DEFINE_STATIC_LOCAL(EnumerationHistogram, did_scale_histogram,
("Viewport.DidScalePage", 2));
did_scale_histogram.Count(did_scale ? 1 : 0);
if (did_scale) {
int zoom_percentage = floor(max_page_scale_ * 100);
// See the PageScaleFactor enumeration in histograms.xml for the bucket
// ranges.
int bucket = floor(zoom_percentage / 25.f);
DEFINE_STATIC_LOCAL(EnumerationHistogram, max_scale_histogram,
("Viewport.MaxPageScale", 21));
max_scale_histogram.Count(bucket);
}
}
max_page_scale_ = -1;
track_pinch_zoom_stats_for_page_ = false;
}
bool VisualViewport::ShouldDisableDesktopWorkarounds() const {
if (!MainFrame() || !MainFrame()->View())
return false;
if (!MainFrame()->GetSettings()->GetViewportEnabled())
return false;
// A document is considered adapted to small screen UAs if one of these holds:
// 1. The author specified viewport has a constrained width that is equal to
// the initial viewport width.
// 2. The author has disabled viewport zoom.
const PageScaleConstraints& constraints =
GetPage().GetPageScaleConstraintsSet().PageDefinedConstraints();
return MainFrame()->View()->GetLayoutSize().Width() == size_.Width() ||
(constraints.minimum_scale == constraints.maximum_scale &&
constraints.minimum_scale != -1);
}
CompositorAnimationHost* VisualViewport::GetCompositorAnimationHost() const {
DCHECK(GetPage().MainFrame()->IsLocalFrame());
ScrollingCoordinator* c = GetPage().GetScrollingCoordinator();
return c ? c->GetCompositorAnimationHost() : nullptr;
}
CompositorAnimationTimeline* VisualViewport::GetCompositorAnimationTimeline()
const {
DCHECK(GetPage().MainFrame()->IsLocalFrame());
ScrollingCoordinator* c = GetPage().GetScrollingCoordinator();
return c ? c->GetCompositorAnimationTimeline() : nullptr;
}
void VisualViewport::NotifyRootFrameViewport() const {
if (!MainFrame() || !MainFrame()->View())
return;
RootFrameViewport* root_frame_viewport =
MainFrame()->View()->GetRootFrameViewport();
if (!root_frame_viewport)
return;
root_frame_viewport->DidUpdateVisualViewport();
}
String VisualViewport::DebugName(const GraphicsLayer* graphics_layer) const {
String name;
if (graphics_layer == inner_viewport_container_layer_.get()) {
name = "Inner Viewport Container Layer";
} else if (graphics_layer == overscroll_elasticity_layer_.get()) {
name = "Overscroll Elasticity Layer";
} else if (graphics_layer == page_scale_layer_.get()) {
name = "Page Scale Layer";
} else if (graphics_layer == inner_viewport_scroll_layer_.get()) {
name = "Inner Viewport Scroll Layer";
} else if (graphics_layer == overlay_scrollbar_horizontal_.get()) {
name = "Overlay Scrollbar Horizontal Layer";
} else if (graphics_layer == overlay_scrollbar_vertical_.get()) {
name = "Overlay Scrollbar Vertical Layer";
} else if (graphics_layer == root_transform_layer_.get()) {
name = "Root Transform Layer";
} else {
NOTREACHED();
}
return name;
}
} // namespace blink