• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "config.h"
6 #include "core/frame/FrameViewAutoSizeInfo.h"
7 
8 #include "core/frame/FrameView.h"
9 #include "core/frame/LocalFrame.h"
10 #include "core/rendering/RenderBox.h"
11 #include "core/rendering/RenderView.h"
12 
13 namespace blink {
14 
FrameViewAutoSizeInfo(FrameView * view)15 FrameViewAutoSizeInfo::FrameViewAutoSizeInfo(FrameView* view)
16     : m_frameView(view)
17     , m_inAutoSize(false)
18     , m_didRunAutosize(false)
19 {
20     ASSERT(m_frameView);
21 }
22 
~FrameViewAutoSizeInfo()23 FrameViewAutoSizeInfo::~FrameViewAutoSizeInfo()
24 {
25     removeAutoSizeMode();
26 }
27 
configureAutoSizeMode(const IntSize & minSize,const IntSize & maxSize)28 void FrameViewAutoSizeInfo::configureAutoSizeMode(const IntSize& minSize, const IntSize& maxSize)
29 {
30     ASSERT(!minSize.isEmpty());
31     ASSERT(minSize.width() <= maxSize.width());
32     ASSERT(minSize.height() <= maxSize.height());
33 
34     if (m_minAutoSize == minSize && m_maxAutoSize == maxSize)
35         return;
36 
37     m_minAutoSize = minSize;
38     m_maxAutoSize = maxSize;
39     m_didRunAutosize = false;
40 
41     m_frameView->setLayoutSizeFixedToFrameSize(true);
42     m_frameView->setNeedsLayout();
43     m_frameView->scheduleRelayout();
44 }
45 
removeAutoSizeMode()46 void FrameViewAutoSizeInfo::removeAutoSizeMode()
47 {
48     m_frameView->setLayoutSizeFixedToFrameSize(false);
49     m_frameView->setNeedsLayout();
50     m_frameView->scheduleRelayout();
51 
52     // Since autosize mode forces the scrollbar mode, change them to being auto.
53     m_frameView->setVerticalScrollbarLock(false);
54     m_frameView->setHorizontalScrollbarLock(false);
55     m_frameView->setScrollbarModes(ScrollbarAuto, ScrollbarAuto);
56 }
57 
autoSizeIfNeeded()58 void FrameViewAutoSizeInfo::autoSizeIfNeeded()
59 {
60     if (m_inAutoSize)
61         return;
62 
63     TemporaryChange<bool> changeInAutoSize(m_inAutoSize, true);
64 
65     Document* document = m_frameView->frame().document();
66     if (!document || !document->isActive())
67         return;
68 
69     Element* documentElement = document->documentElement();
70     if (!documentElement)
71         return;
72 
73     // If this is the first time we run autosize, start from small height and
74     // allow it to grow.
75     if (!m_didRunAutosize)
76         m_frameView->resize(m_frameView->frameRect().width(), m_minAutoSize.height());
77 
78     IntSize size = m_frameView->frameRect().size();
79 
80     // Do the resizing twice. The first time is basically a rough calculation using the preferred width
81     // which may result in a height change during the second iteration.
82     for (int i = 0; i < 2; i++) {
83         // Update various sizes including contentsSize, scrollHeight, etc.
84         document->updateLayoutIgnorePendingStylesheets();
85 
86         RenderView* renderView = document->renderView();
87         if (!renderView)
88             return;
89 
90         int width = renderView->minPreferredLogicalWidth();
91 
92         RenderBox* documentRenderBox = documentElement->renderBox();
93         if (!documentRenderBox)
94             return;
95 
96         int height = documentRenderBox->scrollHeight();
97         IntSize newSize(width, height);
98 
99         // Check to see if a scrollbar is needed for a given dimension and
100         // if so, increase the other dimension to account for the scrollbar.
101         // Since the dimensions are only for the view rectangle, once a
102         // dimension exceeds the maximum, there is no need to increase it further.
103         if (newSize.width() > m_maxAutoSize.width()) {
104             RefPtr<Scrollbar> localHorizontalScrollbar = m_frameView->horizontalScrollbar();
105             if (!localHorizontalScrollbar)
106                 localHorizontalScrollbar = m_frameView->createScrollbar(HorizontalScrollbar);
107             if (!localHorizontalScrollbar->isOverlayScrollbar())
108                 newSize.setHeight(newSize.height() + localHorizontalScrollbar->height());
109 
110             // Don't bother checking for a vertical scrollbar because the width is at
111             // already greater the maximum.
112         } else if (newSize.height() > m_maxAutoSize.height()) {
113             RefPtr<Scrollbar> localVerticalScrollbar = m_frameView->verticalScrollbar();
114             if (!localVerticalScrollbar)
115                 localVerticalScrollbar = m_frameView->createScrollbar(VerticalScrollbar);
116             if (!localVerticalScrollbar->isOverlayScrollbar())
117                 newSize.setWidth(newSize.width() + localVerticalScrollbar->width());
118 
119             // Don't bother checking for a horizontal scrollbar because the height is
120             // already greater the maximum.
121         }
122 
123         // Ensure the size is at least the min bounds.
124         newSize = newSize.expandedTo(m_minAutoSize);
125 
126         // Bound the dimensions by the max bounds and determine what scrollbars to show.
127         ScrollbarMode horizonalScrollbarMode = ScrollbarAlwaysOff;
128         if (newSize.width() > m_maxAutoSize.width()) {
129             newSize.setWidth(m_maxAutoSize.width());
130             horizonalScrollbarMode = ScrollbarAlwaysOn;
131         }
132         ScrollbarMode verticalScrollbarMode = ScrollbarAlwaysOff;
133         if (newSize.height() > m_maxAutoSize.height()) {
134             newSize.setHeight(m_maxAutoSize.height());
135             verticalScrollbarMode = ScrollbarAlwaysOn;
136         }
137 
138         if (newSize == size)
139             continue;
140 
141         // While loading only allow the size to increase (to avoid twitching during intermediate smaller states)
142         // unless autoresize has just been turned on or the maximum size is smaller than the current size.
143         if (m_didRunAutosize && size.height() <= m_maxAutoSize.height() && size.width() <= m_maxAutoSize.width()
144             && !m_frameView->frame().document()->loadEventFinished() && (newSize.height() < size.height() || newSize.width() < size.width()))
145             break;
146 
147         m_frameView->resize(newSize.width(), newSize.height());
148         // Force the scrollbar state to avoid the scrollbar code adding them and causing them to be needed. For example,
149         // a vertical scrollbar may cause text to wrap and thus increase the height (which is the only reason the scollbar is needed).
150         m_frameView->setVerticalScrollbarLock(false);
151         m_frameView->setHorizontalScrollbarLock(false);
152         m_frameView->setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
153     }
154     m_didRunAutosize = true;
155 }
156 
157 } // namespace blink
158