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 15 FrameViewAutoSizeInfo::FrameViewAutoSizeInfo(FrameView* view) 16 : m_frameView(view) 17 , m_inAutoSize(false) 18 , m_didRunAutosize(false) 19 { 20 ASSERT(m_frameView); 21 } 22 23 FrameViewAutoSizeInfo::~FrameViewAutoSizeInfo() 24 { 25 removeAutoSizeMode(); 26 } 27 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 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 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