OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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_impl.h" | |
6 | |
7 #include <android/bitmap.h> | |
8 | |
9 #include "android_webview/browser/in_process_view_renderer.h" | |
10 #include "android_webview/common/aw_switches.h" | |
11 #include "android_webview/common/renderer_picture_map.h" | |
12 #include "android_webview/public/browser/draw_gl.h" | |
13 #include "android_webview/public/browser/draw_sw.h" | |
14 #include "base/android/jni_android.h" | |
15 #include "base/command_line.h" | |
16 #include "base/debug/trace_event.h" | |
17 #include "base/logging.h" | |
18 #include "cc/layers/layer.h" | |
19 #include "content/public/browser/android/content_view_core.h" | |
20 #include "content/public/browser/render_process_host.h" | |
21 #include "content/public/browser/render_view_host.h" | |
22 #include "content/public/browser/web_contents.h" | |
23 #include "third_party/skia/include/core/SkBitmap.h" | |
24 #include "third_party/skia/include/core/SkCanvas.h" | |
25 #include "third_party/skia/include/core/SkDevice.h" | |
26 #include "third_party/skia/include/core/SkGraphics.h" | |
27 #include "ui/gfx/size_conversions.h" | |
28 #include "ui/gfx/transform.h" | |
29 #include "ui/gfx/vector2d_f.h" | |
30 #include "ui/gl/gl_bindings.h" | |
31 | |
32 using base::android::AttachCurrentThread; | |
33 using base::android::JavaRef; | |
34 using base::android::ScopedJavaLocalRef; | |
35 using content::Compositor; | |
36 using content::ContentViewCore; | |
37 | |
38 namespace android_webview { | |
39 | |
40 namespace { | |
41 | |
42 // Provides software rendering functions from the Android glue layer. | |
43 // Allows preventing extra copies of data when rendering. | |
44 AwDrawSWFunctionTable* g_sw_draw_functions = NULL; | |
45 | |
46 // Tells if the Skia library versions in Android and Chromium are compatible. | |
47 // If they are then it's possible to pass Skia objects like SkPictures to the | |
48 // Android glue layer via the SW rendering functions. | |
49 // If they are not, then additional copies and rasterizations are required | |
50 // as a fallback mechanism, which will have an important performance impact. | |
51 bool g_is_skia_version_compatible = false; | |
52 | |
53 typedef base::Callback<bool(SkCanvas*)> RenderMethod; | |
54 | |
55 bool RasterizeIntoBitmap(JNIEnv* env, | |
56 const JavaRef<jobject>& jbitmap, | |
57 int scroll_x, | |
58 int scroll_y, | |
59 const RenderMethod& renderer) { | |
60 DCHECK(jbitmap.obj()); | |
61 | |
62 AndroidBitmapInfo bitmap_info; | |
63 if (AndroidBitmap_getInfo(env, jbitmap.obj(), &bitmap_info) < 0) { | |
64 LOG(ERROR) << "Error getting java bitmap info."; | |
65 return false; | |
66 } | |
67 | |
68 void* pixels = NULL; | |
69 if (AndroidBitmap_lockPixels(env, jbitmap.obj(), &pixels) < 0) { | |
70 LOG(ERROR) << "Error locking java bitmap pixels."; | |
71 return false; | |
72 } | |
73 | |
74 bool succeeded; | |
75 { | |
76 SkBitmap bitmap; | |
77 bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
78 bitmap_info.width, | |
79 bitmap_info.height, | |
80 bitmap_info.stride); | |
81 bitmap.setPixels(pixels); | |
82 | |
83 SkDevice device(bitmap); | |
84 SkCanvas canvas(&device); | |
85 canvas.translate(-scroll_x, -scroll_y); | |
86 succeeded = renderer.Run(&canvas); | |
87 } | |
88 | |
89 if (AndroidBitmap_unlockPixels(env, jbitmap.obj()) < 0) { | |
90 LOG(ERROR) << "Error unlocking java bitmap pixels."; | |
91 return false; | |
92 } | |
93 | |
94 return succeeded; | |
95 } | |
96 | |
97 bool RenderPictureToCanvas(SkPicture* picture, SkCanvas* canvas) { | |
98 canvas->drawPicture(*picture); | |
99 return true; | |
100 } | |
101 | |
102 const void* kUserDataKey = &kUserDataKey; | |
103 | |
104 } // namespace | |
105 | |
106 class BrowserViewRendererImpl::UserData : public content::WebContents::Data { | |
107 public: | |
108 UserData(BrowserViewRendererImpl* ptr) : instance_(ptr) {} | |
109 virtual ~UserData() { | |
110 instance_->WebContentsGone(); | |
111 } | |
112 | |
113 static BrowserViewRendererImpl* GetInstance(content::WebContents* contents) { | |
114 if (!contents) | |
115 return NULL; | |
116 UserData* data = reinterpret_cast<UserData*>( | |
117 contents->GetUserData(kUserDataKey)); | |
118 return data ? data->instance_ : NULL; | |
119 } | |
120 | |
121 private: | |
122 BrowserViewRendererImpl* instance_; | |
123 }; | |
124 | |
125 // static | |
126 BrowserViewRenderer* BrowserViewRendererImpl::Create( | |
127 BrowserViewRenderer::Client* client, | |
128 JavaHelper* java_helper) { | |
129 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
130 switches::kNoMergeUIAndRendererCompositorThreads)) { | |
131 return new BrowserViewRendererImpl(client, java_helper); | |
132 } | |
133 | |
134 return new InProcessViewRenderer(client, java_helper); | |
135 } | |
136 | |
137 // static | |
138 BrowserViewRendererImpl* BrowserViewRendererImpl::FromWebContents( | |
139 content::WebContents* contents) { | |
140 return UserData::GetInstance(contents); | |
141 } | |
142 | |
143 BrowserViewRendererImpl::BrowserViewRendererImpl( | |
144 BrowserViewRenderer::Client* client, | |
145 JavaHelper* java_helper) | |
146 : client_(client), | |
147 java_helper_(java_helper), | |
148 view_renderer_host_(new ViewRendererHost(NULL, this)), | |
149 compositor_(Compositor::Create(this)), | |
150 view_clip_layer_(cc::Layer::Create()), | |
151 transform_layer_(cc::Layer::Create()), | |
152 scissor_clip_layer_(cc::Layer::Create()), | |
153 view_attached_(false), | |
154 view_visible_(false), | |
155 compositor_visible_(false), | |
156 is_composite_pending_(false), | |
157 dpi_scale_(1.0f), | |
158 page_scale_(1.0f), | |
159 new_picture_enabled_(false), | |
160 last_frame_context_(NULL), | |
161 web_contents_(NULL), | |
162 update_frame_info_callback_( | |
163 base::Bind(&BrowserViewRendererImpl::OnFrameInfoUpdated, | |
164 base::Unretained(this))), | |
165 prevent_client_invalidate_(false) { | |
166 | |
167 DCHECK(java_helper); | |
168 | |
169 // Define the view hierarchy. | |
170 transform_layer_->AddChild(view_clip_layer_); | |
171 scissor_clip_layer_->AddChild(transform_layer_); | |
172 compositor_->SetRootLayer(scissor_clip_layer_); | |
173 | |
174 RendererPictureMap::CreateInstance(); | |
175 } | |
176 | |
177 BrowserViewRendererImpl::~BrowserViewRendererImpl() { | |
178 SetContents(NULL); | |
179 } | |
180 | |
181 // static | |
182 void BrowserViewRenderer::SetAwDrawSWFunctionTable( | |
183 AwDrawSWFunctionTable* table) { | |
184 g_sw_draw_functions = table; | |
185 g_is_skia_version_compatible = | |
186 g_sw_draw_functions->is_skia_version_compatible(&SkGraphics::GetVersion); | |
187 LOG_IF(WARNING, !g_is_skia_version_compatible) | |
188 << "Skia versions are not compatible, rendering performance will suffer."; | |
189 } | |
190 | |
191 // static | |
192 AwDrawSWFunctionTable* BrowserViewRenderer::GetAwDrawSWFunctionTable() { | |
193 return g_sw_draw_functions; | |
194 } | |
195 | |
196 // static | |
197 bool BrowserViewRenderer::IsSkiaVersionCompatible() { | |
198 DCHECK(g_sw_draw_functions); | |
199 return g_is_skia_version_compatible; | |
200 } | |
201 | |
202 void BrowserViewRendererImpl::SetContents(ContentViewCore* content_view_core) { | |
203 // First remove association from the prior ContentViewCore / WebContents. | |
204 if (web_contents_) { | |
205 ContentViewCore* previous_content_view_core = | |
206 ContentViewCore::FromWebContents(web_contents_); | |
207 if (previous_content_view_core) { | |
208 previous_content_view_core->RemoveFrameInfoCallback( | |
209 update_frame_info_callback_); | |
210 } | |
211 web_contents_->SetUserData(kUserDataKey, NULL); | |
212 DCHECK(!web_contents_); // WebContentsGone should have been called. | |
213 } | |
214 | |
215 if (!content_view_core) | |
216 return; | |
217 | |
218 web_contents_ = content_view_core->GetWebContents(); | |
219 web_contents_->SetUserData(kUserDataKey, new UserData(this)); | |
220 view_renderer_host_->Observe(web_contents_); | |
221 | |
222 content_view_core->AddFrameInfoCallback(update_frame_info_callback_); | |
223 dpi_scale_ = content_view_core->GetDpiScale(); | |
224 | |
225 view_clip_layer_->RemoveAllChildren(); | |
226 view_clip_layer_->AddChild(content_view_core->GetLayer()); | |
227 Invalidate(); | |
228 } | |
229 | |
230 void BrowserViewRendererImpl::WebContentsGone() { | |
231 web_contents_ = NULL; | |
232 } | |
233 | |
234 bool BrowserViewRendererImpl::PrepareDrawGL(int x, int y) { | |
235 hw_rendering_scroll_ = gfx::Point(x, y); | |
236 return true; | |
237 } | |
238 | |
239 void BrowserViewRendererImpl::DrawGL(AwDrawGLInfo* draw_info) { | |
240 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawGL"); | |
241 | |
242 if (view_size_.IsEmpty() || !scissor_clip_layer_ || | |
243 draw_info->mode == AwDrawGLInfo::kModeProcess) | |
244 return; | |
245 | |
246 DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw); | |
247 | |
248 SetCompositorVisibility(view_visible_); | |
249 if (!compositor_visible_) | |
250 return; | |
251 | |
252 // We need to watch if the current Android context has changed and enforce | |
253 // a clean-up in the compositor. | |
254 EGLContext current_context = eglGetCurrentContext(); | |
255 if (!current_context) { | |
256 LOG(WARNING) << "No current context attached. Skipping composite."; | |
257 return; | |
258 } | |
259 | |
260 if (last_frame_context_ != current_context) { | |
261 if (last_frame_context_) | |
262 ResetCompositor(); | |
263 last_frame_context_ = current_context; | |
264 } | |
265 | |
266 compositor_->SetWindowBounds(gfx::Size(draw_info->width, draw_info->height)); | |
267 | |
268 // We need to trigger a compositor invalidate because otherwise, if nothing | |
269 // has changed since last draw the compositor will early out (Android may | |
270 // trigger a draw at anytime). However, we don't want to trigger a client | |
271 // (i.e. Android View system) invalidate as a result of this (otherwise we'll | |
272 // end up in a loop of DrawGL calls). | |
273 prevent_client_invalidate_ = true; | |
274 compositor_->SetNeedsRedraw(); | |
275 prevent_client_invalidate_ = false; | |
276 | |
277 if (draw_info->is_layer) { | |
278 // When rendering into a separate layer no view clipping, transform, | |
279 // scissoring or background transparency need to be handled. | |
280 // The Android framework will composite us afterwards. | |
281 compositor_->SetHasTransparentBackground(false); | |
282 view_clip_layer_->SetMasksToBounds(false); | |
283 transform_layer_->SetTransform(gfx::Transform()); | |
284 scissor_clip_layer_->SetMasksToBounds(false); | |
285 scissor_clip_layer_->SetPosition(gfx::PointF()); | |
286 scissor_clip_layer_->SetBounds(gfx::Size()); | |
287 scissor_clip_layer_->SetSublayerTransform(gfx::Transform()); | |
288 | |
289 } else { | |
290 compositor_->SetHasTransparentBackground(true); | |
291 | |
292 gfx::Rect clip_rect(draw_info->clip_left, draw_info->clip_top, | |
293 draw_info->clip_right - draw_info->clip_left, | |
294 draw_info->clip_bottom - draw_info->clip_top); | |
295 | |
296 scissor_clip_layer_->SetPosition(clip_rect.origin()); | |
297 scissor_clip_layer_->SetBounds(clip_rect.size()); | |
298 scissor_clip_layer_->SetMasksToBounds(true); | |
299 | |
300 // The compositor clipping architecture enforces us to have the clip layer | |
301 // as an ancestor of the area we want to clip, but this makes the transform | |
302 // become relative to the clip area rather than the full surface. The clip | |
303 // position offset needs to be undone before applying the transform. | |
304 gfx::Transform undo_clip_position; | |
305 undo_clip_position.Translate(-clip_rect.x(), -clip_rect.y()); | |
306 scissor_clip_layer_->SetSublayerTransform(undo_clip_position); | |
307 | |
308 gfx::Transform transform; | |
309 transform.matrix().setColMajorf(draw_info->transform); | |
310 | |
311 // The scrolling values of the Android Framework affect the transformation | |
312 // matrix. This needs to be undone to let the compositor handle scrolling. | |
313 // TODO(leandrogracia): when scrolling becomes synchronous we should undo | |
314 // or override the translation in the compositor, not the one coming from | |
315 // the Android View System, as it could be out of bounds for overscrolling. | |
316 transform.Translate(hw_rendering_scroll_.x(), hw_rendering_scroll_.y()); | |
317 transform_layer_->SetTransform(transform); | |
318 | |
319 view_clip_layer_->SetMasksToBounds(true); | |
320 } | |
321 | |
322 compositor_->Composite(); | |
323 is_composite_pending_ = false; | |
324 | |
325 // The GL functor must ensure these are set to zero before returning. | |
326 // Not setting them leads to graphical artifacts that can affect other apps. | |
327 glBindBuffer(GL_ARRAY_BUFFER, 0); | |
328 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | |
329 } | |
330 | |
331 bool BrowserViewRendererImpl::DrawSW(jobject java_canvas, | |
332 const gfx::Rect& clip) { | |
333 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::DrawSW"); | |
334 | |
335 if (clip.IsEmpty()) | |
336 return true; | |
337 | |
338 AwPixelInfo* pixels; | |
339 JNIEnv* env = AttachCurrentThread(); | |
340 | |
341 // Render into an auxiliary bitmap if pixel info is not available. | |
342 if (!g_sw_draw_functions || | |
343 (pixels = g_sw_draw_functions->access_pixels(env, java_canvas)) == NULL) { | |
344 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap( | |
345 env, clip.width(), clip.height(), true)); | |
346 if (!jbitmap.obj()) | |
347 return false; | |
348 | |
349 if (!RasterizeIntoBitmap(env, jbitmap, clip.x(), clip.y(), | |
350 base::Bind(&BrowserViewRendererImpl::RenderSW, | |
351 base::Unretained(this)))) { | |
352 return false; | |
353 } | |
354 | |
355 ScopedJavaLocalRef<jobject> jcanvas(env, java_canvas); | |
356 java_helper_->DrawBitmapIntoCanvas(env, jbitmap, jcanvas, | |
357 clip.x(), clip.y()); | |
358 return true; | |
359 } | |
360 | |
361 // Draw in a SkCanvas built over the pixel information. | |
362 bool succeeded = false; | |
363 { | |
364 SkBitmap bitmap; | |
365 bitmap.setConfig(static_cast<SkBitmap::Config>(pixels->config), | |
366 pixels->width, | |
367 pixels->height, | |
368 pixels->row_bytes); | |
369 bitmap.setPixels(pixels->pixels); | |
370 SkDevice device(bitmap); | |
371 SkCanvas canvas(&device); | |
372 SkMatrix matrix; | |
373 for (int i = 0; i < 9; i++) | |
374 matrix.set(i, pixels->matrix[i]); | |
375 canvas.setMatrix(matrix); | |
376 | |
377 SkRegion clip; | |
378 if (pixels->clip_region_size) { | |
379 size_t bytes_read = clip.readFromMemory(pixels->clip_region); | |
380 DCHECK_EQ(pixels->clip_region_size, bytes_read); | |
381 canvas.setClipRegion(clip); | |
382 } else { | |
383 clip.setRect(SkIRect::MakeWH(pixels->width, pixels->height)); | |
384 } | |
385 | |
386 succeeded = RenderSW(&canvas); | |
387 } | |
388 | |
389 g_sw_draw_functions->release_pixels(pixels); | |
390 return succeeded; | |
391 } | |
392 | |
393 ScopedJavaLocalRef<jobject> BrowserViewRendererImpl::CapturePicture() { | |
394 if (!g_sw_draw_functions) | |
395 return ScopedJavaLocalRef<jobject>(); | |
396 | |
397 gfx::Size record_size = ToCeiledSize(content_size_css_); | |
398 | |
399 // Return empty Picture objects for empty SkPictures. | |
400 JNIEnv* env = AttachCurrentThread(); | |
401 if (record_size.width() <= 0 || record_size.height() <= 0) { | |
402 return java_helper_->RecordBitmapIntoPicture( | |
403 env, ScopedJavaLocalRef<jobject>()); | |
404 } | |
405 | |
406 skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture); | |
407 SkCanvas* rec_canvas = picture->beginRecording(record_size.width(), | |
408 record_size.height(), | |
409 0); | |
410 if (!RenderPicture(rec_canvas)) | |
411 return ScopedJavaLocalRef<jobject>(); | |
412 picture->endRecording(); | |
413 | |
414 if (g_is_skia_version_compatible) { | |
415 // Add a reference that the create_picture() will take ownership of. | |
416 picture->ref(); | |
417 return ScopedJavaLocalRef<jobject>(env, | |
418 g_sw_draw_functions->create_picture(env, picture.get())); | |
419 } | |
420 | |
421 // If Skia versions are not compatible, workaround it by rasterizing the | |
422 // picture into a bitmap and drawing it into a new Java picture. | |
423 ScopedJavaLocalRef<jobject> jbitmap(java_helper_->CreateBitmap( | |
424 env, picture->width(), picture->height(), false)); | |
425 if (!jbitmap.obj()) | |
426 return ScopedJavaLocalRef<jobject>(); | |
427 | |
428 if (!RasterizeIntoBitmap(env, jbitmap, 0, 0, | |
429 base::Bind(&RenderPictureToCanvas, | |
430 base::Unretained(picture.get())))) { | |
431 return ScopedJavaLocalRef<jobject>(); | |
432 } | |
433 | |
434 return java_helper_->RecordBitmapIntoPicture(env, jbitmap); | |
435 } | |
436 | |
437 void BrowserViewRendererImpl::EnableOnNewPicture(bool enabled) { | |
438 new_picture_enabled_ = enabled; | |
439 | |
440 // TODO(leandrogracia): when SW rendering uses the compositor rather than | |
441 // picture rasterization, send update the renderer side with the correct | |
442 // listener state. (For now, we always leave render picture listener enabled). | |
443 // render_view_host_ext_->EnableCapturePictureCallback(enabled); | |
444 // http://crbug.com/176945 | |
445 } | |
446 | |
447 void BrowserViewRendererImpl::OnVisibilityChanged(bool view_visible, | |
448 bool window_visible) { | |
449 view_visible_ = window_visible && view_visible; | |
450 Invalidate(); | |
451 } | |
452 | |
453 void BrowserViewRendererImpl::OnSizeChanged(int width, int height) { | |
454 view_size_ = gfx::Size(width, height); | |
455 view_clip_layer_->SetBounds(view_size_); | |
456 } | |
457 | |
458 void BrowserViewRendererImpl::OnAttachedToWindow(int width, int height) { | |
459 view_attached_ = true; | |
460 view_size_ = gfx::Size(width, height); | |
461 view_clip_layer_->SetBounds(view_size_); | |
462 } | |
463 | |
464 void BrowserViewRendererImpl::OnDetachedFromWindow() { | |
465 view_attached_ = false; | |
466 view_visible_ = false; | |
467 SetCompositorVisibility(false); | |
468 } | |
469 | |
470 bool BrowserViewRendererImpl::IsAttachedToWindow() { | |
471 return view_attached_; | |
472 } | |
473 | |
474 bool BrowserViewRendererImpl::IsViewVisible() { | |
475 return view_visible_; | |
476 } | |
477 | |
478 gfx::Rect BrowserViewRendererImpl::GetScreenRect() { | |
479 return gfx::Rect(client_->GetLocationOnScreen(), view_size_); | |
480 } | |
481 | |
482 void BrowserViewRendererImpl::ScheduleComposite() { | |
483 TRACE_EVENT0("android_webview", "BrowserViewRendererImpl::ScheduleComposite"); | |
484 | |
485 if (is_composite_pending_) | |
486 return; | |
487 | |
488 is_composite_pending_ = true; | |
489 | |
490 if (!prevent_client_invalidate_) | |
491 Invalidate(); | |
492 } | |
493 | |
494 skia::RefPtr<SkPicture> BrowserViewRendererImpl::GetLastCapturedPicture() { | |
495 // Use the latest available picture if the listener callback is enabled. | |
496 skia::RefPtr<SkPicture> picture = | |
497 RendererPictureMap::GetInstance()->GetRendererPicture( | |
498 web_contents_->GetRoutingID()); | |
499 if (picture) return picture; | |
500 | |
501 // Get it synchronously. | |
502 view_renderer_host_->CapturePictureSync(); | |
503 return RendererPictureMap::GetInstance()->GetRendererPicture( | |
504 web_contents_->GetRoutingID()); | |
505 } | |
506 | |
507 void BrowserViewRendererImpl::OnPictureUpdated(int process_id, | |
508 int render_view_id) { | |
509 CHECK_EQ(web_contents_->GetRenderProcessHost()->GetID(), process_id); | |
510 if (render_view_id != web_contents_->GetRoutingID()) | |
511 return; | |
512 | |
513 client_->OnNewPicture(); | |
514 | |
515 // TODO(mkosiba): Remove when invalidation path is re-implemented. | |
516 Invalidate(); | |
517 } | |
518 | |
519 void BrowserViewRendererImpl::SetCompositorVisibility(bool visible) { | |
520 if (compositor_visible_ != visible) { | |
521 compositor_visible_ = visible; | |
522 compositor_->SetVisible(compositor_visible_); | |
523 } | |
524 } | |
525 | |
526 void BrowserViewRendererImpl::ResetCompositor() { | |
527 compositor_.reset(content::Compositor::Create(this)); | |
528 compositor_->SetRootLayer(scissor_clip_layer_); | |
529 } | |
530 | |
531 void BrowserViewRendererImpl::Invalidate() { | |
532 if (view_visible_) | |
533 client_->Invalidate(); | |
534 } | |
535 | |
536 bool BrowserViewRendererImpl::RenderSW(SkCanvas* canvas) { | |
537 float content_scale = dpi_scale_ * page_scale_; | |
538 canvas->scale(content_scale, content_scale); | |
539 | |
540 // Clear to white any parts of the view not covered by the scaled contents. | |
541 // TODO(leandrogracia): this should be automatically done by the SW rendering | |
542 // path once multiple layers are supported. | |
543 gfx::Size physical_content_size = gfx::ToCeiledSize( | |
544 gfx::ScaleSize(content_size_css_, content_scale)); | |
545 if (physical_content_size.width() < view_size_.width() || | |
546 physical_content_size.height() < view_size_.height()) | |
547 canvas->clear(SK_ColorWHITE); | |
548 | |
549 // TODO(leandrogracia): use the appropriate SW rendering path when available | |
550 // instead of abusing CapturePicture. | |
551 return RenderPicture(canvas); | |
552 } | |
553 | |
554 bool BrowserViewRendererImpl::RenderPicture(SkCanvas* canvas) { | |
555 skia::RefPtr<SkPicture> picture = GetLastCapturedPicture(); | |
556 if (!picture) | |
557 return false; | |
558 | |
559 picture->draw(canvas); | |
560 return true; | |
561 } | |
562 | |
563 void BrowserViewRendererImpl::OnFrameInfoUpdated( | |
564 const gfx::SizeF& content_size, | |
565 const gfx::Vector2dF& scroll_offset, | |
566 float page_scale_factor) { | |
567 page_scale_ = page_scale_factor; | |
568 content_size_css_ = content_size; | |
569 } | |
570 | |
571 } // namespace android_webview | |
OLD | NEW |