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.
5 #include "chrome/browser/android/compositor/tab_content_manager.h"
7 #include <android/bitmap.h>
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h"
11 #include "base/android/scoped_java_ref.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "cc/layers/layer.h"
15 #include "chrome/browser/android/compositor/layer/thumbnail_layer.h"
16 #include "chrome/browser/android/tab_android.h"
17 #include "chrome/browser/android/thumbnail/thumbnail.h"
18 #include "content/public/browser/android/content_view_core.h"
19 #include "content/public/browser/readback_types.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/web_contents.h"
22 #include "jni/TabContentManager_jni.h"
23 #include "ui/android/resources/ui_resource_provider.h"
24 #include "ui/gfx/android/java_bitmap.h"
25 #include "ui/gfx/geometry/rect.h"
30 const size_t kMaxReadbacks
= 1;
31 typedef base::Callback
<void(float, const SkBitmap
&)> TabReadbackCallback
;
38 class TabContentManager::TabReadbackRequest
{
40 TabReadbackRequest(jobject content_view_core
,
41 float thumbnail_scale
,
42 const TabReadbackCallback
& end_callback
)
43 : thumbnail_scale_(thumbnail_scale
),
44 end_callback_(end_callback
),
45 drop_after_readback_(false),
47 JNIEnv
* env
= base::android::AttachCurrentThread();
48 j_content_view_core_
.Reset(env
, content_view_core
);
51 virtual ~TabReadbackRequest() {}
54 JNIEnv
* env
= base::android::AttachCurrentThread();
55 content::ReadbackRequestCallback result_callback
=
56 base::Bind(&TabReadbackRequest::OnFinishGetTabThumbnailBitmap
,
57 weak_factory_
.GetWeakPtr());
59 if (j_content_view_core_
.is_null()) {
60 result_callback
.Run(SkBitmap(), content::READBACK_FAILED
);
64 content::ContentViewCore
* view
=
65 content::ContentViewCore::GetNativeContentViewCore(
66 env
, j_content_view_core_
.obj());
69 result_callback
.Run(SkBitmap(), content::READBACK_FAILED
);
73 DCHECK(view
->GetWebContents());
74 view
->GetWebContents()->GetRenderViewHost()->LockBackingStore();
76 SkColorType color_type
= kN32_SkColorType
;
78 // Calling this method with an empty rect will return a bitmap of the size
80 view
->GetScaledContentBitmap(thumbnail_scale_
, color_type
, gfx::Rect(),
84 void OnFinishGetTabThumbnailBitmap(const SkBitmap
& bitmap
,
85 content::ReadbackResponse response
) {
86 DCHECK(!j_content_view_core_
.is_null());
87 JNIEnv
* env
= base::android::AttachCurrentThread();
88 content::ContentViewCore
* view
=
89 content::ContentViewCore::GetNativeContentViewCore(
90 env
, j_content_view_core_
.obj());
93 DCHECK(view
->GetWebContents());
94 view
->GetWebContents()->GetRenderViewHost()->UnlockBackingStore();
97 // TODO(jdduke): Tailor response to different failure values appropriately.
98 if (response
!= content::READBACK_SUCCESS
|| drop_after_readback_
) {
99 end_callback_
.Run(0.f
, SkBitmap());
103 SkBitmap result_bitmap
= bitmap
;
104 result_bitmap
.setImmutable();
105 end_callback_
.Run(thumbnail_scale_
, bitmap
);
108 void SetToDropAfterReadback() { drop_after_readback_
= true; }
111 base::android::ScopedJavaGlobalRef
<jobject
> j_content_view_core_
;
112 const float thumbnail_scale_
;
113 TabReadbackCallback end_callback_
;
114 bool drop_after_readback_
;
116 base::WeakPtrFactory
<TabReadbackRequest
> weak_factory_
;
118 DISALLOW_COPY_AND_ASSIGN(TabReadbackRequest
);
122 TabContentManager
* TabContentManager::FromJavaObject(jobject jobj
) {
125 return reinterpret_cast<TabContentManager
*>(
126 Java_TabContentManager_getNativePtr(base::android::AttachCurrentThread(),
130 TabContentManager::TabContentManager(JNIEnv
* env
,
132 jstring disk_cache_path
,
133 jint default_cache_size
,
134 jint approximation_cache_size
,
135 jint compression_queue_max_size
,
136 jint write_queue_max_size
,
137 jboolean use_approximation_thumbnail
)
138 : weak_java_tab_content_manager_(env
, obj
), weak_factory_(this) {
139 std::string disk_cache_path_str
=
140 base::android::ConvertJavaStringToUTF8(env
, disk_cache_path
);
141 thumbnail_cache_
= make_scoped_ptr(new ThumbnailCache(
142 disk_cache_path_str
, (size_t)default_cache_size
,
143 (size_t)approximation_cache_size
, (size_t)compression_queue_max_size
,
144 (size_t)write_queue_max_size
, use_approximation_thumbnail
));
145 thumbnail_cache_
->AddThumbnailStoreObserver(this);
148 TabContentManager::~TabContentManager() {
151 void TabContentManager::Destroy(JNIEnv
* env
, jobject obj
) {
152 thumbnail_cache_
->RemoveThumbnailStoreObserver(this);
156 void TabContentManager::SetUIResourceProvider(JNIEnv
* env
,
158 jlong ui_resource_provider_ptr
) {
159 ui::UIResourceProvider
* ui_resource_provider
=
160 reinterpret_cast<ui::UIResourceProvider
*>(ui_resource_provider_ptr
);
161 SetUIResourceProvider(ui_resource_provider
);
164 void TabContentManager::SetUIResourceProvider(
165 ui::UIResourceProvider
* ui_resource_provider
) {
166 thumbnail_cache_
->SetUIResourceProvider(ui_resource_provider
);
169 scoped_refptr
<cc::Layer
> TabContentManager::GetLiveLayer(int tab_id
) {
170 scoped_refptr
<cc::Layer
> layer
= live_layer_list_
[tab_id
];
177 scoped_refptr
<ThumbnailLayer
> TabContentManager::GetStaticLayer(
179 bool force_disk_read
) {
180 Thumbnail
* thumbnail
= thumbnail_cache_
->Get(tab_id
, force_disk_read
, true);
181 scoped_refptr
<ThumbnailLayer
> static_layer
= static_layer_cache_
[tab_id
];
183 if (!thumbnail
|| !thumbnail
->ui_resource_id()) {
184 if (static_layer
.get()) {
185 static_layer
->layer()->RemoveFromParent();
186 static_layer_cache_
.erase(tab_id
);
191 if (!static_layer
.get()) {
192 static_layer
= ThumbnailLayer::Create();
193 static_layer_cache_
[tab_id
] = static_layer
;
196 static_layer
->SetThumbnail(thumbnail
);
200 void TabContentManager::AttachLiveLayer(int tab_id
,
201 scoped_refptr
<cc::Layer
> layer
) {
205 scoped_refptr
<cc::Layer
> cached_layer
= live_layer_list_
[tab_id
];
206 if (cached_layer
!= layer
)
207 live_layer_list_
[tab_id
] = layer
;
210 void TabContentManager::DetachLiveLayer(int tab_id
,
211 scoped_refptr
<cc::Layer
> layer
) {
212 scoped_refptr
<cc::Layer
> current_layer
= live_layer_list_
[tab_id
];
213 if (!current_layer
.get()) {
214 // Empty cached layer should not exist but it is ok if it happens.
218 // We need to remove if we're getting a detach for our current layer or we're
219 // getting a detach with NULL and we have a current layer, which means remove
221 if (current_layer
.get() &&
222 (layer
.get() == current_layer
.get() || !layer
.get())) {
223 live_layer_list_
.erase(tab_id
);
227 void TabContentManager::OnFinishDecompressThumbnail(int tab_id
,
230 JNIEnv
* env
= base::android::AttachCurrentThread();
231 ScopedJavaLocalRef
<jobject
> java_bitmap
;
233 java_bitmap
= gfx::ConvertToJavaBitmap(&bitmap
);
235 Java_TabContentManager_notifyDecompressBitmapFinished(
236 env
, weak_java_tab_content_manager_
.get(env
).obj(), tab_id
,
240 jboolean
TabContentManager::HasFullCachedThumbnail(JNIEnv
* env
,
243 return thumbnail_cache_
->Get(tab_id
, false, false) != nullptr;
246 void TabContentManager::CacheTab(JNIEnv
* env
,
249 jobject content_view_core
,
250 jfloat thumbnail_scale
) {
251 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, tab
);
253 int tab_id
= tab_android
->GetAndroidId();
254 GURL url
= tab_android
->GetURL();
256 content::ContentViewCore
* view
=
257 content::ContentViewCore::GetNativeContentViewCore(env
,
260 if (thumbnail_cache_
->CheckAndUpdateThumbnailMetaData(tab_id
, url
)) {
262 !view
->GetWebContents()
263 ->GetRenderViewHost()
264 ->CanCopyFromBackingStore() ||
265 pending_tab_readbacks_
.find(tab_id
) != pending_tab_readbacks_
.end() ||
266 pending_tab_readbacks_
.size() >= kMaxReadbacks
) {
267 thumbnail_cache_
->Remove(tab_id
);
271 TabReadbackCallback readback_done_callback
=
272 base::Bind(&TabContentManager::PutThumbnailIntoCache
,
273 weak_factory_
.GetWeakPtr(), tab_id
);
274 scoped_ptr
<TabReadbackRequest
> readback_request
=
275 make_scoped_ptr(new TabReadbackRequest(
276 content_view_core
, thumbnail_scale
, readback_done_callback
));
277 pending_tab_readbacks_
.set(tab_id
, readback_request
.Pass());
278 pending_tab_readbacks_
.get(tab_id
)->Run();
282 void TabContentManager::CacheTabWithBitmap(JNIEnv
* env
,
286 jfloat thumbnail_scale
) {
287 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, tab
);
289 int tab_id
= tab_android
->GetAndroidId();
290 GURL url
= tab_android
->GetURL();
292 gfx::JavaBitmap
java_bitmap_lock(bitmap
);
293 SkBitmap skbitmap
= gfx::CreateSkBitmapFromJavaBitmap(java_bitmap_lock
);
294 skbitmap
.setImmutable();
296 if (thumbnail_cache_
->CheckAndUpdateThumbnailMetaData(tab_id
, url
))
297 PutThumbnailIntoCache(tab_id
, thumbnail_scale
, skbitmap
);
300 void TabContentManager::InvalidateIfChanged(JNIEnv
* env
,
304 thumbnail_cache_
->InvalidateThumbnailIfChanged(
305 tab_id
, GURL(base::android::ConvertJavaStringToUTF8(env
, jurl
)));
308 void TabContentManager::UpdateVisibleIds(JNIEnv
* env
,
310 jintArray priority
) {
311 std::list
<int> priority_ids
;
312 jsize length
= env
->GetArrayLength(priority
);
313 jint
* ints
= env
->GetIntArrayElements(priority
, NULL
);
314 for (jsize i
= 0; i
< length
; ++i
)
315 priority_ids
.push_back(static_cast<int>(ints
[i
]));
317 env
->ReleaseIntArrayElements(priority
, ints
, JNI_ABORT
);
318 thumbnail_cache_
->UpdateVisibleIds(priority_ids
);
321 void TabContentManager::RemoveTabThumbnail(JNIEnv
* env
,
324 TabReadbackRequestMap::iterator readback_iter
=
325 pending_tab_readbacks_
.find(tab_id
);
326 if (readback_iter
!= pending_tab_readbacks_
.end())
327 readback_iter
->second
->SetToDropAfterReadback();
328 thumbnail_cache_
->Remove(tab_id
);
331 void TabContentManager::RemoveTabThumbnailFromDiskAtAndAboveId(
334 jint min_forbidden_id
) {
335 thumbnail_cache_
->RemoveFromDiskAtAndAboveId(min_forbidden_id
);
338 void TabContentManager::GetDecompressedThumbnail(JNIEnv
* env
,
341 base::Callback
<void(bool, SkBitmap
)> decompress_done_callback
=
342 base::Bind(&TabContentManager::OnFinishDecompressThumbnail
,
343 weak_factory_
.GetWeakPtr(), reinterpret_cast<int>(tab_id
));
344 thumbnail_cache_
->DecompressThumbnailFromFile(reinterpret_cast<int>(tab_id
),
345 decompress_done_callback
);
348 void TabContentManager::OnFinishedThumbnailRead(int tab_id
) {
349 JNIEnv
* env
= base::android::AttachCurrentThread();
350 Java_TabContentManager_notifyListenersOfThumbnailChange(
351 env
, weak_java_tab_content_manager_
.get(env
).obj(), tab_id
);
354 void TabContentManager::PutThumbnailIntoCache(int tab_id
,
355 float thumbnail_scale
,
356 const SkBitmap
& bitmap
) {
357 TabReadbackRequestMap::iterator readback_iter
=
358 pending_tab_readbacks_
.find(tab_id
);
360 if (readback_iter
!= pending_tab_readbacks_
.end())
361 pending_tab_readbacks_
.erase(tab_id
);
363 if (thumbnail_scale
> 0 && !bitmap
.empty())
364 thumbnail_cache_
->Put(tab_id
, bitmap
, thumbnail_scale
);
367 bool RegisterTabContentManager(JNIEnv
* env
) {
368 return RegisterNativesImpl(env
);
371 // ----------------------------------------------------------------------------
372 // Native JNI methods
373 // ----------------------------------------------------------------------------
375 jlong
Init(JNIEnv
* env
,
377 jstring disk_cache_path
,
378 jint default_cache_size
,
379 jint approximation_cache_size
,
380 jint compression_queue_max_size
,
381 jint write_queue_max_size
,
382 jboolean use_approximation_thumbnail
) {
383 TabContentManager
* manager
= new TabContentManager(
384 env
, obj
, disk_cache_path
, default_cache_size
, approximation_cache_size
,
385 compression_queue_max_size
, write_queue_max_size
,
386 use_approximation_thumbnail
);
387 return reinterpret_cast<intptr_t>(manager
);
390 } // namespace android
391 } // namespace chrome