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 jint default_cache_size
,
133 jint approximation_cache_size
,
134 jint compression_queue_max_size
,
135 jint write_queue_max_size
,
136 jboolean use_approximation_thumbnail
)
137 : weak_java_tab_content_manager_(env
, obj
), weak_factory_(this) {
138 thumbnail_cache_
= make_scoped_ptr(new ThumbnailCache(
139 (size_t)default_cache_size
, (size_t)approximation_cache_size
,
140 (size_t)compression_queue_max_size
, (size_t)write_queue_max_size
,
141 use_approximation_thumbnail
));
142 thumbnail_cache_
->AddThumbnailCacheObserver(this);
145 TabContentManager::~TabContentManager() {
148 void TabContentManager::Destroy(JNIEnv
* env
, jobject obj
) {
149 thumbnail_cache_
->RemoveThumbnailCacheObserver(this);
153 void TabContentManager::SetUIResourceProvider(JNIEnv
* env
,
155 jlong ui_resource_provider_ptr
) {
156 ui::UIResourceProvider
* ui_resource_provider
=
157 reinterpret_cast<ui::UIResourceProvider
*>(ui_resource_provider_ptr
);
158 SetUIResourceProvider(ui_resource_provider
);
161 void TabContentManager::SetUIResourceProvider(
162 ui::UIResourceProvider
* ui_resource_provider
) {
163 thumbnail_cache_
->SetUIResourceProvider(ui_resource_provider
);
166 scoped_refptr
<cc::Layer
> TabContentManager::GetLiveLayer(int tab_id
) {
167 scoped_refptr
<cc::Layer
> layer
= live_layer_list_
[tab_id
];
174 scoped_refptr
<ThumbnailLayer
> TabContentManager::GetStaticLayer(
176 bool force_disk_read
) {
177 Thumbnail
* thumbnail
= thumbnail_cache_
->Get(tab_id
, force_disk_read
, true);
178 scoped_refptr
<ThumbnailLayer
> static_layer
= static_layer_cache_
[tab_id
];
180 if (!thumbnail
|| !thumbnail
->ui_resource_id()) {
181 if (static_layer
.get()) {
182 static_layer
->layer()->RemoveFromParent();
183 static_layer_cache_
.erase(tab_id
);
188 if (!static_layer
.get()) {
189 static_layer
= ThumbnailLayer::Create();
190 static_layer_cache_
[tab_id
] = static_layer
;
193 static_layer
->SetThumbnail(thumbnail
);
197 void TabContentManager::AttachLiveLayer(int tab_id
,
198 scoped_refptr
<cc::Layer
> layer
) {
202 scoped_refptr
<cc::Layer
> cached_layer
= live_layer_list_
[tab_id
];
203 if (cached_layer
!= layer
)
204 live_layer_list_
[tab_id
] = layer
;
207 void TabContentManager::DetachLiveLayer(int tab_id
,
208 scoped_refptr
<cc::Layer
> layer
) {
209 scoped_refptr
<cc::Layer
> current_layer
= live_layer_list_
[tab_id
];
210 if (!current_layer
.get()) {
211 // Empty cached layer should not exist but it is ok if it happens.
215 // We need to remove if we're getting a detach for our current layer or we're
216 // getting a detach with NULL and we have a current layer, which means remove
218 if (current_layer
.get() &&
219 (layer
.get() == current_layer
.get() || !layer
.get())) {
220 live_layer_list_
.erase(tab_id
);
224 void TabContentManager::OnFinishDecompressThumbnail(int tab_id
,
227 JNIEnv
* env
= base::android::AttachCurrentThread();
228 ScopedJavaLocalRef
<jobject
> java_bitmap
;
230 java_bitmap
= gfx::ConvertToJavaBitmap(&bitmap
);
232 Java_TabContentManager_notifyDecompressBitmapFinished(
233 env
, weak_java_tab_content_manager_
.get(env
).obj(), tab_id
,
237 jboolean
TabContentManager::HasFullCachedThumbnail(JNIEnv
* env
,
240 return thumbnail_cache_
->Get(tab_id
, false, false) != nullptr;
243 void TabContentManager::CacheTab(JNIEnv
* env
,
246 jobject content_view_core
,
247 jfloat thumbnail_scale
) {
248 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, tab
);
250 int tab_id
= tab_android
->GetAndroidId();
251 GURL url
= tab_android
->GetURL();
253 content::ContentViewCore
* view
=
254 content::ContentViewCore::GetNativeContentViewCore(env
,
257 if (thumbnail_cache_
->CheckAndUpdateThumbnailMetaData(tab_id
, url
)) {
259 !view
->GetWebContents()
260 ->GetRenderViewHost()
261 ->CanCopyFromBackingStore() ||
262 pending_tab_readbacks_
.find(tab_id
) != pending_tab_readbacks_
.end() ||
263 pending_tab_readbacks_
.size() >= kMaxReadbacks
) {
264 thumbnail_cache_
->Remove(tab_id
);
268 TabReadbackCallback readback_done_callback
=
269 base::Bind(&TabContentManager::PutThumbnailIntoCache
,
270 weak_factory_
.GetWeakPtr(), tab_id
);
271 scoped_ptr
<TabReadbackRequest
> readback_request
=
272 make_scoped_ptr(new TabReadbackRequest(
273 content_view_core
, thumbnail_scale
, readback_done_callback
));
274 pending_tab_readbacks_
.set(tab_id
, readback_request
.Pass());
275 pending_tab_readbacks_
.get(tab_id
)->Run();
279 void TabContentManager::CacheTabWithBitmap(JNIEnv
* env
,
283 jfloat thumbnail_scale
) {
284 TabAndroid
* tab_android
= TabAndroid::GetNativeTab(env
, tab
);
286 int tab_id
= tab_android
->GetAndroidId();
287 GURL url
= tab_android
->GetURL();
289 gfx::JavaBitmap
java_bitmap_lock(bitmap
);
290 SkBitmap skbitmap
= gfx::CreateSkBitmapFromJavaBitmap(java_bitmap_lock
);
291 skbitmap
.setImmutable();
293 if (thumbnail_cache_
->CheckAndUpdateThumbnailMetaData(tab_id
, url
))
294 PutThumbnailIntoCache(tab_id
, thumbnail_scale
, skbitmap
);
297 void TabContentManager::InvalidateIfChanged(JNIEnv
* env
,
301 thumbnail_cache_
->InvalidateThumbnailIfChanged(
302 tab_id
, GURL(base::android::ConvertJavaStringToUTF8(env
, jurl
)));
305 void TabContentManager::UpdateVisibleIds(JNIEnv
* env
,
307 jintArray priority
) {
308 std::list
<int> priority_ids
;
309 jsize length
= env
->GetArrayLength(priority
);
310 jint
* ints
= env
->GetIntArrayElements(priority
, NULL
);
311 for (jsize i
= 0; i
< length
; ++i
)
312 priority_ids
.push_back(static_cast<int>(ints
[i
]));
314 env
->ReleaseIntArrayElements(priority
, ints
, JNI_ABORT
);
315 thumbnail_cache_
->UpdateVisibleIds(priority_ids
);
318 void TabContentManager::RemoveTabThumbnail(JNIEnv
* env
,
321 TabReadbackRequestMap::iterator readback_iter
=
322 pending_tab_readbacks_
.find(tab_id
);
323 if (readback_iter
!= pending_tab_readbacks_
.end())
324 readback_iter
->second
->SetToDropAfterReadback();
325 thumbnail_cache_
->Remove(tab_id
);
328 void TabContentManager::RemoveTabThumbnailFromDiskAtAndAboveId(
331 jint min_forbidden_id
) {
332 thumbnail_cache_
->RemoveFromDiskAtAndAboveId(min_forbidden_id
);
335 void TabContentManager::GetDecompressedThumbnail(JNIEnv
* env
,
338 base::Callback
<void(bool, SkBitmap
)> decompress_done_callback
=
339 base::Bind(&TabContentManager::OnFinishDecompressThumbnail
,
340 weak_factory_
.GetWeakPtr(), reinterpret_cast<int>(tab_id
));
341 thumbnail_cache_
->DecompressThumbnailFromFile(reinterpret_cast<int>(tab_id
),
342 decompress_done_callback
);
345 void TabContentManager::OnFinishedThumbnailRead(int tab_id
) {
346 JNIEnv
* env
= base::android::AttachCurrentThread();
347 Java_TabContentManager_notifyListenersOfThumbnailChange(
348 env
, weak_java_tab_content_manager_
.get(env
).obj(), tab_id
);
351 void TabContentManager::PutThumbnailIntoCache(int tab_id
,
352 float thumbnail_scale
,
353 const SkBitmap
& bitmap
) {
354 TabReadbackRequestMap::iterator readback_iter
=
355 pending_tab_readbacks_
.find(tab_id
);
357 if (readback_iter
!= pending_tab_readbacks_
.end())
358 pending_tab_readbacks_
.erase(tab_id
);
360 if (thumbnail_scale
> 0 && !bitmap
.empty())
361 thumbnail_cache_
->Put(tab_id
, bitmap
, thumbnail_scale
);
364 bool RegisterTabContentManager(JNIEnv
* env
) {
365 return RegisterNativesImpl(env
);
368 // ----------------------------------------------------------------------------
369 // Native JNI methods
370 // ----------------------------------------------------------------------------
372 jlong
Init(JNIEnv
* env
,
373 const JavaParamRef
<jobject
>& obj
,
374 jint default_cache_size
,
375 jint approximation_cache_size
,
376 jint compression_queue_max_size
,
377 jint write_queue_max_size
,
378 jboolean use_approximation_thumbnail
) {
379 TabContentManager
* manager
= new TabContentManager(
380 env
, obj
, default_cache_size
, approximation_cache_size
,
381 compression_queue_max_size
, write_queue_max_size
,
382 use_approximation_thumbnail
);
383 return reinterpret_cast<intptr_t>(manager
);
386 } // namespace android
387 } // namespace chrome