Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/tab/ThumbnailTabHelper.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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.chrome.browser.tab;
6
7 import android.content.res.Resources;
8 import android.graphics.Bitmap;
9 import android.graphics.Bitmap.Config;
10 import android.graphics.Canvas;
11 import android.graphics.Paint;
12 import android.graphics.Rect;
13 import android.os.Handler;
14 import android.text.TextUtils;
15 import android.util.Log;
16
17 import com.google.android.apps.chrome.R;
18
19 import org.chromium.chrome.browser.ChromeActivity;
20 import org.chromium.chrome.browser.EmptyTabObserver;
21 import org.chromium.chrome.browser.Tab;
22 import org.chromium.chrome.browser.TabObserver;
23 import org.chromium.chrome.browser.UrlConstants;
24 import org.chromium.chrome.browser.profiles.Profile;
25 import org.chromium.chrome.browser.util.MathUtils;
26 import org.chromium.content.browser.ContentReadbackHandler;
27 import org.chromium.content.browser.ContentViewCore;
28 import org.chromium.content_public.browser.GestureStateListener;
29 import org.chromium.content_public.browser.WebContents;
30 import org.chromium.ui.base.WindowAndroid;
31
32 /**
33 * Handles capturing most visited thumbnails for a tab.
34 */
35 public class ThumbnailTabHelper {
36
37 private static final String TAG = "ThumbnailTabHelper";
38
39 /** The general motivation for this value is giving the scrollbar fadeout
40 * animation sufficient time to finish before the capture executes. */
41 private static final int THUMBNAIL_CAPTURE_DELAY_MS = 350;
42
43 private final Tab mTab;
44 private final Handler mHandler;
45
46 private final int mThumbnailWidth;
47 private final int mThumbnailHeight;
48
49 private ContentViewCore mContentViewCore;
50 private boolean mThumbnailCapturedForLoad;
51 private boolean mIsRenderViewHostReady;
52 private boolean mWasRenderViewHostReady;
53
54 private final Runnable mThumbnailRunnable = new Runnable() {
55 @Override
56 public void run() {
57 // http://crbug.com/461506 : Do not get thumbnail unless render view host is ready.
58 if (!mIsRenderViewHostReady) return;
59
60 if (mThumbnailCapturedForLoad) return;
61 // Prevent redundant thumbnail capture attempts.
62 mThumbnailCapturedForLoad = true;
63 if (!canUpdateHistoryThumbnail()) {
64 // Allow a hidden tab to re-attempt capture in the future via |s how()|.
65 mThumbnailCapturedForLoad = !mTab.isHidden();
66 return;
67 }
68 if (!shouldUpdateThumbnail()) return;
69
70 // Scale the image so we're not copying more than we need to (from
71 // the GPU).
72 int[] dim = new int[] {
73 mTab.getWidth(), mTab.getHeight()
74 };
75 MathUtils.scaleToFitTargetSize(dim, mThumbnailWidth, mThumbnailHeigh t);
76
77 ContentReadbackHandler readbackHandler = getActivity().getContentRea dbackHandler();
78 if (readbackHandler == null || mTab.getContentViewCore() == null) re turn;
79 final String requestedUrl = mTab.getUrl();
80 ContentReadbackHandler.GetBitmapCallback bitmapCallback =
81 new ContentReadbackHandler.GetBitmapCallback() {
82 @Override
83 public void onFinishGetBitmap(Bitmap bitmap, int respons e) {
84 // Ensure that the URLs match for the requested page , and ensure
85 // that the page is still valid for thumbnail captur ing (i.e.
86 // not showing an error page).
87 if (bitmap == null
88 || !TextUtils.equals(requestedUrl, mTab.getU rl())
89 || !mThumbnailCapturedForLoad
90 || !canUpdateHistoryThumbnail()) {
91 return;
92 }
93 updateHistoryThumbnail(bitmap);
94 bitmap.recycle();
95 }
96 };
97 readbackHandler.getContentBitmapAsync(1, new Rect(0, 0, dim[0], dim[ 1]),
98 mTab.getContentViewCore(), Bitmap.Config.ARGB_8888, bitmapCa llback);
99 }
100 };
101
102 private final TabObserver mTabObserver = new EmptyTabObserver() {
103 @Override
104 public void onContentChanged(Tab tab) {
105 ThumbnailTabHelper.this.onContentChanged();
106 }
107
108 @Override
109 public void onCrash(Tab tab, boolean sadTabShown) {
110 cancelThumbnailCapture();
111 }
112
113 @Override
114 public void onPageLoadStarted(Tab tab) {
115 cancelThumbnailCapture();
116 mThumbnailCapturedForLoad = false;
117 }
118
119 @Override
120 public void onPageLoadFinished(Tab tab) {
121 rescheduleThumbnailCapture();
122 }
123
124 @Override
125 public void onPageLoadFailed(Tab tab, int errorCode) {
126 cancelThumbnailCapture();
127 }
128
129 @Override
130 public void onShown(Tab tab) {
131 // For tabs opened in the background, they may finish loading prior to becoming visible
132 // and the thumbnail capture triggered as part of load finish will b e skipped as the
133 // tab has nothing rendered. To handle this case, we also attempt t humbnail capture
134 // when showing the tab to give it a better chance to have valid con tent.
135 rescheduleThumbnailCapture();
136 }
137
138 @Override
139 public void onClosingStateChanged(Tab tab, boolean closing) {
140 if (closing) cancelThumbnailCapture();
141 }
142
143 @Override
144 public void onDestroyed(Tab tab) {
145 mTab.removeObserver(mTabObserver);
146 if (mContentViewCore != null) {
147 mContentViewCore.removeGestureStateListener(mGestureListener);
148 mContentViewCore = null;
149 }
150 }
151
152 @Override
153 public void onDidStartProvisionalLoadForFrame(
154 Tab tab, long frameId, long parentFrameId, boolean isMainFrame, String validatedUrl,
155 boolean isErrorPage, boolean isIframeSrcdoc) {
156 if (isMainFrame) {
157 mWasRenderViewHostReady = mIsRenderViewHostReady;
158 mIsRenderViewHostReady = false;
159 }
160 }
161
162 @Override
163 public void onDidFailLoad(
164 Tab tab, boolean isProvisionalLoad, boolean isMainFrame, int err orCode,
165 String description, String failingUrl) {
166 // For a case that URL overriding happens, we should recover |mIsRen derViewHostReady| to
167 // its old value to enable capturing thumbnail of the current page.
168 // If this failure shows an error page, capturing thumbnail will be denied anyway in
169 // canUpdateHistoryThumbnail().
170 if (isProvisionalLoad && isMainFrame) mIsRenderViewHostReady = mWasR enderViewHostReady;
171 }
172
173 @Override
174 public void onDidCommitProvisionalLoadForFrame(
175 Tab tab, long frameId, boolean isMainFrame, String url, int tran sitionType) {
176 if (isMainFrame) mIsRenderViewHostReady = true;
177 }
178 };
179
180 private GestureStateListener mGestureListener = new GestureStateListener() {
181 @Override
182 public void onFlingStartGesture(int vx, int vy, int scrollOffsetY, int s crollExtentY) {
183 cancelThumbnailCapture();
184 }
185
186 @Override
187 public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) {
188 rescheduleThumbnailCapture();
189 }
190
191 @Override
192 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) {
193 cancelThumbnailCapture();
194 }
195
196 @Override
197 public void onScrollEnded(int scrollOffsetY, int scrollExtentY) {
198 rescheduleThumbnailCapture();
199 }
200 };
201
202 /**
203 * Creates a thumbnail tab helper for the given tab.
204 * @param tab The Tab whose thumbnails will be generated by this helper.
205 */
206 public static void createForTab(Tab tab) {
207 new ThumbnailTabHelper(tab);
208 }
209
210 /**
211 * Constructs the thumbnail tab helper for a given Tab.
212 * @param tab The Tab whose thumbnails will be generated by this helper.
213 */
214 private ThumbnailTabHelper(Tab tab) {
215 mTab = tab;
216 mTab.addObserver(mTabObserver);
217
218 mHandler = new Handler();
219
220 Resources res = tab.getWindowAndroid().getApplicationContext().getResour ces();
221 mThumbnailWidth = res.getDimensionPixelSize(R.dimen.most_visited_thumbna il_width);
222 mThumbnailHeight = res.getDimensionPixelSize(R.dimen.most_visited_thumbn ail_height);
223
224 onContentChanged();
225 }
226
227 private void onContentChanged() {
228 if (mContentViewCore != null) {
229 mContentViewCore.removeGestureStateListener(mGestureListener);
230 }
231
232 mContentViewCore = mTab.getContentViewCore();
233 if (mContentViewCore != null) {
234 mContentViewCore.addGestureStateListener(mGestureListener);
235 nativeInitThumbnailHelper(mContentViewCore.getWebContents());
236 }
237 }
238
239 private ChromeActivity getActivity() {
240 WindowAndroid window = mTab.getWindowAndroid();
241 return (ChromeActivity) window.getActivity().get();
242 }
243
244 private void cancelThumbnailCapture() {
245 mHandler.removeCallbacks(mThumbnailRunnable);
246 }
247
248 private void rescheduleThumbnailCapture() {
249 if (mThumbnailCapturedForLoad) return;
250 cancelThumbnailCapture();
251 // Capture will be rescheduled when the GestureStateListener receives a
252 // scroll or fling end notification.
253 if (mTab.getContentViewCore() != null
254 && mTab.getContentViewCore().isScrollInProgress()) {
255 return;
256 }
257 mHandler.postDelayed(mThumbnailRunnable, THUMBNAIL_CAPTURE_DELAY_MS);
258 }
259
260 private boolean shouldUpdateThumbnail() {
261 return nativeShouldUpdateThumbnail(mTab.getProfile(), mTab.getUrl());
262 }
263
264 private void updateThumbnail(Bitmap bitmap) {
265 if (mTab.getContentViewCore() != null) {
266 final boolean atTop = mTab.getContentViewCore().computeVerticalScrol lOffset() == 0;
267 nativeUpdateThumbnail(mTab.getWebContents(), bitmap, atTop);
268 }
269 }
270
271 private boolean canUpdateHistoryThumbnail() {
272 String url = mTab.getUrl();
273 if (url.startsWith(UrlConstants.CHROME_SCHEME)
274 || url.startsWith(UrlConstants.CHROME_NATIVE_SCHEME)) {
275 return false;
276 }
277 return mTab.isReady()
278 && !mTab.isShowingErrorPage()
279 && !mTab.isHidden()
280 && !mTab.isShowingSadTab()
281 && !mTab.isShowingInterstitialPage()
282 && mTab.getProgress() == 100
283 && mTab.getWidth() > 0
284 && mTab.getHeight() > 0;
285 }
286
287 private void updateHistoryThumbnail(Bitmap bitmap) {
288 if (mTab.isIncognito()) return;
289
290 // TODO(yusufo): It will probably be faster and more efficient on resour ces to do this on
291 // the native side, but the thumbnail_generator code has to be refactore d a bit to allow
292 // creating a downsized version of a bitmap progressively.
293 if (bitmap.getWidth() != mThumbnailWidth
294 || bitmap.getHeight() != mThumbnailHeight
295 || bitmap.getConfig() != Config.ARGB_8888) {
296 try {
297 int[] dim = new int[] {
298 bitmap.getWidth(), bitmap.getHeight()
299 };
300 // If the thumbnail size is small compared to the bitmap size do wnsize in
301 // two stages. This makes the final quality better.
302 float scale = Math.max(
303 (float) mThumbnailWidth / dim[0],
304 (float) mThumbnailHeight / dim[1]);
305 int adjustedWidth = (scale < 1)
306 ? mThumbnailWidth * (int) (1 / Math.sqrt(scale)) : mThum bnailWidth;
307 int adjustedHeight = (scale < 1)
308 ? mThumbnailHeight * (int) (1 / Math.sqrt(scale)) : mThu mbnailHeight;
309 scale = MathUtils.scaleToFitTargetSize(dim, adjustedWidth, adjus tedHeight);
310 // Horizontally center the source bitmap in the final result.
311 float leftOffset = (adjustedWidth - dim[0]) / 2.0f / scale;
312 Bitmap tmpBitmap = Bitmap.createBitmap(adjustedWidth,
313 adjustedHeight, Config.ARGB_8888);
314 Canvas c = new Canvas(tmpBitmap);
315 c.scale(scale, scale);
316 c.drawBitmap(bitmap, leftOffset, 0, new Paint(Paint.FILTER_BITMA P_FLAG));
317 if (scale < 1) {
318 tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
319 mThumbnailWidth, mThumbnailHeight, true);
320 }
321 updateThumbnail(tmpBitmap);
322 tmpBitmap.recycle();
323 } catch (OutOfMemoryError ex) {
324 Log.w(TAG, "OutOfMemoryError while updating the history thumbnai l.");
325 }
326 } else {
327 updateThumbnail(bitmap);
328 }
329 }
330
331 private static native void nativeInitThumbnailHelper(WebContents webContents );
332 private static native void nativeUpdateThumbnail(
333 WebContents webContents, Bitmap bitmap, boolean atTop);
334 private static native boolean nativeShouldUpdateThumbnail(Profile profile, S tring url);
335 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698