1 // Copyright (c) 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 #include "weblayer/browser/content_view_render_view.h"
6
7 #include <android/bitmap.h>
8 #include <android/native_window_jni.h>
9
10 #include <memory>
11 #include <utility>
12
13 #include "base/android/jni_android.h"
14 #include "base/android/jni_string.h"
15 #include "base/android/scoped_java_ref.h"
16 #include "base/bind.h"
17 #include "base/lazy_instance.h"
18 #include "cc/layers/layer.h"
19 #include "cc/layers/picture_layer.h"
20 #include "content/public/browser/android/compositor.h"
21 #include "content/public/browser/web_contents.h"
22 #include "ui/android/resources/resource_manager.h"
23 #include "ui/android/view_android.h"
24 #include "ui/android/window_android.h"
25 #include "ui/gfx/android/java_bitmap.h"
26 #include "ui/gfx/geometry/size.h"
27 #include "weblayer/browser/java/jni/ContentViewRenderView_jni.h"
28
29 using base::android::JavaParamRef;
30 using base::android::ScopedJavaLocalRef;
31
32 namespace weblayer {
33
ContentViewRenderView(JNIEnv * env,jobject obj,gfx::NativeWindow root_window)34 ContentViewRenderView::ContentViewRenderView(JNIEnv* env,
35 jobject obj,
36 gfx::NativeWindow root_window)
37 : root_window_(root_window) {
38 java_obj_.Reset(env, obj);
39 }
40
~ContentViewRenderView()41 ContentViewRenderView::~ContentViewRenderView() {
42 DCHECK(height_changed_listener_.is_null());
43 }
44
SetHeightChangedListener(base::RepeatingClosure callback)45 void ContentViewRenderView::SetHeightChangedListener(
46 base::RepeatingClosure callback) {
47 DCHECK(height_changed_listener_.is_null() || callback.is_null());
48 height_changed_listener_ = std::move(callback);
49 }
50
51 // static
JNI_ContentViewRenderView_Init(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & jroot_window_android)52 static jlong JNI_ContentViewRenderView_Init(
53 JNIEnv* env,
54 const JavaParamRef<jobject>& obj,
55 const JavaParamRef<jobject>& jroot_window_android) {
56 gfx::NativeWindow root_window =
57 ui::WindowAndroid::FromJavaWindowAndroid(jroot_window_android);
58 ContentViewRenderView* content_view_render_view =
59 new ContentViewRenderView(env, obj, root_window);
60 return reinterpret_cast<intptr_t>(content_view_render_view);
61 }
62
Destroy(JNIEnv * env)63 void ContentViewRenderView::Destroy(JNIEnv* env) {
64 delete this;
65 }
66
SetCurrentWebContents(JNIEnv * env,const JavaParamRef<jobject> & jweb_contents)67 void ContentViewRenderView::SetCurrentWebContents(
68 JNIEnv* env,
69 const JavaParamRef<jobject>& jweb_contents) {
70 InitCompositor();
71 content::WebContents* web_contents =
72 content::WebContents::FromJavaWebContents(jweb_contents);
73 if (web_contents_layer_)
74 web_contents_layer_->RemoveFromParent();
75 web_contents_layer_ = web_contents ? web_contents->GetNativeView()->GetLayer()
76 : scoped_refptr<cc::Layer>();
77
78 if (web_contents_layer_)
79 root_container_layer_->AddChild(web_contents_layer_);
80 }
81
OnPhysicalBackingSizeChanged(JNIEnv * env,const JavaParamRef<jobject> & jweb_contents,jint width,jint height,jboolean for_config_change)82 void ContentViewRenderView::OnPhysicalBackingSizeChanged(
83 JNIEnv* env,
84 const JavaParamRef<jobject>& jweb_contents,
85 jint width,
86 jint height,
87 jboolean for_config_change) {
88 bool height_changed = height_ != height;
89 height_ = height;
90 content::WebContents* web_contents =
91 content::WebContents::FromJavaWebContents(jweb_contents);
92 gfx::Size size(width, height);
93
94 // The default resize timeout on Android is 1s. It was chosen with browser
95 // use case in mind where resize is rare (eg orientation change, fullscreen)
96 // and users are generally willing to wait for those cases instead of seeing
97 // a frame at the wrong size. Weblayer currently can be resized while user
98 // is interacting with the page, in which case the timeout is too long.
99 // For now, use the default long timeout only for rotation (ie config change)
100 // and just use a zero timeout for all other cases.
101 base::Optional<base::TimeDelta> override_deadline;
102 if (!for_config_change)
103 override_deadline = base::TimeDelta();
104 web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(
105 size, override_deadline);
106
107 if (height_changed && !height_changed_listener_.is_null())
108 height_changed_listener_.Run();
109 }
110
SurfaceCreated(JNIEnv * env)111 void ContentViewRenderView::SurfaceCreated(JNIEnv* env) {
112 InitCompositor();
113 current_surface_format_ = 0;
114 }
115
SurfaceDestroyed(JNIEnv * env,jboolean cache_back_buffer)116 void ContentViewRenderView::SurfaceDestroyed(JNIEnv* env,
117 jboolean cache_back_buffer) {
118 if (cache_back_buffer)
119 compositor_->CacheBackBufferForCurrentSurface();
120 compositor_->SetSurface(nullptr, false);
121 current_surface_format_ = 0;
122 }
123
SurfaceChanged(JNIEnv * env,jboolean can_be_used_with_surface_control,jint format,jint width,jint height,const JavaParamRef<jobject> & surface)124 void ContentViewRenderView::SurfaceChanged(
125 JNIEnv* env,
126 jboolean can_be_used_with_surface_control,
127 jint format,
128 jint width,
129 jint height,
130 const JavaParamRef<jobject>& surface) {
131 current_surface_format_ = format;
132 compositor_->SetSurface(surface, can_be_used_with_surface_control);
133 compositor_->SetWindowBounds(gfx::Size(width, height));
134 }
135
SetNeedsRedraw(JNIEnv * env)136 void ContentViewRenderView::SetNeedsRedraw(JNIEnv* env) {
137 compositor_->SetNeedsRedraw();
138 }
139
140 base::android::ScopedJavaLocalRef<jobject>
GetResourceManager(JNIEnv * env)141 ContentViewRenderView::GetResourceManager(JNIEnv* env) {
142 return compositor_->GetResourceManager().GetJavaObject();
143 }
144
UpdateBackgroundColor(JNIEnv * env)145 void ContentViewRenderView::UpdateBackgroundColor(JNIEnv* env) {
146 if (!compositor_)
147 return;
148 compositor_->SetBackgroundColor(
149 Java_ContentViewRenderView_getBackgroundColor(env, java_obj_));
150 }
151
UpdateLayerTreeHost()152 void ContentViewRenderView::UpdateLayerTreeHost() {
153 // TODO(wkorman): Rename Layout to UpdateLayerTreeHost in all Android
154 // Compositor related classes.
155 }
156
DidSwapFrame(int pending_frames)157 void ContentViewRenderView::DidSwapFrame(int pending_frames) {
158 JNIEnv* env = base::android::AttachCurrentThread();
159 TRACE_EVENT0("weblayer", "Java_ContentViewRenderView_didSwapFrame");
160 if (Java_ContentViewRenderView_didSwapFrame(env, java_obj_)) {
161 compositor_->SetNeedsRedraw();
162 }
163 }
164
DidSwapBuffers(const gfx::Size & swap_size)165 void ContentViewRenderView::DidSwapBuffers(const gfx::Size& swap_size) {
166 JNIEnv* env = base::android::AttachCurrentThread();
167 bool matches_window_bounds = swap_size == compositor_->GetWindowBounds();
168 Java_ContentViewRenderView_didSwapBuffers(env, java_obj_,
169 matches_window_bounds);
170 }
171
EvictCachedSurface(JNIEnv * env)172 void ContentViewRenderView::EvictCachedSurface(JNIEnv* env) {
173 compositor_->EvictCachedBackBuffer();
174 }
175
InitCompositor()176 void ContentViewRenderView::InitCompositor() {
177 if (compositor_)
178 return;
179
180 compositor_.reset(content::Compositor::Create(this, root_window_));
181 root_container_layer_ = cc::Layer::Create();
182 root_container_layer_->SetHitTestable(false);
183 root_container_layer_->SetElementId(
184 cc::ElementId(root_container_layer_->id()));
185 root_container_layer_->SetIsDrawable(false);
186 compositor_->SetRootLayer(root_container_layer_);
187 UpdateBackgroundColor(base::android::AttachCurrentThread());
188 }
189
190 } // namespace weblayer
191