1 // Copyright 2019 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/browser_controls_container_view.h"
6 
7 #include "base/android/jni_string.h"
8 #include "base/bind.h"
9 #include "base/feature_list.h"
10 #include "cc/layers/ui_resource_layer.h"
11 #include "content/public/browser/android/compositor.h"
12 #include "content/public/browser/render_view_host.h"
13 #include "content/public/browser/render_widget_host.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/browser_controls_state.h"
16 #include "ui/android/resources/resource.h"
17 #include "ui/android/resources/resource_manager.h"
18 #include "ui/android/view_android.h"
19 #include "weblayer/browser/content_view_render_view.h"
20 #include "weblayer/browser/java/jni/BrowserControlsContainerView_jni.h"
21 #include "weblayer/browser/weblayer_features.h"
22 
23 using base::android::AttachCurrentThread;
24 using base::android::JavaParamRef;
25 
26 namespace weblayer {
27 
BrowserControlsContainerView(const JavaParamRef<jobject> & java_browser_controls_container_view,ContentViewRenderView * content_view_render_view,bool is_top)28 BrowserControlsContainerView::BrowserControlsContainerView(
29     const JavaParamRef<jobject>& java_browser_controls_container_view,
30     ContentViewRenderView* content_view_render_view,
31     bool is_top)
32     : java_browser_controls_container_view_(
33           java_browser_controls_container_view),
34       content_view_render_view_(content_view_render_view),
35       is_top_(is_top) {
36   DCHECK(content_view_render_view_);
37   if (!is_top_) {
38     content_view_render_view_->SetHeightChangedListener(
39         base::BindRepeating(&BrowserControlsContainerView::ContentHeightChanged,
40                             base::Unretained(this)));
41   }
42 }
43 
~BrowserControlsContainerView()44 BrowserControlsContainerView::~BrowserControlsContainerView() {
45   if (!is_top_) {
46     content_view_render_view_->SetHeightChangedListener(
47         base::RepeatingClosure());
48   }
49 }
50 
GetControlsHeight()51 int BrowserControlsContainerView::GetControlsHeight() {
52   return controls_layer_ ? controls_layer_->bounds().height() : 0;
53 }
54 
GetMinHeight()55 int BrowserControlsContainerView::GetMinHeight() {
56   return Java_BrowserControlsContainerView_getMinHeight(
57       AttachCurrentThread(), java_browser_controls_container_view_);
58 }
59 
OnlyExpandControlsAtPageTop()60 bool BrowserControlsContainerView::OnlyExpandControlsAtPageTop() {
61   return Java_BrowserControlsContainerView_onlyExpandControlsAtPageTop(
62       AttachCurrentThread(), java_browser_controls_container_view_);
63 }
64 
ShouldAnimateBrowserControlsHeightChanges()65 bool BrowserControlsContainerView::ShouldAnimateBrowserControlsHeightChanges() {
66   return Java_BrowserControlsContainerView_shouldAnimateBrowserControlsHeightChanges(
67       AttachCurrentThread(), java_browser_controls_container_view_);
68 }
69 
GetContentHeightDelta()70 int BrowserControlsContainerView::GetContentHeightDelta() {
71   if (!controls_layer_ || !web_contents())
72     return 0;
73 
74   if (is_top_)
75     return web_contents()->GetNativeView()->GetLayer()->position().y();
76 
77   return content_view_render_view_->height() - controls_layer_->position().y();
78 }
79 
IsFullyVisible() const80 bool BrowserControlsContainerView::IsFullyVisible() const {
81   // At this time this is only needed for top-controls.
82   DCHECK(is_top_);
83   return controls_layer_ && controls_layer_->position().y() == 0;
84 }
85 
CreateControlsLayer(JNIEnv * env,int id)86 void BrowserControlsContainerView::CreateControlsLayer(
87     JNIEnv* env,
88     int id) {
89   controls_resource_id_ = id;
90   controls_layer_ = cc::UIResourceLayer::Create();
91   // Real size is sent in SetControlsSize().
92   controls_layer_->SetBounds(gfx::Size(1, 1));
93   controls_layer_->SetPosition(gfx::PointF(0, 0));
94   controls_layer_->SetElementId(cc::ElementId(controls_layer_->id()));
95   controls_layer_->SetHitTestable(false);
96   controls_layer_->SetIsDrawable(true);
97   content_view_render_view_->root_container_layer()->AddChild(controls_layer_);
98   UpdateControlsResource(env);
99 }
100 
DeleteBrowserControlsContainerView(JNIEnv * env)101 void BrowserControlsContainerView::DeleteBrowserControlsContainerView(
102     JNIEnv* env) {
103   delete this;
104 }
105 
DeleteControlsLayer(JNIEnv * env)106 void BrowserControlsContainerView::DeleteControlsLayer(JNIEnv* env) {
107   controls_layer_.reset();
108 }
109 
SetTopControlsOffset(JNIEnv * env,int content_offset_y)110 void BrowserControlsContainerView::SetTopControlsOffset(JNIEnv* env,
111                                                         int content_offset_y) {
112   DCHECK(is_top_);
113   // |controls_layer_| may not be created if the controls view has 0 height.
114   if (controls_layer_)
115     controls_layer_->SetPosition(gfx::PointF(0, GetControlsOffset()));
116   if (web_contents()) {
117     web_contents()->GetNativeView()->GetLayer()->SetPosition(
118         gfx::PointF(0, content_offset_y));
119   }
120 }
121 
SetBottomControlsOffset(JNIEnv * env)122 void BrowserControlsContainerView::SetBottomControlsOffset(JNIEnv* env) {
123   DCHECK(!is_top_);
124   DoSetBottomControlsOffset();
125 }
126 
SetControlsSize(JNIEnv * env,int width,int height)127 void BrowserControlsContainerView::SetControlsSize(
128     JNIEnv* env,
129     int width,
130     int height) {
131   DCHECK(controls_layer_);
132   controls_layer_->SetBounds(gfx::Size(width, height));
133   // It's assumed the caller handles triggering SynchronizeVisualProperties()
134   // being called (this is done in java code).
135 }
136 
UpdateControlsResource(JNIEnv * env)137 void BrowserControlsContainerView::UpdateControlsResource(JNIEnv* env) {
138   DCHECK(controls_layer_);
139   ui::ResourceManager& resource_manager =
140       content_view_render_view_->compositor()->GetResourceManager();
141   ui::Resource* controls_resource = resource_manager.GetResource(
142       ui::ANDROID_RESOURCE_TYPE_DYNAMIC, controls_resource_id_);
143   DCHECK(controls_resource);
144   controls_layer_->SetUIResourceId(controls_resource->ui_resource()->id());
145 }
146 
SetWebContents(JNIEnv * env,const JavaParamRef<jobject> & web_contents)147 void BrowserControlsContainerView::SetWebContents(
148     JNIEnv* env,
149     const JavaParamRef<jobject>& web_contents) {
150   Observe(content::WebContents::FromJavaWebContents(web_contents));
151 }
152 
DidToggleFullscreenModeForTab(bool entered_fullscreen,bool will_cause_resize)153 void BrowserControlsContainerView::DidToggleFullscreenModeForTab(
154     bool entered_fullscreen,
155     bool will_cause_resize) {
156   TRACE_EVENT0(
157       "weblayer",
158       "Java_BrowserControlsContainerView_didToggleFullscreenModeForTab");
159   Java_BrowserControlsContainerView_didToggleFullscreenModeForTab(
160       AttachCurrentThread(), java_browser_controls_container_view_,
161       entered_fullscreen);
162 }
163 
ContentHeightChanged()164 void BrowserControlsContainerView::ContentHeightChanged() {
165   DCHECK(!is_top_);
166   DoSetBottomControlsOffset();
167 }
168 
GetControlsOffset()169 int BrowserControlsContainerView::GetControlsOffset() {
170   return Java_BrowserControlsContainerView_getControlsOffset(
171       AttachCurrentThread(), java_browser_controls_container_view_);
172 }
173 
DoSetBottomControlsOffset()174 void BrowserControlsContainerView::DoSetBottomControlsOffset() {
175   DCHECK(!is_top_);
176   // |controls_layer_| may not be created if the controls view has 0 height.
177   if (!controls_layer_)
178     return;
179   controls_layer_->SetPosition(
180       gfx::PointF(0, content_view_render_view_->height() - GetControlsHeight() +
181                          GetControlsOffset()));
182 }
183 
184 static jlong
JNI_BrowserControlsContainerView_CreateBrowserControlsContainerView(JNIEnv * env,const JavaParamRef<jobject> & java_browser_controls_container_view,jlong native_content_view_render_view,jboolean is_top)185 JNI_BrowserControlsContainerView_CreateBrowserControlsContainerView(
186     JNIEnv* env,
187     const JavaParamRef<jobject>& java_browser_controls_container_view,
188     jlong native_content_view_render_view,
189     jboolean is_top) {
190   return reinterpret_cast<jlong>(new BrowserControlsContainerView(
191       java_browser_controls_container_view,
192       reinterpret_cast<ContentViewRenderView*>(native_content_view_render_view),
193       is_top));
194 }
195 
JNI_BrowserControlsContainerView_ShouldDelayVisibilityChange(JNIEnv * env)196 static jboolean JNI_BrowserControlsContainerView_ShouldDelayVisibilityChange(
197     JNIEnv* env) {
198   return !base::FeatureList::IsEnabled(kImmediatelyHideBrowserControlsForTest);
199 }
200 
201 }  // namespace weblayer
202