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.
4
5 #include "android_webview/browser/browser_view_renderer.h"
6
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/command_line.h"
13 #include "base/debug/trace_event.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "cc/output/compositor_frame.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/content_switches.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkPicture.h"
25 #include "third_party/skia/include/core/SkPictureRecorder.h"
26 #include "ui/gfx/vector2d_conversions.h"
27
28 using base::android::AttachCurrentThread;
29 using base::android::JavaRef;
30 using base::android::ScopedJavaLocalRef;
31 using content::BrowserThread;
32 using content::SynchronousCompositorMemoryPolicy;
33
34 namespace android_webview {
35
36 namespace {
37
38 const int64 kFallbackTickTimeoutInMilliseconds = 100;
39
40 // Used to calculate memory allocation. Determined experimentally.
41 const size_t kMemoryMultiplier = 20;
42 const size_t kBytesPerPixel = 4;
43 const size_t kMemoryAllocationStep = 5 * 1024 * 1024;
44
45 // Used to calculate tile allocation. Determined experimentally.
46 const size_t kTileMultiplier = 12;
47 const size_t kTileAllocationStep = 20;
48 // This will be set by static function CalculateTileMemoryPolicy() during init.
49 // See AwMainDelegate::BasicStartupComplete.
50 size_t g_tile_area;
51
52 class TracedValue : public base::debug::ConvertableToTraceFormat {
53 public:
TracedValue(base::Value * value)54 explicit TracedValue(base::Value* value) : value_(value) {}
FromValue(base::Value * value)55 static scoped_refptr<base::debug::ConvertableToTraceFormat> FromValue(
56 base::Value* value) {
57 return scoped_refptr<base::debug::ConvertableToTraceFormat>(
58 new TracedValue(value));
59 }
AppendAsTraceFormat(std::string * out) const60 virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
61 std::string tmp;
62 base::JSONWriter::Write(value_.get(), &tmp);
63 *out += tmp;
64 }
65
66 private:
~TracedValue()67 virtual ~TracedValue() {}
68 scoped_ptr<base::Value> value_;
69
70 DISALLOW_COPY_AND_ASSIGN(TracedValue);
71 };
72
73 } // namespace
74
75 // static
CalculateTileMemoryPolicy(bool use_zero_copy)76 void BrowserViewRenderer::CalculateTileMemoryPolicy(bool use_zero_copy) {
77 if (!use_zero_copy) {
78 // Use chrome's default tile size, which varies from 256 to 512.
79 // Be conservative here and use the smallest tile size possible.
80 g_tile_area = 256 * 256;
81
82 // Also use a high tile limit since there are no file descriptor issues.
83 GlobalTileManager::GetInstance()->SetTileLimit(1000);
84 return;
85 }
86
87 CommandLine* cl = CommandLine::ForCurrentProcess();
88 const char kDefaultTileSize[] = "384";
89
90 if (!cl->HasSwitch(switches::kDefaultTileWidth))
91 cl->AppendSwitchASCII(switches::kDefaultTileWidth, kDefaultTileSize);
92
93 if (!cl->HasSwitch(switches::kDefaultTileHeight))
94 cl->AppendSwitchASCII(switches::kDefaultTileHeight, kDefaultTileSize);
95
96 size_t tile_size;
97 base::StringToSizeT(kDefaultTileSize, &tile_size);
98 g_tile_area = tile_size * tile_size;
99 }
100
BrowserViewRenderer(BrowserViewRendererClient * client,SharedRendererState * shared_renderer_state,content::WebContents * web_contents,const scoped_refptr<base::SingleThreadTaskRunner> & ui_task_runner)101 BrowserViewRenderer::BrowserViewRenderer(
102 BrowserViewRendererClient* client,
103 SharedRendererState* shared_renderer_state,
104 content::WebContents* web_contents,
105 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
106 : client_(client),
107 shared_renderer_state_(shared_renderer_state),
108 web_contents_(web_contents),
109 ui_task_runner_(ui_task_runner),
110 compositor_(NULL),
111 is_paused_(false),
112 view_visible_(false),
113 window_visible_(false),
114 attached_to_window_(false),
115 hardware_enabled_(false),
116 dip_scale_(0.0),
117 page_scale_factor_(1.0),
118 on_new_picture_enable_(false),
119 clear_view_(false),
120 compositor_needs_continuous_invalidate_(false),
121 block_invalidates_(false),
122 width_(0),
123 height_(0),
124 num_tiles_(0u),
125 num_bytes_(0u) {
126 CHECK(web_contents_);
127 content::SynchronousCompositor::SetClientForWebContents(web_contents_, this);
128
129 // Currently the logic in this class relies on |compositor_| remaining
130 // NULL until the DidInitializeCompositor() call, hence it is not set here.
131 }
132
~BrowserViewRenderer()133 BrowserViewRenderer::~BrowserViewRenderer() {
134 content::SynchronousCompositor::SetClientForWebContents(web_contents_, NULL);
135 // OnDetachedFromWindow should be called before the destructor, so the memory
136 // policy should have already been updated.
137 }
138
139 // This function updates the resource allocation in GlobalTileManager.
TrimMemory(const int level,const bool visible)140 void BrowserViewRenderer::TrimMemory(const int level, const bool visible) {
141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 // Constants from Android ComponentCallbacks2.
143 enum {
144 TRIM_MEMORY_RUNNING_LOW = 10,
145 TRIM_MEMORY_UI_HIDDEN = 20,
146 TRIM_MEMORY_BACKGROUND = 40,
147 };
148
149 // Not urgent enough. TRIM_MEMORY_UI_HIDDEN is treated specially because
150 // it does not indicate memory pressure, but merely that the app is
151 // backgrounded.
152 if (level < TRIM_MEMORY_RUNNING_LOW || level == TRIM_MEMORY_UI_HIDDEN)
153 return;
154
155 // Do not release resources on view we expect to get DrawGL soon.
156 if (level < TRIM_MEMORY_BACKGROUND && visible)
157 return;
158
159 // Just set the memory limit to 0 and drop all tiles. This will be reset to
160 // normal levels in the next DrawGL call.
161 SynchronousCompositorMemoryPolicy zero_policy;
162 if (memory_policy_ == zero_policy)
163 return;
164
165 TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory");
166
167 RequestMemoryPolicy(zero_policy);
168 EnforceMemoryPolicyImmediately(zero_policy);
169 }
170
171 SynchronousCompositorMemoryPolicy
CalculateDesiredMemoryPolicy()172 BrowserViewRenderer::CalculateDesiredMemoryPolicy() {
173 SynchronousCompositorMemoryPolicy policy;
174 size_t width = last_on_draw_global_visible_rect_.width();
175 size_t height = last_on_draw_global_visible_rect_.height();
176 policy.bytes_limit = kMemoryMultiplier * kBytesPerPixel * width * height;
177 // Round up to a multiple of kMemoryAllocationStep.
178 policy.bytes_limit =
179 (policy.bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep;
180
181 size_t tiles = width * height * kTileMultiplier / g_tile_area;
182 // Round up to a multiple of kTileAllocationStep. The minimum number of tiles
183 // is also kTileAllocationStep.
184 tiles = (tiles / kTileAllocationStep + 1) * kTileAllocationStep;
185 policy.num_resources_limit = tiles;
186 return policy;
187 }
188
189 // This function updates the cached memory policy in shared renderer state, as
190 // well as the tile resource allocation in GlobalTileManager.
RequestMemoryPolicy(SynchronousCompositorMemoryPolicy & new_policy)191 void BrowserViewRenderer::RequestMemoryPolicy(
192 SynchronousCompositorMemoryPolicy& new_policy) {
193 // This will be used in SetNumTiles.
194 num_bytes_ = new_policy.bytes_limit;
195
196 GlobalTileManager* manager = GlobalTileManager::GetInstance();
197
198 // The following line will call BrowserViewRenderer::SetTilesNum().
199 manager->RequestTiles(new_policy.num_resources_limit, tile_manager_key_);
200 }
201
SetNumTiles(size_t num_tiles,bool effective_immediately)202 void BrowserViewRenderer::SetNumTiles(size_t num_tiles,
203 bool effective_immediately) {
204 num_tiles_ = num_tiles;
205
206 memory_policy_.num_resources_limit = num_tiles_;
207 memory_policy_.bytes_limit = num_bytes_;
208
209 if (effective_immediately)
210 EnforceMemoryPolicyImmediately(memory_policy_);
211 }
212
EnforceMemoryPolicyImmediately(SynchronousCompositorMemoryPolicy new_policy)213 void BrowserViewRenderer::EnforceMemoryPolicyImmediately(
214 SynchronousCompositorMemoryPolicy new_policy) {
215 compositor_->SetMemoryPolicy(new_policy);
216 ForceFakeCompositeSW();
217 }
218
GetNumTiles() const219 size_t BrowserViewRenderer::GetNumTiles() const {
220 return memory_policy_.num_resources_limit;
221 }
222
OnDraw(jobject java_canvas,bool is_hardware_canvas,const gfx::Vector2d & scroll,const gfx::Rect & global_visible_rect)223 bool BrowserViewRenderer::OnDraw(jobject java_canvas,
224 bool is_hardware_canvas,
225 const gfx::Vector2d& scroll,
226 const gfx::Rect& global_visible_rect) {
227 last_on_draw_scroll_offset_ = scroll;
228 last_on_draw_global_visible_rect_ = global_visible_rect;
229
230 if (clear_view_)
231 return false;
232
233 if (is_hardware_canvas && attached_to_window_)
234 return OnDrawHardware(java_canvas);
235 // Perform a software draw
236 return OnDrawSoftware(java_canvas);
237 }
238
OnDrawHardware(jobject java_canvas)239 bool BrowserViewRenderer::OnDrawHardware(jobject java_canvas) {
240 if (!compositor_)
241 return false;
242
243 if (last_on_draw_global_visible_rect_.IsEmpty() &&
244 parent_draw_constraints_.surface_rect.IsEmpty()) {
245 shared_renderer_state_->SetForceInvalidateOnNextDrawGL(true);
246 return client_->RequestDrawGL(java_canvas, false);
247 }
248
249 if (!hardware_enabled_) {
250 hardware_enabled_ = compositor_->InitializeHwDraw();
251 if (hardware_enabled_) {
252 tile_manager_key_ = GlobalTileManager::GetInstance()->PushBack(this);
253 gpu::GLInProcessContext* share_context = compositor_->GetShareContext();
254 DCHECK(share_context);
255 shared_renderer_state_->SetSharedContext(share_context);
256 }
257 }
258 if (!hardware_enabled_)
259 return false;
260
261 ReturnResourceFromParent();
262 SynchronousCompositorMemoryPolicy new_policy = CalculateDesiredMemoryPolicy();
263 RequestMemoryPolicy(new_policy);
264 compositor_->SetMemoryPolicy(memory_policy_);
265
266 scoped_ptr<DrawGLInput> draw_gl_input(new DrawGLInput);
267 draw_gl_input->scroll_offset = last_on_draw_scroll_offset_;
268 draw_gl_input->width = width_;
269 draw_gl_input->height = height_;
270
271 parent_draw_constraints_ = shared_renderer_state_->ParentDrawConstraints();
272 gfx::Size surface_size(width_, height_);
273 gfx::Rect viewport(surface_size);
274 gfx::Rect clip = viewport;
275 gfx::Transform transform_for_tile_priority =
276 parent_draw_constraints_.transform;
277
278 // If the WebView is on a layer, WebView does not know what transform is
279 // applied onto the layer so global visible rect does not make sense here.
280 // In this case, just use the surface rect for tiling.
281 gfx::Rect viewport_rect_for_tile_priority;
282 if (parent_draw_constraints_.is_layer ||
283 last_on_draw_global_visible_rect_.IsEmpty()) {
284 viewport_rect_for_tile_priority = parent_draw_constraints_.surface_rect;
285 } else {
286 viewport_rect_for_tile_priority = last_on_draw_global_visible_rect_;
287 }
288
289 scoped_ptr<cc::CompositorFrame> frame =
290 compositor_->DemandDrawHw(surface_size,
291 gfx::Transform(),
292 viewport,
293 clip,
294 viewport_rect_for_tile_priority,
295 transform_for_tile_priority);
296 if (!frame.get())
297 return false;
298
299 GlobalTileManager::GetInstance()->DidUse(tile_manager_key_);
300
301 frame->AssignTo(&draw_gl_input->frame);
302 ReturnUnusedResource(shared_renderer_state_->PassDrawGLInput());
303 shared_renderer_state_->SetDrawGLInput(draw_gl_input.Pass());
304 DidComposite();
305 return client_->RequestDrawGL(java_canvas, false);
306 }
307
UpdateParentDrawConstraints()308 void BrowserViewRenderer::UpdateParentDrawConstraints() {
309 // Post an invalidate if the parent draw constraints are stale and there is
310 // no pending invalidate.
311 if (shared_renderer_state_->NeedsForceInvalidateOnNextDrawGL() ||
312 !parent_draw_constraints_.Equals(
313 shared_renderer_state_->ParentDrawConstraints())) {
314 shared_renderer_state_->SetForceInvalidateOnNextDrawGL(false);
315 client_->PostInvalidate();
316 EnsureContinuousInvalidation(true);
317 }
318 }
319
ReturnUnusedResource(scoped_ptr<DrawGLInput> input)320 void BrowserViewRenderer::ReturnUnusedResource(scoped_ptr<DrawGLInput> input) {
321 if (!input.get())
322 return;
323
324 cc::CompositorFrameAck frame_ack;
325 cc::TransferableResource::ReturnResources(
326 input->frame.delegated_frame_data->resource_list,
327 &frame_ack.resources);
328 if (!frame_ack.resources.empty())
329 compositor_->ReturnResources(frame_ack);
330 }
331
ReturnResourceFromParent()332 void BrowserViewRenderer::ReturnResourceFromParent() {
333 cc::CompositorFrameAck frame_ack;
334 shared_renderer_state_->SwapReturnedResources(&frame_ack.resources);
335 if (!frame_ack.resources.empty()) {
336 compositor_->ReturnResources(frame_ack);
337 }
338 }
339
OnDrawSoftware(jobject java_canvas)340 bool BrowserViewRenderer::OnDrawSoftware(jobject java_canvas) {
341 if (!compositor_) {
342 TRACE_EVENT_INSTANT0(
343 "android_webview", "EarlyOut_NoCompositor", TRACE_EVENT_SCOPE_THREAD);
344 return false;
345 }
346
347 // TODO(hush): right now webview size is passed in as the auxiliary bitmap
348 // size, which might hurt performace (only for software draws with auxiliary
349 // bitmap). For better performance, get global visible rect, transform it
350 // from screen space to view space, then intersect with the webview in
351 // viewspace. Use the resulting rect as the auxiliary
352 // bitmap.
353 return BrowserViewRendererJavaHelper::GetInstance()
354 ->RenderViaAuxilaryBitmapIfNeeded(
355 java_canvas,
356 last_on_draw_scroll_offset_,
357 gfx::Size(width_, height_),
358 base::Bind(&BrowserViewRenderer::CompositeSW,
359 base::Unretained(this)));
360 }
361
CapturePicture(int width,int height)362 skia::RefPtr<SkPicture> BrowserViewRenderer::CapturePicture(int width,
363 int height) {
364 TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture");
365
366 // Return empty Picture objects for empty SkPictures.
367 if (width <= 0 || height <= 0) {
368 return skia::AdoptRef(new SkPicture);
369 }
370
371 // Reset scroll back to the origin, will go back to the old
372 // value when scroll_reset is out of scope.
373 base::AutoReset<gfx::Vector2dF> scroll_reset(&scroll_offset_dip_,
374 gfx::Vector2dF());
375
376 SkPictureRecorder recorder;
377 SkCanvas* rec_canvas = recorder.beginRecording(width, height, NULL, 0);
378 if (compositor_)
379 CompositeSW(rec_canvas);
380 return skia::AdoptRef(recorder.endRecording());
381 }
382
EnableOnNewPicture(bool enabled)383 void BrowserViewRenderer::EnableOnNewPicture(bool enabled) {
384 on_new_picture_enable_ = enabled;
385 }
386
ClearView()387 void BrowserViewRenderer::ClearView() {
388 TRACE_EVENT_INSTANT0("android_webview",
389 "BrowserViewRenderer::ClearView",
390 TRACE_EVENT_SCOPE_THREAD);
391 if (clear_view_)
392 return;
393
394 clear_view_ = true;
395 // Always invalidate ignoring the compositor to actually clear the webview.
396 EnsureContinuousInvalidation(true);
397 }
398
SetIsPaused(bool paused)399 void BrowserViewRenderer::SetIsPaused(bool paused) {
400 TRACE_EVENT_INSTANT1("android_webview",
401 "BrowserViewRenderer::SetIsPaused",
402 TRACE_EVENT_SCOPE_THREAD,
403 "paused",
404 paused);
405 is_paused_ = paused;
406 EnsureContinuousInvalidation(false);
407 }
408
SetViewVisibility(bool view_visible)409 void BrowserViewRenderer::SetViewVisibility(bool view_visible) {
410 TRACE_EVENT_INSTANT1("android_webview",
411 "BrowserViewRenderer::SetViewVisibility",
412 TRACE_EVENT_SCOPE_THREAD,
413 "view_visible",
414 view_visible);
415 view_visible_ = view_visible;
416 }
417
SetWindowVisibility(bool window_visible)418 void BrowserViewRenderer::SetWindowVisibility(bool window_visible) {
419 TRACE_EVENT_INSTANT1("android_webview",
420 "BrowserViewRenderer::SetWindowVisibility",
421 TRACE_EVENT_SCOPE_THREAD,
422 "window_visible",
423 window_visible);
424 window_visible_ = window_visible;
425 EnsureContinuousInvalidation(false);
426 }
427
OnSizeChanged(int width,int height)428 void BrowserViewRenderer::OnSizeChanged(int width, int height) {
429 TRACE_EVENT_INSTANT2("android_webview",
430 "BrowserViewRenderer::OnSizeChanged",
431 TRACE_EVENT_SCOPE_THREAD,
432 "width",
433 width,
434 "height",
435 height);
436 width_ = width;
437 height_ = height;
438 }
439
OnAttachedToWindow(int width,int height)440 void BrowserViewRenderer::OnAttachedToWindow(int width, int height) {
441 TRACE_EVENT2("android_webview",
442 "BrowserViewRenderer::OnAttachedToWindow",
443 "width",
444 width,
445 "height",
446 height);
447 attached_to_window_ = true;
448 width_ = width;
449 height_ = height;
450 }
451
OnDetachedFromWindow()452 void BrowserViewRenderer::OnDetachedFromWindow() {
453 TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow");
454 attached_to_window_ = false;
455 DCHECK(!hardware_enabled_);
456 }
457
ReleaseHardware()458 void BrowserViewRenderer::ReleaseHardware() {
459 DCHECK(hardware_enabled_);
460 ReturnUnusedResource(shared_renderer_state_->PassDrawGLInput());
461 ReturnResourceFromParent();
462 DCHECK(shared_renderer_state_->ReturnedResourcesEmpty());
463
464 compositor_->ReleaseHwDraw();
465 shared_renderer_state_->SetSharedContext(NULL);
466 hardware_enabled_ = false;
467
468 SynchronousCompositorMemoryPolicy zero_policy;
469 RequestMemoryPolicy(zero_policy);
470 GlobalTileManager::GetInstance()->Remove(tile_manager_key_);
471 }
472
IsVisible() const473 bool BrowserViewRenderer::IsVisible() const {
474 // Ignore |window_visible_| if |attached_to_window_| is false.
475 return view_visible_ && (!attached_to_window_ || window_visible_);
476 }
477
GetScreenRect() const478 gfx::Rect BrowserViewRenderer::GetScreenRect() const {
479 return gfx::Rect(client_->GetLocationOnScreen(), gfx::Size(width_, height_));
480 }
481
DidInitializeCompositor(content::SynchronousCompositor * compositor)482 void BrowserViewRenderer::DidInitializeCompositor(
483 content::SynchronousCompositor* compositor) {
484 TRACE_EVENT0("android_webview",
485 "BrowserViewRenderer::DidInitializeCompositor");
486 DCHECK(compositor);
487 DCHECK(!compositor_);
488 compositor_ = compositor;
489 }
490
DidDestroyCompositor(content::SynchronousCompositor * compositor)491 void BrowserViewRenderer::DidDestroyCompositor(
492 content::SynchronousCompositor* compositor) {
493 TRACE_EVENT0("android_webview", "BrowserViewRenderer::DidDestroyCompositor");
494 DCHECK(compositor_);
495 compositor_ = NULL;
496 SynchronousCompositorMemoryPolicy zero_policy;
497 DCHECK(memory_policy_ == zero_policy);
498 }
499
SetContinuousInvalidate(bool invalidate)500 void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate) {
501 if (compositor_needs_continuous_invalidate_ == invalidate)
502 return;
503
504 TRACE_EVENT_INSTANT1("android_webview",
505 "BrowserViewRenderer::SetContinuousInvalidate",
506 TRACE_EVENT_SCOPE_THREAD,
507 "invalidate",
508 invalidate);
509 compositor_needs_continuous_invalidate_ = invalidate;
510
511 EnsureContinuousInvalidation(false);
512 }
513
SetDipScale(float dip_scale)514 void BrowserViewRenderer::SetDipScale(float dip_scale) {
515 dip_scale_ = dip_scale;
516 CHECK(dip_scale_ > 0);
517 }
518
max_scroll_offset() const519 gfx::Vector2d BrowserViewRenderer::max_scroll_offset() const {
520 DCHECK_GT(dip_scale_, 0);
521 return gfx::ToCeiledVector2d(gfx::ScaleVector2d(
522 max_scroll_offset_dip_, dip_scale_ * page_scale_factor_));
523 }
524
ScrollTo(gfx::Vector2d scroll_offset)525 void BrowserViewRenderer::ScrollTo(gfx::Vector2d scroll_offset) {
526 gfx::Vector2d max_offset = max_scroll_offset();
527 gfx::Vector2dF scroll_offset_dip;
528 // To preserve the invariant that scrolling to the maximum physical pixel
529 // value also scrolls to the maximum dip pixel value we transform the physical
530 // offset into the dip offset by using a proportion (instead of dividing by
531 // dip_scale * page_scale_factor).
532 if (max_offset.x()) {
533 scroll_offset_dip.set_x((scroll_offset.x() * max_scroll_offset_dip_.x()) /
534 max_offset.x());
535 }
536 if (max_offset.y()) {
537 scroll_offset_dip.set_y((scroll_offset.y() * max_scroll_offset_dip_.y()) /
538 max_offset.y());
539 }
540
541 DCHECK_LE(0, scroll_offset_dip.x());
542 DCHECK_LE(0, scroll_offset_dip.y());
543 DCHECK_LE(scroll_offset_dip.x(), max_scroll_offset_dip_.x());
544 DCHECK_LE(scroll_offset_dip.y(), max_scroll_offset_dip_.y());
545
546 if (scroll_offset_dip_ == scroll_offset_dip)
547 return;
548
549 scroll_offset_dip_ = scroll_offset_dip;
550
551 TRACE_EVENT_INSTANT2("android_webview",
552 "BrowserViewRenderer::ScrollTo",
553 TRACE_EVENT_SCOPE_THREAD,
554 "x",
555 scroll_offset_dip.x(),
556 "y",
557 scroll_offset_dip.y());
558
559 if (compositor_)
560 compositor_->DidChangeRootLayerScrollOffset();
561 }
562
DidUpdateContent()563 void BrowserViewRenderer::DidUpdateContent() {
564 TRACE_EVENT_INSTANT0("android_webview",
565 "BrowserViewRenderer::DidUpdateContent",
566 TRACE_EVENT_SCOPE_THREAD);
567 clear_view_ = false;
568 if (on_new_picture_enable_)
569 client_->OnNewPicture();
570 }
571
SetTotalRootLayerScrollOffset(gfx::Vector2dF scroll_offset_dip)572 void BrowserViewRenderer::SetTotalRootLayerScrollOffset(
573 gfx::Vector2dF scroll_offset_dip) {
574 // TOOD(mkosiba): Add a DCHECK to say that this does _not_ get called during
575 // DrawGl when http://crbug.com/249972 is fixed.
576 if (scroll_offset_dip_ == scroll_offset_dip)
577 return;
578
579 scroll_offset_dip_ = scroll_offset_dip;
580
581 gfx::Vector2d max_offset = max_scroll_offset();
582 gfx::Vector2d scroll_offset;
583 // For an explanation as to why this is done this way see the comment in
584 // BrowserViewRenderer::ScrollTo.
585 if (max_scroll_offset_dip_.x()) {
586 scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) /
587 max_scroll_offset_dip_.x());
588 }
589
590 if (max_scroll_offset_dip_.y()) {
591 scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) /
592 max_scroll_offset_dip_.y());
593 }
594
595 DCHECK(0 <= scroll_offset.x());
596 DCHECK(0 <= scroll_offset.y());
597 DCHECK(scroll_offset.x() <= max_offset.x());
598 DCHECK(scroll_offset.y() <= max_offset.y());
599
600 client_->ScrollContainerViewTo(scroll_offset);
601 }
602
GetTotalRootLayerScrollOffset()603 gfx::Vector2dF BrowserViewRenderer::GetTotalRootLayerScrollOffset() {
604 return scroll_offset_dip_;
605 }
606
IsExternalFlingActive() const607 bool BrowserViewRenderer::IsExternalFlingActive() const {
608 return client_->IsFlingActive();
609 }
610
UpdateRootLayerState(const gfx::Vector2dF & total_scroll_offset_dip,const gfx::Vector2dF & max_scroll_offset_dip,const gfx::SizeF & scrollable_size_dip,float page_scale_factor,float min_page_scale_factor,float max_page_scale_factor)611 void BrowserViewRenderer::UpdateRootLayerState(
612 const gfx::Vector2dF& total_scroll_offset_dip,
613 const gfx::Vector2dF& max_scroll_offset_dip,
614 const gfx::SizeF& scrollable_size_dip,
615 float page_scale_factor,
616 float min_page_scale_factor,
617 float max_page_scale_factor) {
618 TRACE_EVENT_INSTANT1(
619 "android_webview",
620 "BrowserViewRenderer::UpdateRootLayerState",
621 TRACE_EVENT_SCOPE_THREAD,
622 "state",
623 TracedValue::FromValue(
624 RootLayerStateAsValue(total_scroll_offset_dip, scrollable_size_dip)
625 .release()));
626
627 DCHECK_GT(dip_scale_, 0);
628
629 max_scroll_offset_dip_ = max_scroll_offset_dip;
630 DCHECK_LE(0, max_scroll_offset_dip_.x());
631 DCHECK_LE(0, max_scroll_offset_dip_.y());
632
633 page_scale_factor_ = page_scale_factor;
634 DCHECK_GT(page_scale_factor_, 0);
635
636 client_->UpdateScrollState(max_scroll_offset(),
637 scrollable_size_dip,
638 page_scale_factor,
639 min_page_scale_factor,
640 max_page_scale_factor);
641 SetTotalRootLayerScrollOffset(total_scroll_offset_dip);
642 }
643
RootLayerStateAsValue(const gfx::Vector2dF & total_scroll_offset_dip,const gfx::SizeF & scrollable_size_dip)644 scoped_ptr<base::Value> BrowserViewRenderer::RootLayerStateAsValue(
645 const gfx::Vector2dF& total_scroll_offset_dip,
646 const gfx::SizeF& scrollable_size_dip) {
647 scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
648
649 state->SetDouble("total_scroll_offset_dip.x", total_scroll_offset_dip.x());
650 state->SetDouble("total_scroll_offset_dip.y", total_scroll_offset_dip.y());
651
652 state->SetDouble("max_scroll_offset_dip.x", max_scroll_offset_dip_.x());
653 state->SetDouble("max_scroll_offset_dip.y", max_scroll_offset_dip_.y());
654
655 state->SetDouble("scrollable_size_dip.width", scrollable_size_dip.width());
656 state->SetDouble("scrollable_size_dip.height", scrollable_size_dip.height());
657
658 state->SetDouble("page_scale_factor", page_scale_factor_);
659 return state.PassAs<base::Value>();
660 }
661
DidOverscroll(gfx::Vector2dF accumulated_overscroll,gfx::Vector2dF latest_overscroll_delta,gfx::Vector2dF current_fling_velocity)662 void BrowserViewRenderer::DidOverscroll(gfx::Vector2dF accumulated_overscroll,
663 gfx::Vector2dF latest_overscroll_delta,
664 gfx::Vector2dF current_fling_velocity) {
665 const float physical_pixel_scale = dip_scale_ * page_scale_factor_;
666 if (accumulated_overscroll == latest_overscroll_delta)
667 overscroll_rounding_error_ = gfx::Vector2dF();
668 gfx::Vector2dF scaled_overscroll_delta =
669 gfx::ScaleVector2d(latest_overscroll_delta, physical_pixel_scale);
670 gfx::Vector2d rounded_overscroll_delta = gfx::ToRoundedVector2d(
671 scaled_overscroll_delta + overscroll_rounding_error_);
672 overscroll_rounding_error_ =
673 scaled_overscroll_delta - rounded_overscroll_delta;
674 client_->DidOverscroll(rounded_overscroll_delta);
675 }
676
EnsureContinuousInvalidation(bool force_invalidate)677 void BrowserViewRenderer::EnsureContinuousInvalidation(bool force_invalidate) {
678 // This method should be called again when any of these conditions change.
679 bool need_invalidate =
680 compositor_needs_continuous_invalidate_ || force_invalidate;
681 if (!need_invalidate || block_invalidates_)
682 return;
683
684 // Always call view invalidate. We rely the Android framework to ignore the
685 // invalidate when it's not needed such as when view is not visible.
686 client_->PostInvalidate();
687
688 // Stop fallback ticks when one of these is true.
689 // 1) Webview is paused. Also need to check we are not in clear view since
690 // paused, offscreen still expect clear view to recover.
691 // 2) If we are attached to window and the window is not visible (eg when
692 // app is in the background). We are sure in this case the webview is used
693 // "on-screen" but that updates are not needed when in the background.
694 bool throttle_fallback_tick =
695 (is_paused_ && !clear_view_) || (attached_to_window_ && !window_visible_);
696 if (throttle_fallback_tick)
697 return;
698
699 block_invalidates_ = compositor_needs_continuous_invalidate_;
700
701 // Unretained here is safe because the callbacks are cancelled when
702 // they are destroyed.
703 post_fallback_tick_.Reset(base::Bind(&BrowserViewRenderer::PostFallbackTick,
704 base::Unretained(this)));
705 fallback_tick_fired_.Cancel();
706
707 // No need to reschedule fallback tick if compositor does not need to be
708 // ticked. This can happen if this is reached because force_invalidate is
709 // true.
710 if (compositor_needs_continuous_invalidate_)
711 ui_task_runner_->PostTask(FROM_HERE, post_fallback_tick_.callback());
712 }
713
PostFallbackTick()714 void BrowserViewRenderer::PostFallbackTick() {
715 DCHECK(fallback_tick_fired_.IsCancelled());
716 fallback_tick_fired_.Reset(base::Bind(&BrowserViewRenderer::FallbackTickFired,
717 base::Unretained(this)));
718 if (compositor_needs_continuous_invalidate_) {
719 ui_task_runner_->PostDelayedTask(
720 FROM_HERE,
721 fallback_tick_fired_.callback(),
722 base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds));
723 } else {
724 // Pretend we just composited to unblock further invalidates.
725 DidComposite();
726 }
727 }
728
FallbackTickFired()729 void BrowserViewRenderer::FallbackTickFired() {
730 TRACE_EVENT1("android_webview",
731 "BrowserViewRenderer::FallbackTickFired",
732 "compositor_needs_continuous_invalidate_",
733 compositor_needs_continuous_invalidate_);
734
735 // This should only be called if OnDraw or DrawGL did not come in time, which
736 // means block_invalidates_ must still be true.
737 DCHECK(block_invalidates_);
738 if (compositor_needs_continuous_invalidate_ && compositor_) {
739 ForceFakeCompositeSW();
740 client_->PostInvalidate();
741 } else {
742 // Pretend we just composited to unblock further invalidates.
743 DidComposite();
744 }
745 }
746
ForceFakeCompositeSW()747 void BrowserViewRenderer::ForceFakeCompositeSW() {
748 DCHECK(compositor_);
749 SkBitmap bitmap;
750 bitmap.allocN32Pixels(1, 1);
751 bitmap.eraseColor(0);
752 SkCanvas canvas(bitmap);
753 CompositeSW(&canvas);
754 }
755
CompositeSW(SkCanvas * canvas)756 bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) {
757 DCHECK(compositor_);
758 ReturnResourceFromParent();
759 bool result = compositor_->DemandDrawSw(canvas);
760 DidComposite();
761 return result;
762 }
763
DidComposite()764 void BrowserViewRenderer::DidComposite() {
765 block_invalidates_ = false;
766 post_fallback_tick_.Cancel();
767 fallback_tick_fired_.Cancel();
768 EnsureContinuousInvalidation(false);
769 }
770
ToString(AwDrawGLInfo * draw_info) const771 std::string BrowserViewRenderer::ToString(AwDrawGLInfo* draw_info) const {
772 std::string str;
773 base::StringAppendF(&str, "is_paused: %d ", is_paused_);
774 base::StringAppendF(&str, "view_visible: %d ", view_visible_);
775 base::StringAppendF(&str, "window_visible: %d ", window_visible_);
776 base::StringAppendF(&str, "dip_scale: %f ", dip_scale_);
777 base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_);
778 base::StringAppendF(&str,
779 "compositor_needs_continuous_invalidate: %d ",
780 compositor_needs_continuous_invalidate_);
781 base::StringAppendF(&str, "block_invalidates: %d ", block_invalidates_);
782 base::StringAppendF(&str, "view width height: [%d %d] ", width_, height_);
783 base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_);
784 base::StringAppendF(&str,
785 "global visible rect: %s ",
786 last_on_draw_global_visible_rect_.ToString().c_str());
787 base::StringAppendF(
788 &str, "scroll_offset_dip: %s ", scroll_offset_dip_.ToString().c_str());
789 base::StringAppendF(&str,
790 "overscroll_rounding_error_: %s ",
791 overscroll_rounding_error_.ToString().c_str());
792 base::StringAppendF(
793 &str, "on_new_picture_enable: %d ", on_new_picture_enable_);
794 base::StringAppendF(&str, "clear_view: %d ", clear_view_);
795 if (draw_info) {
796 base::StringAppendF(&str,
797 "clip left top right bottom: [%d %d %d %d] ",
798 draw_info->clip_left,
799 draw_info->clip_top,
800 draw_info->clip_right,
801 draw_info->clip_bottom);
802 base::StringAppendF(&str,
803 "surface width height: [%d %d] ",
804 draw_info->width,
805 draw_info->height);
806 base::StringAppendF(&str, "is_layer: %d ", draw_info->is_layer);
807 }
808 return str;
809 }
810
811 } // namespace android_webview
812