blob: f5af179cec03402a8d1c94a78c865f1c276a35d7 [file] [log] [blame]
// Copyright 2014 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/frame/frame_view_auto_size_info.h"
#include "base/auto_reset.h"
#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_box.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
namespace blink {
FrameViewAutoSizeInfo::FrameViewAutoSizeInfo(LocalFrameView* view)
: frame_view_(view), in_auto_size_(false), did_run_autosize_(false) {
DCHECK(frame_view_);
}
void FrameViewAutoSizeInfo::Trace(Visitor* visitor) const {
visitor->Trace(frame_view_);
}
void FrameViewAutoSizeInfo::ConfigureAutoSizeMode(const IntSize& min_size,
const IntSize& max_size) {
DCHECK(!min_size.IsEmpty());
DCHECK_LE(min_size.Width(), max_size.Width());
DCHECK_LE(min_size.Height(), max_size.Height());
if (min_auto_size_ == min_size && max_auto_size_ == max_size)
return;
min_auto_size_ = min_size;
max_auto_size_ = max_size;
did_run_autosize_ = false;
}
void FrameViewAutoSizeInfo::AutoSizeIfNeeded() {
if (in_auto_size_)
return;
base::AutoReset<bool> change_in_auto_size(&in_auto_size_, true);
Document* document = frame_view_->GetFrame().GetDocument();
if (!document || !document->IsActive())
return;
Element* document_element = document->documentElement();
if (!document_element)
return;
// If this is the first time we run autosize, start from small height and
// allow it to grow.
if (!did_run_autosize_)
frame_view_->Resize(frame_view_->Width(), min_auto_size_.Height());
IntSize size = frame_view_->Size();
PaintLayerScrollableArea* layout_viewport = frame_view_->LayoutViewport();
// Do the resizing twice. The first time is basically a rough calculation
// using the preferred width which may result in a height change during the
// second iteration.
for (int i = 0; i < 2; i++) {
// Update various sizes including contentsSize, scrollHeight, etc.
document->UpdateStyleAndLayout(DocumentUpdateReason::kSizeChange);
auto* layout_view = document->GetLayoutView();
if (!layout_view)
return;
// TODO(bokan): This code doesn't handle subpixel sizes correctly. Because
// of that, it's forced to maintain all the special ScrollbarMode code
// below. https://crbug.com/812311.
int width = layout_view->PreferredLogicalWidths().min_size.ToInt();
LayoutBox* document_layout_box = document_element->GetLayoutBox();
if (!document_layout_box)
return;
int height = document_layout_box->ScrollHeight().ToInt();
IntSize new_size(width, height);
// Check to see if a scrollbar is needed for a given dimension and
// if so, increase the other dimension to account for the scrollbar.
// Since the dimensions are only for the view rectangle, once a
// dimension exceeds the maximum, there is no need to increase it further.
if (new_size.Width() > max_auto_size_.Width()) {
new_size.Expand(0, layout_viewport->HypotheticalScrollbarThickness(
kHorizontalScrollbar));
// Don't bother checking for a vertical scrollbar because the width is at
// already greater the maximum.
} else if (new_size.Height() > max_auto_size_.Height() &&
// If we have a real vertical scrollbar, it's already included in
// PreferredLogicalWidths(), so don't add a hypothetical one.
!layout_viewport->HasVerticalScrollbar()) {
new_size.Expand(
layout_viewport->HypotheticalScrollbarThickness(kVerticalScrollbar),
0);
// Don't bother checking for a horizontal scrollbar because the height is
// already greater the maximum.
}
// Ensure the size is at least the min bounds.
new_size = new_size.ExpandedTo(min_auto_size_);
// Bound the dimensions by the max bounds and determine what scrollbars to
// show.
mojom::blink::ScrollbarMode horizontal_scrollbar_mode =
mojom::blink::ScrollbarMode::kAlwaysOff;
if (new_size.Width() > max_auto_size_.Width()) {
new_size.SetWidth(max_auto_size_.Width());
horizontal_scrollbar_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
}
mojom::blink::ScrollbarMode vertical_scrollbar_mode =
mojom::blink::ScrollbarMode::kAlwaysOff;
if (new_size.Height() > max_auto_size_.Height()) {
new_size.SetHeight(max_auto_size_.Height());
vertical_scrollbar_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
}
if (new_size == size)
continue;
// While loading only allow the size to increase (to avoid twitching during
// intermediate smaller states) unless autoresize has just been turned on or
// the maximum size is smaller than the current size.
if (did_run_autosize_ && size.Height() <= max_auto_size_.Height() &&
size.Width() <= max_auto_size_.Width() &&
!frame_view_->GetFrame().GetDocument()->LoadEventFinished() &&
(new_size.Height() < size.Height() || new_size.Width() < size.Width()))
break;
frame_view_->Resize(new_size.Width(), new_size.Height());
// Force the scrollbar state to avoid the scrollbar code adding them and
// causing them to be needed. For example, a vertical scrollbar may cause
// text to wrap and thus increase the height (which is the only reason the
// scollbar is needed).
frame_view_->GetLayoutView()->SetAutosizeScrollbarModes(
horizontal_scrollbar_mode, vertical_scrollbar_mode);
}
did_run_autosize_ = true;
}
} // namespace blink