Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / android_webview / java / src / org / chromium / android_webview / AwLayoutSizer.java
1 // Copyright 2012 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 package org.chromium.android_webview;
6
7 import android.view.View;
8 import android.view.View.MeasureSpec;
9
10 /**
11  * Helper methods used to manage the layout of the View that contains AwContents.
12  */
13 public class AwLayoutSizer {
14     // These are used to prevent a re-layout if the content size changes within a dimension that is
15     // fixed by the view system.
16     private boolean mWidthMeasurementIsFixed;
17     private boolean mHeightMeasurementIsFixed;
18
19     // Size of the rendered content, as reported by native.
20     private int mContentHeightCss;
21     private int mContentWidthCss;
22
23     // Page scale factor. This is set to zero initially so that we don't attempt to do a layout if
24     // we get the content size change notification first and a page scale change second.
25     private float mPageScaleFactor = 0.0f;
26
27     // Whether to postpone layout requests.
28     private boolean mFreezeLayoutRequests;
29     // Did we try to request a layout since the last time mPostponeLayoutRequests was set to true.
30     private boolean mFrozenLayoutRequestPending;
31
32     private double mDIPScale;
33
34     // Was our height larger than the AT_MOST constraint the last time onMeasure was called?
35     private boolean mHeightMeasurementLimited;
36     // If mHeightMeasurementLimited is true then this contains the height limit.
37     private int mHeightMeasurementLimit;
38
39     // Callback object for interacting with the View.
40     private Delegate mDelegate;
41
42     /**
43      * Delegate interface through which the AwLayoutSizer communicates with the view it's sizing.
44      */
45     public interface Delegate {
46         void requestLayout();
47         void setMeasuredDimension(int measuredWidth, int measuredHeight);
48         boolean isLayoutParamsHeightWrapContent();
49         void setForceZeroLayoutHeight(boolean forceZeroHeight);
50     }
51
52     /**
53      * Default constructor. Note: both setDelegate and setDIPScale must be called before the class
54      * is ready for use.
55      */
56     public AwLayoutSizer() {
57     }
58
59     public void setDelegate(Delegate delegate) {
60         mDelegate = delegate;
61     }
62
63     public void setDIPScale(double dipScale) {
64         mDIPScale = dipScale;
65     }
66
67     /**
68      * Postpone requesting layouts till unfreezeLayoutRequests is called.
69      */
70     public void freezeLayoutRequests() {
71         mFreezeLayoutRequests = true;
72         mFrozenLayoutRequestPending = false;
73     }
74
75     /**
76      * Stop postponing layout requests and request layout if such a request would have been made
77      * had the freezeLayoutRequests method not been called before.
78      */
79     public void unfreezeLayoutRequests() {
80         mFreezeLayoutRequests = false;
81         if (mFrozenLayoutRequestPending) {
82             mFrozenLayoutRequestPending = false;
83             mDelegate.requestLayout();
84         }
85     }
86
87     /**
88      * Update the contents size.
89      * This should be called whenever the content size changes (due to DOM manipulation or page
90      * load, for example).
91      * The width and height should be in CSS pixels.
92      */
93     public void onContentSizeChanged(int widthCss, int heightCss) {
94         doUpdate(widthCss, heightCss, mPageScaleFactor);
95     }
96
97     /**
98      * Update the contents page scale.
99      * This should be called whenever the content page scale factor changes (due to pinch zoom, for
100      * example).
101      */
102     public void onPageScaleChanged(float pageScaleFactor) {
103         doUpdate(mContentWidthCss, mContentHeightCss, pageScaleFactor);
104     }
105
106     private void doUpdate(int widthCss, int heightCss, float pageScaleFactor) {
107         // We want to request layout only if the size or scale change, however if any of the
108         // measurements are 'fixed', then changing the underlying size won't have any effect, so we
109         // ignore changes to dimensions that are 'fixed'.
110         final int heightPix = (int) (heightCss * mPageScaleFactor * mDIPScale);
111         boolean pageScaleChanged = mPageScaleFactor != pageScaleFactor;
112         boolean contentHeightChangeMeaningful = !mHeightMeasurementIsFixed &&
113             (!mHeightMeasurementLimited || heightPix < mHeightMeasurementLimit);
114         boolean pageScaleChangeMeaningful =
115             !mWidthMeasurementIsFixed || contentHeightChangeMeaningful;
116         boolean layoutNeeded = (mContentWidthCss != widthCss && !mWidthMeasurementIsFixed) ||
117             (mContentHeightCss != heightCss && contentHeightChangeMeaningful) ||
118             (pageScaleChanged && pageScaleChangeMeaningful);
119
120         mContentWidthCss = widthCss;
121         mContentHeightCss = heightCss;
122         mPageScaleFactor = pageScaleFactor;
123
124         if (layoutNeeded) {
125             if (mFreezeLayoutRequests) {
126                 mFrozenLayoutRequestPending = true;
127             } else {
128                 mDelegate.requestLayout();
129             }
130         }
131     }
132
133     /**
134      * Calculate the size of the view.
135      * This is designed to be used to implement the android.view.View#onMeasure() method.
136      */
137     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
138         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
139         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
140         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
141         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
142
143         int contentHeightPix = (int) (mContentHeightCss * mPageScaleFactor * mDIPScale);
144         int contentWidthPix = (int) (mContentWidthCss * mPageScaleFactor * mDIPScale);
145
146         int measuredHeight = contentHeightPix;
147         int measuredWidth = contentWidthPix;
148
149         // Always use the given size unless unspecified. This matches WebViewClassic behavior.
150         mWidthMeasurementIsFixed = (widthMode != MeasureSpec.UNSPECIFIED);
151         mHeightMeasurementIsFixed = (heightMode == MeasureSpec.EXACTLY);
152         mHeightMeasurementLimited =
153             (heightMode == MeasureSpec.AT_MOST) && (contentHeightPix > heightSize);
154         mHeightMeasurementLimit = heightSize;
155
156         if (mHeightMeasurementIsFixed || mHeightMeasurementLimited) {
157             measuredHeight = heightSize;
158         }
159
160         if (mWidthMeasurementIsFixed) {
161             measuredWidth = widthSize;
162         }
163
164         if (measuredHeight < contentHeightPix) {
165             measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
166         }
167
168         if (measuredWidth < contentWidthPix) {
169             measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
170         }
171
172         mDelegate.setMeasuredDimension(measuredWidth, measuredHeight);
173     }
174
175     /**
176      * Notify the AwLayoutSizer that the size of the view has changed.
177      * This should be called by the Android view system after onMeasure if the view's size has
178      * changed.
179      */
180     public void onSizeChanged(int w, int h, int ow, int oh) {
181         updateLayoutSettings();
182     }
183
184     /**
185      * Notify the AwLayoutSizer that the layout pass requested via Delegate.requestLayout has
186      * completed.
187      * This should be called after onSizeChanged regardless of whether the size has changed or not.
188      */
189     public void onLayoutChange() {
190         updateLayoutSettings();
191     }
192
193     // This needs to be called every time either the physical size of the view is changed or layout
194     // params are updated.
195     private void updateLayoutSettings() {
196         mDelegate.setForceZeroLayoutHeight(mDelegate.isLayoutParamsHeightWrapContent());
197     }
198 }