Home | History | Annotate | Download | only in frame
      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