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 "android_webview/browser/browser_view_renderer.h"
7 #include "android_webview/browser/browser_view_renderer_client.h"
8 #include "android_webview/browser/shared_renderer_state.h"
9 #include "android_webview/public/browser/draw_gl.h"
10 #include "base/android/jni_android.h"
11 #include "base/auto_reset.h"
12 #include "base/debug/trace_event.h"
13 #include "base/logging.h"
14 #include "base/strings/stringprintf.h"
15 #include "content/public/browser/android/synchronous_compositor.h"
16 #include "content/public/browser/web_contents.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "third_party/skia/include/core/SkCanvas.h"
19 #include "third_party/skia/include/core/SkPicture.h"
20 #include "ui/gfx/vector2d_conversions.h"
22 using base::android::AttachCurrentThread
;
23 using base::android::JavaRef
;
24 using base::android::ScopedJavaLocalRef
;
26 namespace android_webview
{
30 const int64 kFallbackTickTimeoutInMilliseconds
= 20;
32 class AutoResetWithLock
{
34 AutoResetWithLock(gfx::Vector2dF
* scoped_variable
,
35 gfx::Vector2dF new_value
,
37 : scoped_variable_(scoped_variable
),
38 original_value_(*scoped_variable
),
40 base::AutoLock
auto_lock(lock_
);
41 *scoped_variable_
= new_value
;
44 ~AutoResetWithLock() {
45 base::AutoLock
auto_lock(lock_
);
46 *scoped_variable_
= original_value_
;
50 gfx::Vector2dF
* scoped_variable_
;
51 gfx::Vector2dF original_value_
;
54 DISALLOW_COPY_AND_ASSIGN(AutoResetWithLock
);
59 BrowserViewRenderer::BrowserViewRenderer(
60 BrowserViewRendererClient
* client
,
61 SharedRendererState
* shared_renderer_state
,
62 content::WebContents
* web_contents
,
63 const scoped_refptr
<base::SingleThreadTaskRunner
>& ui_task_runner
)
65 shared_renderer_state_(shared_renderer_state
),
66 web_contents_(web_contents
),
67 weak_factory_on_ui_thread_(this),
68 ui_thread_weak_ptr_(weak_factory_on_ui_thread_
.GetWeakPtr()),
69 ui_task_runner_(ui_task_runner
),
70 has_compositor_(false),
73 window_visible_(false),
74 attached_to_window_(false),
76 page_scale_factor_(1.0),
77 on_new_picture_enable_(false),
79 compositor_needs_continuous_invalidate_(false),
80 block_invalidates_(false),
84 content::SynchronousCompositor::SetClientForWebContents(web_contents_
, this);
86 // Currently the logic in this class relies on |has_compositor_| remaining
87 // false until the DidInitializeCompositor() call, hence it is not set here.
90 BrowserViewRenderer::~BrowserViewRenderer() {
91 content::SynchronousCompositor::SetClientForWebContents(web_contents_
, NULL
);
94 bool BrowserViewRenderer::OnDraw(jobject java_canvas
,
95 bool is_hardware_canvas
,
96 const gfx::Vector2d
& scroll
,
97 const gfx::Rect
& global_visible_rect
,
98 const gfx::Rect
& clip
) {
99 draw_gl_input_
.frame_id
++;
100 draw_gl_input_
.scroll_offset
= scroll
;
101 draw_gl_input_
.global_visible_rect
= global_visible_rect
;
104 if (is_hardware_canvas
&& attached_to_window_
) {
105 shared_renderer_state_
->SetDrawGLInput(draw_gl_input_
);
106 // We should be performing a hardware draw here. If we don't have the
107 // compositor yet or if RequestDrawGL fails, it means we failed this draw
108 // and thus return false here to clear to background color for this draw.
109 return has_compositor_
&& client_
->RequestDrawGL(java_canvas
);
111 // Perform a software draw
112 return DrawSWInternal(java_canvas
, clip
);
115 void BrowserViewRenderer::DidDrawGL(const DrawGLResult
& result
) {
116 DidComposite(!result
.clip_contains_visible_rect
);
119 bool BrowserViewRenderer::DrawSWInternal(jobject java_canvas
,
120 const gfx::Rect
& clip
) {
121 if (clip
.IsEmpty()) {
122 TRACE_EVENT_INSTANT0(
123 "android_webview", "EarlyOut_EmptyClip", TRACE_EVENT_SCOPE_THREAD
);
127 if (!has_compositor_
) {
128 TRACE_EVENT_INSTANT0(
129 "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD
);
133 return BrowserViewRendererJavaHelper::GetInstance()
134 ->RenderViaAuxilaryBitmapIfNeeded(
136 draw_gl_input_
.scroll_offset
,
138 base::Bind(&BrowserViewRenderer::CompositeSW
,
139 base::Unretained(this)));
142 skia::RefPtr
<SkPicture
> BrowserViewRenderer::CapturePicture(int width
,
144 TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture");
146 // Return empty Picture objects for empty SkPictures.
147 skia::RefPtr
<SkPicture
> picture
= skia::AdoptRef(new SkPicture
);
148 if (width
<= 0 || height
<= 0) {
152 // Reset scroll back to the origin, will go back to the old
153 // value when scroll_reset is out of scope.
154 AutoResetWithLock
scroll_reset(
155 &scroll_offset_dip_
, gfx::Vector2dF(), scroll_offset_dip_lock_
);
157 SkCanvas
* rec_canvas
= picture
->beginRecording(width
, height
, 0);
159 CompositeSW(rec_canvas
);
160 picture
->endRecording();
164 void BrowserViewRenderer::EnableOnNewPicture(bool enabled
) {
165 on_new_picture_enable_
= enabled
;
168 void BrowserViewRenderer::ClearView() {
169 TRACE_EVENT_INSTANT0("android_webview",
170 "BrowserViewRenderer::ClearView",
171 TRACE_EVENT_SCOPE_THREAD
);
176 // Always invalidate ignoring the compositor to actually clear the webview.
177 EnsureContinuousInvalidation(true);
180 void BrowserViewRenderer::SetIsPaused(bool paused
) {
181 TRACE_EVENT_INSTANT1("android_webview",
182 "BrowserViewRenderer::SetIsPaused",
183 TRACE_EVENT_SCOPE_THREAD
,
187 EnsureContinuousInvalidation(false);
190 void BrowserViewRenderer::SetViewVisibility(bool view_visible
) {
191 TRACE_EVENT_INSTANT1("android_webview",
192 "BrowserViewRenderer::SetViewVisibility",
193 TRACE_EVENT_SCOPE_THREAD
,
196 view_visible_
= view_visible
;
199 void BrowserViewRenderer::SetWindowVisibility(bool window_visible
) {
200 TRACE_EVENT_INSTANT1("android_webview",
201 "BrowserViewRenderer::SetWindowVisibility",
202 TRACE_EVENT_SCOPE_THREAD
,
205 window_visible_
= window_visible
;
206 EnsureContinuousInvalidation(false);
209 void BrowserViewRenderer::OnSizeChanged(int width
, int height
) {
210 TRACE_EVENT_INSTANT2("android_webview",
211 "BrowserViewRenderer::OnSizeChanged",
212 TRACE_EVENT_SCOPE_THREAD
,
221 void BrowserViewRenderer::OnAttachedToWindow(int width
, int height
) {
222 TRACE_EVENT2("android_webview",
223 "BrowserViewRenderer::OnAttachedToWindow",
228 attached_to_window_
= true;
233 void BrowserViewRenderer::OnDetachedFromWindow() {
234 TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow");
235 attached_to_window_
= false;
238 bool BrowserViewRenderer::IsAttachedToWindow() const {
239 return attached_to_window_
;
242 bool BrowserViewRenderer::IsVisible() const {
243 // Ignore |window_visible_| if |attached_to_window_| is false.
244 return view_visible_
&& (!attached_to_window_
|| window_visible_
);
247 gfx::Rect
BrowserViewRenderer::GetScreenRect() const {
248 return gfx::Rect(client_
->GetLocationOnScreen(), gfx::Size(width_
, height_
));
251 void BrowserViewRenderer::DidInitializeCompositor(
252 content::SynchronousCompositor
* compositor
) {
253 TRACE_EVENT0("android_webview",
254 "BrowserViewRenderer::DidInitializeCompositor");
256 DCHECK(!has_compositor_
);
257 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
258 has_compositor_
= true;
259 shared_renderer_state_
->SetCompositorOnUiThread(compositor
);
262 void BrowserViewRenderer::DidDestroyCompositor(
263 content::SynchronousCompositor
* compositor
) {
264 TRACE_EVENT0("android_webview", "BrowserViewRenderer::DidDestroyCompositor");
265 DCHECK(has_compositor_
);
266 DCHECK(ui_task_runner_
->BelongsToCurrentThread());
267 has_compositor_
= false;
268 shared_renderer_state_
->SetCompositorOnUiThread(NULL
);
271 void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate
) {
272 if (!ui_task_runner_
->BelongsToCurrentThread()) {
273 ui_task_runner_
->PostTask(
275 base::Bind(&BrowserViewRenderer::SetContinuousInvalidate
,
280 if (compositor_needs_continuous_invalidate_
== invalidate
)
283 TRACE_EVENT_INSTANT1("android_webview",
284 "BrowserViewRenderer::SetContinuousInvalidate",
285 TRACE_EVENT_SCOPE_THREAD
,
288 compositor_needs_continuous_invalidate_
= invalidate
;
289 EnsureContinuousInvalidation(false);
292 void BrowserViewRenderer::SetDipScale(float dip_scale
) {
293 dip_scale_
= dip_scale
;
294 CHECK(dip_scale_
> 0);
297 gfx::Vector2d
BrowserViewRenderer::max_scroll_offset() const {
298 DCHECK_GT(dip_scale_
, 0);
299 return gfx::ToCeiledVector2d(gfx::ScaleVector2d(
300 max_scroll_offset_dip_
, dip_scale_
* page_scale_factor_
));
303 void BrowserViewRenderer::ScrollTo(gfx::Vector2d scroll_offset
) {
304 gfx::Vector2d max_offset
= max_scroll_offset();
305 gfx::Vector2dF scroll_offset_dip
;
306 // To preserve the invariant that scrolling to the maximum physical pixel
307 // value also scrolls to the maximum dip pixel value we transform the physical
308 // offset into the dip offset by using a proportion (instead of dividing by
309 // dip_scale * page_scale_factor).
310 if (max_offset
.x()) {
311 scroll_offset_dip
.set_x((scroll_offset
.x() * max_scroll_offset_dip_
.x()) /
314 if (max_offset
.y()) {
315 scroll_offset_dip
.set_y((scroll_offset
.y() * max_scroll_offset_dip_
.y()) /
319 DCHECK_LE(0, scroll_offset_dip
.x());
320 DCHECK_LE(0, scroll_offset_dip
.y());
321 DCHECK_LE(scroll_offset_dip
.x(), max_scroll_offset_dip_
.x());
322 DCHECK_LE(scroll_offset_dip
.y(), max_scroll_offset_dip_
.y());
325 base::AutoLock
lock(scroll_offset_dip_lock_
);
326 if (scroll_offset_dip_
== scroll_offset_dip
)
329 scroll_offset_dip_
= scroll_offset_dip
;
333 shared_renderer_state_
->CompositorDidChangeRootLayerScrollOffset();
336 void BrowserViewRenderer::DidUpdateContent() {
337 if (!ui_task_runner_
->BelongsToCurrentThread()) {
338 ui_task_runner_
->PostTask(FROM_HERE
,
339 base::Bind(&BrowserViewRenderer::DidUpdateContent
,
340 ui_thread_weak_ptr_
));
343 TRACE_EVENT_INSTANT0("android_webview",
344 "BrowserViewRenderer::DidUpdateContent",
345 TRACE_EVENT_SCOPE_THREAD
);
347 if (on_new_picture_enable_
)
348 client_
->OnNewPicture();
351 void BrowserViewRenderer::SetMaxRootLayerScrollOffset(
352 gfx::Vector2dF new_value_dip
) {
353 if (!ui_task_runner_
->BelongsToCurrentThread()) {
354 ui_task_runner_
->PostTask(
356 base::Bind(&BrowserViewRenderer::SetMaxRootLayerScrollOffset
,
361 DCHECK_GT(dip_scale_
, 0);
363 max_scroll_offset_dip_
= new_value_dip
;
364 DCHECK_LE(0, max_scroll_offset_dip_
.x());
365 DCHECK_LE(0, max_scroll_offset_dip_
.y());
367 client_
->SetMaxContainerViewScrollOffset(max_scroll_offset());
370 void BrowserViewRenderer::SetTotalRootLayerScrollOffset(
371 gfx::Vector2dF scroll_offset_dip
) {
372 if (!ui_task_runner_
->BelongsToCurrentThread()) {
373 ui_task_runner_
->PostTask(
375 base::Bind(&BrowserViewRenderer::SetTotalRootLayerScrollOffset
,
382 base::AutoLock
lock(scroll_offset_dip_lock_
);
383 // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during
384 // DrawGl when http://crbug.com/249972 is fixed.
385 if (scroll_offset_dip_
== scroll_offset_dip
)
388 scroll_offset_dip_
= scroll_offset_dip
;
391 gfx::Vector2d max_offset
= max_scroll_offset();
392 gfx::Vector2d scroll_offset
;
393 // For an explanation as to why this is done this way see the comment in
394 // BrowserViewRenderer::ScrollTo.
395 if (max_scroll_offset_dip_
.x()) {
396 scroll_offset
.set_x((scroll_offset_dip
.x() * max_offset
.x()) /
397 max_scroll_offset_dip_
.x());
400 if (max_scroll_offset_dip_
.y()) {
401 scroll_offset
.set_y((scroll_offset_dip
.y() * max_offset
.y()) /
402 max_scroll_offset_dip_
.y());
405 DCHECK(0 <= scroll_offset
.x());
406 DCHECK(0 <= scroll_offset
.y());
407 // Disabled because the conditions are being violated while running
408 // AwZoomTest.testMagnification, see http://crbug.com/340648
409 // DCHECK(scroll_offset.x() <= max_offset.x());
410 // DCHECK(scroll_offset.y() <= max_offset.y());
412 client_
->ScrollContainerViewTo(scroll_offset
);
415 gfx::Vector2dF
BrowserViewRenderer::GetTotalRootLayerScrollOffset() {
416 base::AutoLock
lock(scroll_offset_dip_lock_
);
417 return scroll_offset_dip_
;
420 bool BrowserViewRenderer::IsExternalFlingActive() const {
421 if (!ui_task_runner_
->BelongsToCurrentThread()) {
422 // TODO(boliu): This is short term hack since we cannot call into
423 // view system on non-UI thread.
426 return client_
->IsFlingActive();
429 void BrowserViewRenderer::SetRootLayerPageScaleFactorAndLimits(
430 float page_scale_factor
,
431 float min_page_scale_factor
,
432 float max_page_scale_factor
) {
433 if (!ui_task_runner_
->BelongsToCurrentThread()) {
434 ui_task_runner_
->PostTask(
436 base::Bind(&BrowserViewRenderer::SetRootLayerPageScaleFactorAndLimits
,
439 min_page_scale_factor
,
440 max_page_scale_factor
));
443 page_scale_factor_
= page_scale_factor
;
444 DCHECK_GT(page_scale_factor_
, 0);
445 client_
->SetPageScaleFactorAndLimits(
446 page_scale_factor
, min_page_scale_factor
, max_page_scale_factor
);
447 client_
->SetMaxContainerViewScrollOffset(max_scroll_offset());
450 void BrowserViewRenderer::SetRootLayerScrollableSize(
451 gfx::SizeF scrollable_size
) {
452 if (!ui_task_runner_
->BelongsToCurrentThread()) {
453 ui_task_runner_
->PostTask(
455 base::Bind(&BrowserViewRenderer::SetRootLayerScrollableSize
,
460 client_
->SetContentsSize(scrollable_size
);
463 void BrowserViewRenderer::DidOverscroll(gfx::Vector2dF accumulated_overscroll
,
464 gfx::Vector2dF latest_overscroll_delta
,
465 gfx::Vector2dF current_fling_velocity
) {
466 if (!ui_task_runner_
->BelongsToCurrentThread()) {
467 ui_task_runner_
->PostTask(
469 base::Bind(&BrowserViewRenderer::DidOverscroll
,
471 accumulated_overscroll
,
472 latest_overscroll_delta
,
473 current_fling_velocity
));
476 const float physical_pixel_scale
= dip_scale_
* page_scale_factor_
;
477 if (accumulated_overscroll
== latest_overscroll_delta
)
478 overscroll_rounding_error_
= gfx::Vector2dF();
479 gfx::Vector2dF scaled_overscroll_delta
=
480 gfx::ScaleVector2d(latest_overscroll_delta
, physical_pixel_scale
);
481 gfx::Vector2d rounded_overscroll_delta
= gfx::ToRoundedVector2d(
482 scaled_overscroll_delta
+ overscroll_rounding_error_
);
483 overscroll_rounding_error_
=
484 scaled_overscroll_delta
- rounded_overscroll_delta
;
485 client_
->DidOverscroll(rounded_overscroll_delta
);
488 void BrowserViewRenderer::EnsureContinuousInvalidation(bool force_invalidate
) {
489 // This method should be called again when any of these conditions change.
490 bool need_invalidate
=
491 compositor_needs_continuous_invalidate_
|| force_invalidate
;
492 if (!need_invalidate
|| block_invalidates_
)
495 // Always call view invalidate. We rely the Android framework to ignore the
496 // invalidate when it's not needed such as when view is not visible.
497 client_
->PostInvalidate();
499 // Stop fallback ticks when one of these is true.
500 // 1) Webview is paused. Also need to check we are not in clear view since
501 // paused, offscreen still expect clear view to recover.
502 // 2) If we are attached to window and the window is not visible (eg when
503 // app is in the background). We are sure in this case the webview is used
504 // "on-screen" but that updates are not needed when in the background.
505 bool throttle_fallback_tick
=
506 (is_paused_
&& !clear_view_
) || (attached_to_window_
&& !window_visible_
);
507 if (throttle_fallback_tick
)
510 block_invalidates_
= compositor_needs_continuous_invalidate_
;
512 // Unretained here is safe because the callback is cancelled when
513 // |fallback_tick_| is destroyed.
514 fallback_tick_
.Reset(base::Bind(&BrowserViewRenderer::FallbackTickFired
,
515 base::Unretained(this)));
517 // No need to reschedule fallback tick if compositor does not need to be
518 // ticked. This can happen if this is reached because force_invalidate is
520 if (compositor_needs_continuous_invalidate_
) {
521 ui_task_runner_
->PostDelayedTask(
523 fallback_tick_
.callback(),
524 base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds
));
528 void BrowserViewRenderer::FallbackTickFired() {
529 TRACE_EVENT1("android_webview",
530 "BrowserViewRenderer::FallbackTickFired",
531 "compositor_needs_continuous_invalidate_",
532 compositor_needs_continuous_invalidate_
);
534 // This should only be called if OnDraw or DrawGL did not come in time, which
535 // means block_invalidates_ must still be true.
536 DCHECK(block_invalidates_
);
537 if (compositor_needs_continuous_invalidate_
&& has_compositor_
)
538 ForceFakeCompositeSW();
541 void BrowserViewRenderer::ForceFakeCompositeSW() {
542 DCHECK(has_compositor_
);
544 bitmap
.allocN32Pixels(1, 1);
545 bitmap
.eraseColor(0);
546 SkCanvas
canvas(bitmap
);
547 CompositeSW(&canvas
);
550 bool BrowserViewRenderer::CompositeSW(SkCanvas
* canvas
) {
551 DCHECK(has_compositor_
);
552 bool result
= shared_renderer_state_
->CompositorDemandDrawSw(canvas
);
557 void BrowserViewRenderer::DidComposite(bool force_invalidate
) {
558 fallback_tick_
.Cancel();
559 block_invalidates_
= false;
560 EnsureContinuousInvalidation(force_invalidate
);
563 std::string
BrowserViewRenderer::ToString(AwDrawGLInfo
* draw_info
) const {
565 base::StringAppendF(&str
, "is_paused: %d ", is_paused_
);
566 base::StringAppendF(&str
, "view_visible: %d ", view_visible_
);
567 base::StringAppendF(&str
, "window_visible: %d ", window_visible_
);
568 base::StringAppendF(&str
, "dip_scale: %f ", dip_scale_
);
569 base::StringAppendF(&str
, "page_scale_factor: %f ", page_scale_factor_
);
570 base::StringAppendF(&str
,
571 "compositor_needs_continuous_invalidate: %d ",
572 compositor_needs_continuous_invalidate_
);
573 base::StringAppendF(&str
, "block_invalidates: %d ", block_invalidates_
);
574 base::StringAppendF(&str
, "view width height: [%d %d] ", width_
, height_
);
575 base::StringAppendF(&str
, "attached_to_window: %d ", attached_to_window_
);
576 base::StringAppendF(&str
,
577 "global visible rect: %s ",
578 draw_gl_input_
.global_visible_rect
.ToString().c_str());
580 &str
, "scroll_offset_dip: %s ", scroll_offset_dip_
.ToString().c_str());
581 base::StringAppendF(&str
,
582 "overscroll_rounding_error_: %s ",
583 overscroll_rounding_error_
.ToString().c_str());
585 &str
, "on_new_picture_enable: %d ", on_new_picture_enable_
);
586 base::StringAppendF(&str
, "clear_view: %d ", clear_view_
);
588 base::StringAppendF(&str
,
589 "clip left top right bottom: [%d %d %d %d] ",
590 draw_info
->clip_left
,
592 draw_info
->clip_right
,
593 draw_info
->clip_bottom
);
594 base::StringAppendF(&str
,
595 "surface width height: [%d %d] ",
598 base::StringAppendF(&str
, "is_layer: %d ", draw_info
->is_layer
);
603 } // namespace android_webview