| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "android_webview/browser/browser_view_renderer.h" | 
 |  | 
 | #include <memory> | 
 | #include <utility> | 
 |  | 
 | #include "android_webview/browser/browser_view_renderer_client.h" | 
 | #include "android_webview/browser/compositor_frame_consumer.h" | 
 | #include "android_webview/common/aw_switches.h" | 
 | #include "base/auto_reset.h" | 
 | #include "base/command_line.h" | 
 | #include "base/logging.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/supports_user_data.h" | 
 | #include "base/trace_event/trace_event_argument.h" | 
 | #include "components/viz/common/quads/compositor_frame.h" | 
 | #include "content/public/browser/render_process_host.h" | 
 | #include "content/public/browser/render_view_host.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "content/public/common/use_zoom_for_dsf_policy.h" | 
 | #include "gpu/command_buffer/service/gpu_switches.h" | 
 | #include "third_party/skia/include/core/SkBitmap.h" | 
 | #include "third_party/skia/include/core/SkCanvas.h" | 
 | #include "third_party/skia/include/core/SkPicture.h" | 
 | #include "third_party/skia/include/core/SkPictureRecorder.h" | 
 | #include "ui/gfx/geometry/point.h" | 
 | #include "ui/gfx/geometry/scroll_offset.h" | 
 | #include "ui/gfx/geometry/vector2d_conversions.h" | 
 |  | 
 | namespace android_webview { | 
 |  | 
 | namespace { | 
 |  | 
 | const double kEpsilon = 1e-8; | 
 |  | 
 | // Used to calculate memory allocation. Determined experimentally. | 
 | const size_t kMemoryMultiplier = 20; | 
 | const size_t kBytesPerPixel = 4; | 
 | const size_t kMemoryAllocationStep = 5 * 1024 * 1024; | 
 | uint64_t g_memory_override_in_bytes = 0u; | 
 |  | 
 | const void* const kBrowserViewRendererUserDataKey = | 
 |     &kBrowserViewRendererUserDataKey; | 
 |  | 
 | class BrowserViewRendererUserData : public base::SupportsUserData::Data { | 
 |  public: | 
 |   explicit BrowserViewRendererUserData(BrowserViewRenderer* ptr) : bvr_(ptr) {} | 
 |  | 
 |   static BrowserViewRenderer* GetBrowserViewRenderer( | 
 |       content::WebContents* web_contents) { | 
 |     if (!web_contents) | 
 |       return NULL; | 
 |     BrowserViewRendererUserData* data = | 
 |         static_cast<BrowserViewRendererUserData*>( | 
 |             web_contents->GetUserData(kBrowserViewRendererUserDataKey)); | 
 |     return data ? data->bvr_ : NULL; | 
 |   } | 
 |  | 
 |  private: | 
 |   BrowserViewRenderer* bvr_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | void BrowserViewRenderer::CalculateTileMemoryPolicy() { | 
 |   base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); | 
 |  | 
 |   // If the value was overridden on the command line, use the specified value. | 
 |   bool client_hard_limit_bytes_overridden = | 
 |       cl->HasSwitch(switches::kForceGpuMemAvailableMb); | 
 |   if (client_hard_limit_bytes_overridden) { | 
 |     base::StringToUint64( | 
 |         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 
 |             switches::kForceGpuMemAvailableMb), | 
 |         &g_memory_override_in_bytes); | 
 |     g_memory_override_in_bytes *= 1024 * 1024; | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | BrowserViewRenderer* BrowserViewRenderer::FromWebContents( | 
 |     content::WebContents* web_contents) { | 
 |   return BrowserViewRendererUserData::GetBrowserViewRenderer(web_contents); | 
 | } | 
 |  | 
 | BrowserViewRenderer::BrowserViewRenderer( | 
 |     BrowserViewRendererClient* client, | 
 |     const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) | 
 |     : client_(client), | 
 |       ui_task_runner_(ui_task_runner), | 
 |       sync_on_draw_hardware_(base::CommandLine::ForCurrentProcess()->HasSwitch( | 
 |           switches::kSyncOnDrawHardware)), | 
 |       current_compositor_frame_consumer_(nullptr), | 
 |       compositor_(nullptr), | 
 |       is_paused_(false), | 
 |       view_visible_(false), | 
 |       window_visible_(false), | 
 |       attached_to_window_(false), | 
 |       was_attached_(false), | 
 |       hardware_enabled_(false), | 
 |       dip_scale_(0.f), | 
 |       page_scale_factor_(1.f), | 
 |       min_page_scale_factor_(0.f), | 
 |       max_page_scale_factor_(0.f), | 
 |       on_new_picture_enable_(false), | 
 |       clear_view_(false), | 
 |       offscreen_pre_raster_(false), | 
 |       allow_async_draw_(false) {} | 
 |  | 
 | BrowserViewRenderer::~BrowserViewRenderer() { | 
 |   DCHECK(compositor_map_.empty()); | 
 |   SetCurrentCompositorFrameConsumer(nullptr); | 
 |   while (compositor_frame_consumers_.size()) { | 
 |     RemoveCompositorFrameConsumer(*compositor_frame_consumers_.begin()); | 
 |   } | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetCurrentCompositorFrameConsumer( | 
 |     CompositorFrameConsumer* compositor_frame_consumer) { | 
 |   if (compositor_frame_consumer == current_compositor_frame_consumer_) { | 
 |     return; | 
 |   } | 
 |   current_compositor_frame_consumer_ = compositor_frame_consumer; | 
 |   if (current_compositor_frame_consumer_) { | 
 |     compositor_frame_consumers_.insert(current_compositor_frame_consumer_); | 
 |     current_compositor_frame_consumer_->SetCompositorFrameProducer(this); | 
 |     OnParentDrawConstraintsUpdated(current_compositor_frame_consumer_); | 
 |   } | 
 | } | 
 |  | 
 | void BrowserViewRenderer::RegisterWithWebContents( | 
 |     content::WebContents* web_contents) { | 
 |   web_contents->SetUserData( | 
 |       kBrowserViewRendererUserDataKey, | 
 |       std::make_unique<BrowserViewRendererUserData>(this)); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::TrimMemory() { | 
 |   DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 
 |   TRACE_EVENT0("android_webview", "BrowserViewRenderer::TrimMemory"); | 
 |   // Just set the memory limit to 0 and drop all tiles. This will be reset to | 
 |   // normal levels in the next DrawGL call. | 
 |   if (!offscreen_pre_raster_) | 
 |     ReleaseHardware(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::UpdateMemoryPolicy() { | 
 |   if (!compositor_) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (!hardware_enabled_) { | 
 |     compositor_->SetMemoryPolicy(0u); | 
 |     return; | 
 |   } | 
 |  | 
 |   size_t bytes_limit = 0u; | 
 |   if (g_memory_override_in_bytes) { | 
 |     bytes_limit = static_cast<size_t>(g_memory_override_in_bytes); | 
 |   } else { | 
 |     gfx::Rect interest_rect = | 
 |         offscreen_pre_raster_ || external_draw_constraints_.is_layer | 
 |             ? gfx::Rect(size_) | 
 |             : last_on_draw_global_visible_rect_; | 
 |     size_t width = interest_rect.width(); | 
 |     size_t height = interest_rect.height(); | 
 |     bytes_limit = kMemoryMultiplier * kBytesPerPixel * width * height; | 
 |     // Round up to a multiple of kMemoryAllocationStep. | 
 |     bytes_limit = | 
 |         (bytes_limit / kMemoryAllocationStep + 1) * kMemoryAllocationStep; | 
 |   } | 
 |  | 
 |   compositor_->SetMemoryPolicy(bytes_limit); | 
 | } | 
 |  | 
 | content::SynchronousCompositor* BrowserViewRenderer::FindCompositor( | 
 |     const CompositorID& compositor_id) const { | 
 |   const auto& compositor_iterator = compositor_map_.find(compositor_id); | 
 |   if (compositor_iterator == compositor_map_.end()) | 
 |     return nullptr; | 
 |  | 
 |   return compositor_iterator->second; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::PrepareToDraw(const gfx::Vector2d& scroll, | 
 |                                         const gfx::Rect& global_visible_rect) { | 
 |   last_on_draw_scroll_offset_ = scroll; | 
 |   last_on_draw_global_visible_rect_ = global_visible_rect; | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::CanOnDraw() { | 
 |   if (!compositor_) { | 
 |     TRACE_EVENT_INSTANT0("android_webview", "EarlyOut_NoCompositor", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |     return false; | 
 |   } | 
 |   if (clear_view_) { | 
 |     TRACE_EVENT_INSTANT0("android_webview", "EarlyOut_ClearView", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::OnDrawHardware() { | 
 |   DCHECK(current_compositor_frame_consumer_); | 
 |   TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware"); | 
 |  | 
 |   current_compositor_frame_consumer_->InitializeHardwareDrawIfNeededOnUI(); | 
 |  | 
 |   if (!CanOnDraw()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   current_compositor_frame_consumer_->SetScrollOffsetOnUI( | 
 |       last_on_draw_scroll_offset_); | 
 |   hardware_enabled_ = true; | 
 |  | 
 |   external_draw_constraints_ = | 
 |       current_compositor_frame_consumer_->GetParentDrawConstraintsOnUI(); | 
 |  | 
 |   ReturnResourceFromParent(current_compositor_frame_consumer_); | 
 |   UpdateMemoryPolicy(); | 
 |  | 
 |   gfx::Transform transform_for_tile_priority = | 
 |       external_draw_constraints_.transform; | 
 |  | 
 |   gfx::Rect viewport_rect_for_tile_priority = | 
 |       ComputeViewportRectForTilePriority(); | 
 |  | 
 |   scoped_refptr<content::SynchronousCompositor::FrameFuture> future; // Async. | 
 |   content::SynchronousCompositor::Frame frame; // Sync. | 
 |   bool async = !sync_on_draw_hardware_ && allow_async_draw_; | 
 |   if (async) { | 
 |     future = compositor_->DemandDrawHwAsync( | 
 |         size_, viewport_rect_for_tile_priority, transform_for_tile_priority); | 
 |   } else { | 
 |     frame = compositor_->DemandDrawHw(size_, viewport_rect_for_tile_priority, | 
 |                                       transform_for_tile_priority); | 
 |   } | 
 |  | 
 |   if (!frame.frame && !future) { | 
 |     TRACE_EVENT_INSTANT0("android_webview", "NoNewFrame", | 
 |                          TRACE_EVENT_SCOPE_THREAD); | 
 |     return current_compositor_frame_consumer_->HasFrameOnUI(); | 
 |   } | 
 |  | 
 |   allow_async_draw_ = true; | 
 |   std::unique_ptr<ChildFrame> child_frame = std::make_unique<ChildFrame>( | 
 |       std::move(future), frame.layer_tree_frame_sink_id, std::move(frame.frame), | 
 |       compositor_id_, viewport_rect_for_tile_priority.IsEmpty(), | 
 |       transform_for_tile_priority, offscreen_pre_raster_, | 
 |       external_draw_constraints_.is_layer); | 
 |  | 
 |   ReturnUnusedResource( | 
 |       current_compositor_frame_consumer_->SetFrameOnUI(std::move(child_frame))); | 
 |   return true; | 
 | } | 
 |  | 
 | gfx::Rect BrowserViewRenderer::ComputeViewportRectForTilePriority() { | 
 |   // If the WebView is on a layer, WebView does not know what transform is | 
 |   // applied onto the layer so global visible rect does not make sense here. | 
 |   // In this case, just use the surface rect for tiling. | 
 |   // Leave viewport_rect_for_tile_priority empty if offscreen_pre_raster_ is on. | 
 |   gfx::Rect viewport_rect_for_tile_priority; | 
 |  | 
 |   if (!offscreen_pre_raster_ && !external_draw_constraints_.is_layer) { | 
 |     viewport_rect_for_tile_priority = last_on_draw_global_visible_rect_; | 
 |   } | 
 |   return viewport_rect_for_tile_priority; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::OnParentDrawConstraintsUpdated( | 
 |     CompositorFrameConsumer* compositor_frame_consumer) { | 
 |   DCHECK(compositor_frame_consumer); | 
 |   if (compositor_frame_consumer != current_compositor_frame_consumer_) | 
 |     return; | 
 |   PostInvalidate(compositor_); | 
 |   external_draw_constraints_ = | 
 |       current_compositor_frame_consumer_->GetParentDrawConstraintsOnUI(); | 
 |   UpdateMemoryPolicy(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::RemoveCompositorFrameConsumer( | 
 |     CompositorFrameConsumer* compositor_frame_consumer) { | 
 |   DCHECK(compositor_frame_consumers_.count(compositor_frame_consumer)); | 
 |   compositor_frame_consumers_.erase(compositor_frame_consumer); | 
 |   if (current_compositor_frame_consumer_ == compositor_frame_consumer) { | 
 |     SetCurrentCompositorFrameConsumer(nullptr); | 
 |   } | 
 |  | 
 |   // At this point the compositor frame consumer has to hand back all resources | 
 |   // to the child compositor. | 
 |   compositor_frame_consumer->DeleteHardwareRendererOnUI(); | 
 |   ReturnUncommittedFrames( | 
 |       compositor_frame_consumer->PassUncommittedFrameOnUI()); | 
 |   ReturnResourceFromParent(compositor_frame_consumer); | 
 |   compositor_frame_consumer->SetCompositorFrameProducer(nullptr); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ReturnUncommittedFrames( | 
 |     ChildFrameQueue child_frames) { | 
 |   for (auto& child_frame : child_frames) | 
 |     ReturnUnusedResource(std::move(child_frame)); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ReturnUnusedResource( | 
 |     std::unique_ptr<ChildFrame> child_frame) { | 
 |   if (!child_frame.get() || !child_frame->frame.get()) | 
 |     return; | 
 |  | 
 |   std::vector<viz::ReturnedResource> resources = | 
 |       viz::TransferableResource::ReturnResources( | 
 |           child_frame->frame->resource_list); | 
 |   content::SynchronousCompositor* compositor = | 
 |       FindCompositor(child_frame->compositor_id); | 
 |   if (compositor && !resources.empty()) | 
 |     compositor->ReturnResources(child_frame->layer_tree_frame_sink_id, | 
 |                                 std::move(resources)); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ReturnResourceFromParent( | 
 |     CompositorFrameConsumer* compositor_frame_consumer) { | 
 |   CompositorFrameConsumer::ReturnedResourcesMap returned_resource_map; | 
 |   compositor_frame_consumer->SwapReturnedResourcesOnUI(&returned_resource_map); | 
 |   for (auto& pair : returned_resource_map) { | 
 |     CompositorID compositor_id = pair.first; | 
 |     content::SynchronousCompositor* compositor = FindCompositor(compositor_id); | 
 |     std::vector<viz::ReturnedResource> resources; | 
 |     resources.swap(pair.second.resources); | 
 |  | 
 |     if (compositor && !resources.empty()) { | 
 |       compositor->ReturnResources(pair.second.layer_tree_frame_sink_id, | 
 |                                   resources); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::OnDrawSoftware(SkCanvas* canvas) { | 
 |   return CanOnDraw() && CompositeSW(canvas); | 
 | } | 
 |  | 
 | sk_sp<SkPicture> BrowserViewRenderer::CapturePicture(int width, | 
 |                                                      int height) { | 
 |   TRACE_EVENT0("android_webview", "BrowserViewRenderer::CapturePicture"); | 
 |  | 
 |   // Return empty Picture objects for empty SkPictures. | 
 |   if (width <= 0 || height <= 0) { | 
 |     SkPictureRecorder emptyRecorder; | 
 |     emptyRecorder.beginRecording(0, 0); | 
 |     return emptyRecorder.finishRecordingAsPicture(); | 
 |   } | 
 |  | 
 |   SkPictureRecorder recorder; | 
 |   SkCanvas* rec_canvas = recorder.beginRecording(width, height, NULL, 0); | 
 |   if (compositor_) { | 
 |     { | 
 |       // Reset scroll back to the origin, will go back to the old | 
 |       // value when scroll_reset is out of scope. | 
 |       base::AutoReset<gfx::Vector2dF> scroll_reset(&scroll_offset_dip_, | 
 |                                                    gfx::Vector2dF()); | 
 |       compositor_->DidChangeRootLayerScrollOffset( | 
 |           gfx::ScrollOffset(scroll_offset_dip_)); | 
 |       CompositeSW(rec_canvas); | 
 |     } | 
 |     compositor_->DidChangeRootLayerScrollOffset( | 
 |         gfx::ScrollOffset(scroll_offset_dip_)); | 
 |   } | 
 |   return recorder.finishRecordingAsPicture(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::EnableOnNewPicture(bool enabled) { | 
 |   on_new_picture_enable_ = enabled; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ClearView() { | 
 |   TRACE_EVENT_INSTANT0("android_webview", | 
 |                        "BrowserViewRenderer::ClearView", | 
 |                        TRACE_EVENT_SCOPE_THREAD); | 
 |   if (clear_view_) | 
 |     return; | 
 |  | 
 |   clear_view_ = true; | 
 |   // Always invalidate ignoring the compositor to actually clear the webview. | 
 |   PostInvalidate(compositor_); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetOffscreenPreRaster(bool enable) { | 
 |   if (offscreen_pre_raster_ != enable) { | 
 |     offscreen_pre_raster_ = enable; | 
 |     UpdateMemoryPolicy(); | 
 |   } | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetIsPaused(bool paused) { | 
 |   TRACE_EVENT_INSTANT1("android_webview", | 
 |                        "BrowserViewRenderer::SetIsPaused", | 
 |                        TRACE_EVENT_SCOPE_THREAD, | 
 |                        "paused", | 
 |                        paused); | 
 |   is_paused_ = paused; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetViewVisibility(bool view_visible) { | 
 |   TRACE_EVENT_INSTANT1("android_webview", | 
 |                        "BrowserViewRenderer::SetViewVisibility", | 
 |                        TRACE_EVENT_SCOPE_THREAD, | 
 |                        "view_visible", | 
 |                        view_visible); | 
 |   view_visible_ = view_visible; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetWindowVisibility(bool window_visible) { | 
 |   TRACE_EVENT_INSTANT1("android_webview", | 
 |                        "BrowserViewRenderer::SetWindowVisibility", | 
 |                        TRACE_EVENT_SCOPE_THREAD, | 
 |                        "window_visible", | 
 |                        window_visible); | 
 |   window_visible_ = window_visible; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::OnSizeChanged(int width, int height) { | 
 |   TRACE_EVENT_INSTANT2("android_webview", | 
 |                        "BrowserViewRenderer::OnSizeChanged", | 
 |                        TRACE_EVENT_SCOPE_THREAD, | 
 |                        "width", | 
 |                        width, | 
 |                        "height", | 
 |                        height); | 
 |   size_.SetSize(width, height); | 
 |   if (offscreen_pre_raster_) | 
 |     UpdateMemoryPolicy(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::OnAttachedToWindow(int width, int height) { | 
 |   TRACE_EVENT2("android_webview", | 
 |                "BrowserViewRenderer::OnAttachedToWindow", | 
 |                "width", | 
 |                width, | 
 |                "height", | 
 |                height); | 
 |   attached_to_window_ = true; | 
 |   was_attached_ = true; | 
 |  | 
 |   size_.SetSize(width, height); | 
 |   if (offscreen_pre_raster_) | 
 |     UpdateMemoryPolicy(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::OnDetachedFromWindow() { | 
 |   TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDetachedFromWindow"); | 
 |   attached_to_window_ = false; | 
 |   ReleaseHardware(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ZoomBy(float delta) { | 
 |   if (!compositor_) | 
 |     return; | 
 |   compositor_->SynchronouslyZoomBy( | 
 |       delta, gfx::Point(size_.width() / 2, size_.height() / 2)); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::OnComputeScroll(base::TimeTicks animation_time) { | 
 |   if (!compositor_) | 
 |     return; | 
 |   TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnComputeScroll"); | 
 |   compositor_->OnComputeScroll(animation_time); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ReleaseHardware() { | 
 |   for (auto* compositor_frame_consumer : compositor_frame_consumers_) { | 
 |     ReturnUncommittedFrames( | 
 |         compositor_frame_consumer->PassUncommittedFrameOnUI()); | 
 |     ReturnResourceFromParent(compositor_frame_consumer); | 
 |     DCHECK(compositor_frame_consumer->ReturnedResourcesEmptyOnUI()); | 
 |   } | 
 |   hardware_enabled_ = false; | 
 |   UpdateMemoryPolicy(); | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::IsVisible() const { | 
 |   // Ignore |window_visible_| if |attached_to_window_| is false. | 
 |   return view_visible_ && (!attached_to_window_ || window_visible_); | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::IsClientVisible() const { | 
 |   // When WebView is not paused, we declare it visible even before it is | 
 |   // attached to window to allow for background operations. If it ever gets | 
 |   // attached though, the WebView is visible as long as it is attached | 
 |   // to a window and the window is visible. | 
 |   return is_paused_ | 
 |              ? false | 
 |              : !was_attached_ || (attached_to_window_ && window_visible_); | 
 | } | 
 |  | 
 | gfx::Rect BrowserViewRenderer::GetScreenRect() const { | 
 |   return gfx::Rect(client_->GetLocationOnScreen(), size_); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::DidInitializeCompositor( | 
 |     content::SynchronousCompositor* compositor, | 
 |     int process_id, | 
 |     int routing_id) { | 
 |   TRACE_EVENT_INSTANT0("android_webview", | 
 |                        "BrowserViewRenderer::DidInitializeCompositor", | 
 |                        TRACE_EVENT_SCOPE_THREAD); | 
 |   DCHECK(compositor); | 
 |   CompositorID compositor_id(process_id, routing_id); | 
 |   // This assumes that a RenderViewHost has at most 1 synchronous compositor | 
 |   // througout its lifetime. | 
 |   DCHECK(compositor_map_.count(compositor_id) == 0); | 
 |   compositor_map_[compositor_id] = compositor; | 
 |  | 
 |   // At this point, the RVHChanged event for the new RVH that contains the | 
 |   // |compositor| might have been fired already, in which case just set the | 
 |   // current compositor with the new compositor. | 
 |   if (!compositor_ && compositor_id.Equals(compositor_id_)) | 
 |     compositor_ = compositor; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::DidDestroyCompositor( | 
 |     content::SynchronousCompositor* compositor, | 
 |     int process_id, | 
 |     int routing_id) { | 
 |   TRACE_EVENT_INSTANT0("android_webview", | 
 |                        "BrowserViewRenderer::DidDestroyCompositor", | 
 |                        TRACE_EVENT_SCOPE_THREAD); | 
 |   CompositorID compositor_id(process_id, routing_id); | 
 |   DCHECK(compositor_map_.count(compositor_id)); | 
 |   if (compositor_ == compositor) { | 
 |     compositor_ = nullptr; | 
 |   } | 
 |  | 
 |   compositor_map_.erase(compositor_id); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetActiveCompositorID( | 
 |     const CompositorID& compositor_id) { | 
 |   // Set the old compositor memory policy to 0. | 
 |   if (!compositor_id_.Equals(compositor_id) && compositor_) | 
 |     compositor_->SetMemoryPolicy(0u); | 
 |  | 
 |   if (content::SynchronousCompositor* compositor = | 
 |           FindCompositor(compositor_id)) { | 
 |     compositor_ = compositor; | 
 |     UpdateMemoryPolicy(); | 
 |     compositor_->DidChangeRootLayerScrollOffset( | 
 |         gfx::ScrollOffset(scroll_offset_dip_)); | 
 |   } else { | 
 |     compositor_ = nullptr; | 
 |   } | 
 |   compositor_id_ = compositor_id; | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetDipScale(float dip_scale) { | 
 |   dip_scale_ = dip_scale; | 
 |   CHECK_GT(dip_scale_, 0.f); | 
 | } | 
 |  | 
 | gfx::Vector2d BrowserViewRenderer::max_scroll_offset() const { | 
 |   DCHECK_GT(dip_scale_, 0.f); | 
 |   return gfx::ToCeiledVector2d(gfx::ScaleVector2d( | 
 |       max_scroll_offset_dip_, dip_scale_ * page_scale_factor_)); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::ScrollTo(const gfx::Vector2d& scroll_offset) { | 
 |   gfx::Vector2d max_offset = max_scroll_offset(); | 
 |   gfx::Vector2dF scroll_offset_dip; | 
 |   // To preserve the invariant that scrolling to the maximum physical pixel | 
 |   // value also scrolls to the maximum dip pixel value we transform the physical | 
 |   // offset into the dip offset by using a proportion (instead of dividing by | 
 |   // dip_scale * page_scale_factor). | 
 |   if (max_offset.x()) { | 
 |     scroll_offset_dip.set_x((scroll_offset.x() * max_scroll_offset_dip_.x()) / | 
 |                             max_offset.x()); | 
 |   } | 
 |   if (max_offset.y()) { | 
 |     scroll_offset_dip.set_y((scroll_offset.y() * max_scroll_offset_dip_.y()) / | 
 |                             max_offset.y()); | 
 |   } | 
 |  | 
 |   DCHECK_LE(0.f, scroll_offset_dip.x()); | 
 |   DCHECK_LE(0.f, scroll_offset_dip.y()); | 
 |   DCHECK(scroll_offset_dip.x() < max_scroll_offset_dip_.x() || | 
 |          scroll_offset_dip.x() - max_scroll_offset_dip_.x() < kEpsilon) | 
 |       << scroll_offset_dip.x() << " " << max_scroll_offset_dip_.x(); | 
 |   DCHECK(scroll_offset_dip.y() < max_scroll_offset_dip_.y() || | 
 |          scroll_offset_dip.y() - max_scroll_offset_dip_.y() < kEpsilon) | 
 |       << scroll_offset_dip.y() << " " << max_scroll_offset_dip_.y(); | 
 |  | 
 |   if (scroll_offset_dip_ == scroll_offset_dip) | 
 |     return; | 
 |  | 
 |   scroll_offset_dip_ = scroll_offset_dip; | 
 |  | 
 |   TRACE_EVENT_INSTANT2("android_webview", | 
 |                "BrowserViewRenderer::ScrollTo", | 
 |                TRACE_EVENT_SCOPE_THREAD, | 
 |                "x", | 
 |                scroll_offset_dip.x(), | 
 |                "y", | 
 |                scroll_offset_dip.y()); | 
 |  | 
 |   if (compositor_) { | 
 |     compositor_->DidChangeRootLayerScrollOffset( | 
 |         gfx::ScrollOffset(scroll_offset_dip_)); | 
 |   } | 
 | } | 
 |  | 
 | void BrowserViewRenderer::DidUpdateContent( | 
 |     content::SynchronousCompositor* compositor) { | 
 |   TRACE_EVENT_INSTANT0("android_webview", | 
 |                        "BrowserViewRenderer::DidUpdateContent", | 
 |                        TRACE_EVENT_SCOPE_THREAD); | 
 |   if (compositor != compositor_) | 
 |     return; | 
 |  | 
 |   clear_view_ = false; | 
 |   if (on_new_picture_enable_) | 
 |     client_->OnNewPicture(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::SetTotalRootLayerScrollOffset( | 
 |     const gfx::Vector2dF& scroll_offset_dip) { | 
 |   if (scroll_offset_dip_ == scroll_offset_dip) | 
 |     return; | 
 |   scroll_offset_dip_ = scroll_offset_dip; | 
 |  | 
 |   gfx::Vector2d max_offset = max_scroll_offset(); | 
 |   gfx::Vector2d scroll_offset; | 
 |   // For an explanation as to why this is done this way see the comment in | 
 |   // BrowserViewRenderer::ScrollTo. | 
 |   if (max_scroll_offset_dip_.x()) { | 
 |     scroll_offset.set_x((scroll_offset_dip.x() * max_offset.x()) / | 
 |                         max_scroll_offset_dip_.x()); | 
 |   } | 
 |  | 
 |   if (max_scroll_offset_dip_.y()) { | 
 |     scroll_offset.set_y((scroll_offset_dip.y() * max_offset.y()) / | 
 |                         max_scroll_offset_dip_.y()); | 
 |   } | 
 |  | 
 |   DCHECK_LE(0, scroll_offset.x()); | 
 |   DCHECK_LE(0, scroll_offset.y()); | 
 |   DCHECK_LE(scroll_offset.x(), max_offset.x()); | 
 |   DCHECK_LE(scroll_offset.y(), max_offset.y()); | 
 |  | 
 |   client_->ScrollContainerViewTo(scroll_offset); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::UpdateRootLayerState( | 
 |     content::SynchronousCompositor* compositor, | 
 |     const gfx::Vector2dF& total_scroll_offset, | 
 |     const gfx::Vector2dF& total_max_scroll_offset, | 
 |     const gfx::SizeF& scrollable_size, | 
 |     float page_scale_factor, | 
 |     float min_page_scale_factor, | 
 |     float max_page_scale_factor) { | 
 |   if (compositor != compositor_) | 
 |     return; | 
 |  | 
 |   gfx::Vector2dF total_scroll_offset_dip = total_scroll_offset; | 
 |   gfx::Vector2dF max_scroll_offset_dip = total_max_scroll_offset; | 
 |   gfx::SizeF scrollable_size_dip = scrollable_size; | 
 |   if (content::UseZoomForDSFEnabled()) { | 
 |     total_scroll_offset_dip.Scale(1 / dip_scale_); | 
 |     max_scroll_offset_dip.Scale(1 / dip_scale_); | 
 |     scrollable_size_dip.Scale(1 / dip_scale_); | 
 |   } | 
 |  | 
 |   TRACE_EVENT_INSTANT1( | 
 |       "android_webview", | 
 |       "BrowserViewRenderer::UpdateRootLayerState", | 
 |       TRACE_EVENT_SCOPE_THREAD, | 
 |       "state", | 
 |       RootLayerStateAsValue(total_scroll_offset_dip, scrollable_size_dip)); | 
 |  | 
 |   DCHECK_GE(max_scroll_offset_dip.x(), 0.f); | 
 |   DCHECK_GE(max_scroll_offset_dip.y(), 0.f); | 
 |   DCHECK_GT(page_scale_factor, 0.f); | 
 |   // SetDipScale should have been called at least once before this is called. | 
 |   DCHECK_GT(dip_scale_, 0.f); | 
 |  | 
 |   if (max_scroll_offset_dip_ != max_scroll_offset_dip || | 
 |       scrollable_size_dip_ != scrollable_size_dip || | 
 |       page_scale_factor_ != page_scale_factor || | 
 |       min_page_scale_factor_ != min_page_scale_factor || | 
 |       max_page_scale_factor_ != max_page_scale_factor) { | 
 |     max_scroll_offset_dip_ = max_scroll_offset_dip; | 
 |     scrollable_size_dip_ = scrollable_size_dip; | 
 |     page_scale_factor_ = page_scale_factor; | 
 |     min_page_scale_factor_ = min_page_scale_factor; | 
 |     max_page_scale_factor_ = max_page_scale_factor; | 
 |  | 
 |     client_->UpdateScrollState(max_scroll_offset(), scrollable_size_dip, | 
 |                                page_scale_factor, min_page_scale_factor, | 
 |                                max_page_scale_factor); | 
 |   } | 
 |   SetTotalRootLayerScrollOffset(total_scroll_offset_dip); | 
 | } | 
 |  | 
 | std::unique_ptr<base::trace_event::ConvertableToTraceFormat> | 
 | BrowserViewRenderer::RootLayerStateAsValue( | 
 |     const gfx::Vector2dF& total_scroll_offset_dip, | 
 |     const gfx::SizeF& scrollable_size_dip) { | 
 |   std::unique_ptr<base::trace_event::TracedValue> state( | 
 |       new base::trace_event::TracedValue()); | 
 |  | 
 |   state->SetDouble("total_scroll_offset_dip.x", total_scroll_offset_dip.x()); | 
 |   state->SetDouble("total_scroll_offset_dip.y", total_scroll_offset_dip.y()); | 
 |  | 
 |   state->SetDouble("max_scroll_offset_dip.x", max_scroll_offset_dip_.x()); | 
 |   state->SetDouble("max_scroll_offset_dip.y", max_scroll_offset_dip_.y()); | 
 |  | 
 |   state->SetDouble("scrollable_size_dip.width", scrollable_size_dip.width()); | 
 |   state->SetDouble("scrollable_size_dip.height", scrollable_size_dip.height()); | 
 |  | 
 |   state->SetDouble("page_scale_factor", page_scale_factor_); | 
 |   return std::move(state); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::DidOverscroll( | 
 |     content::SynchronousCompositor* compositor, | 
 |     const gfx::Vector2dF& accumulated_overscroll, | 
 |     const gfx::Vector2dF& latest_overscroll_delta, | 
 |     const gfx::Vector2dF& current_fling_velocity) { | 
 |   if (compositor != compositor_) | 
 |     return; | 
 |  | 
 |   const float physical_pixel_scale = dip_scale_ * page_scale_factor_; | 
 |   if (accumulated_overscroll == latest_overscroll_delta) | 
 |     overscroll_rounding_error_ = gfx::Vector2dF(); | 
 |   gfx::Vector2dF scaled_overscroll_delta = | 
 |       gfx::ScaleVector2d(latest_overscroll_delta, physical_pixel_scale); | 
 |   gfx::Vector2d rounded_overscroll_delta = gfx::ToRoundedVector2d( | 
 |       scaled_overscroll_delta + overscroll_rounding_error_); | 
 |   overscroll_rounding_error_ = | 
 |       scaled_overscroll_delta - rounded_overscroll_delta; | 
 |   gfx::Vector2dF fling_velocity_pixels = | 
 |       gfx::ScaleVector2d(current_fling_velocity, physical_pixel_scale); | 
 |  | 
 |   client_->DidOverscroll(rounded_overscroll_delta, fling_velocity_pixels); | 
 | } | 
 |  | 
 | ui::TouchHandleDrawable* BrowserViewRenderer::CreateDrawable() { | 
 |   return client_->CreateDrawable(); | 
 | } | 
 |  | 
 | void BrowserViewRenderer::PostInvalidate( | 
 |     content::SynchronousCompositor* compositor) { | 
 |   TRACE_EVENT_INSTANT0("android_webview", "BrowserViewRenderer::PostInvalidate", | 
 |                        TRACE_EVENT_SCOPE_THREAD); | 
 |   if (compositor != compositor_) | 
 |     return; | 
 |  | 
 |   client_->PostInvalidate(); | 
 | } | 
 |  | 
 | bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) { | 
 |   DCHECK(compositor_); | 
 |   return compositor_->DemandDrawSw(canvas); | 
 | } | 
 |  | 
 | std::string BrowserViewRenderer::ToString() const { | 
 |   std::string str; | 
 |   base::StringAppendF(&str, "is_paused: %d ", is_paused_); | 
 |   base::StringAppendF(&str, "view_visible: %d ", view_visible_); | 
 |   base::StringAppendF(&str, "window_visible: %d ", window_visible_); | 
 |   base::StringAppendF(&str, "dip_scale: %f ", dip_scale_); | 
 |   base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_); | 
 |   base::StringAppendF(&str, "view size: %s ", size_.ToString().c_str()); | 
 |   base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_); | 
 |   base::StringAppendF(&str, | 
 |                       "global visible rect: %s ", | 
 |                       last_on_draw_global_visible_rect_.ToString().c_str()); | 
 |   base::StringAppendF( | 
 |       &str, "scroll_offset_dip: %s ", scroll_offset_dip_.ToString().c_str()); | 
 |   base::StringAppendF(&str, | 
 |                       "overscroll_rounding_error_: %s ", | 
 |                       overscroll_rounding_error_.ToString().c_str()); | 
 |   base::StringAppendF( | 
 |       &str, "on_new_picture_enable: %d ", on_new_picture_enable_); | 
 |   base::StringAppendF(&str, "clear_view: %d ", clear_view_); | 
 |   return str; | 
 | } | 
 |  | 
 | }  // namespace android_webview |