| // Copyright 2015 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 "content/browser/android/synchronous_compositor_host.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/containers/hash_tables.h" | 
 | #include "base/memory/shared_memory.h" | 
 | #include "base/trace_event/trace_event_argument.h" | 
 | #include "cc/output/compositor_frame_ack.h" | 
 | #include "content/browser/renderer_host/render_widget_host_view_android.h" | 
 | #include "content/browser/web_contents/web_contents_impl.h" | 
 | #include "content/common/android/sync_compositor_messages.h" | 
 | #include "content/public/browser/android/synchronous_compositor_client.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/render_view_host.h" | 
 | #include "ipc/ipc_sender.h" | 
 | #include "third_party/skia/include/core/SkBitmap.h" | 
 | #include "third_party/skia/include/core/SkCanvas.h" | 
 | #include "third_party/skia/include/core/SkImageInfo.h" | 
 | #include "third_party/skia/include/core/SkRect.h" | 
 | #include "ui/gfx/skia_util.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | SynchronousCompositorHost::SynchronousCompositorHost( | 
 |     RenderWidgetHostViewAndroid* rwhva, | 
 |     SynchronousCompositorClient* client) | 
 |     : rwhva_(rwhva), | 
 |       client_(client), | 
 |       ui_task_runner_( | 
 |           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), | 
 |       routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()), | 
 |       sender_(rwhva_->GetRenderWidgetHost()), | 
 |       is_active_(false), | 
 |       bytes_limit_(0u), | 
 |       root_scroll_offset_updated_by_browser_(false), | 
 |       renderer_param_version_(0u), | 
 |       need_animate_scroll_(false), | 
 |       need_invalidate_(false), | 
 |       need_begin_frame_(false), | 
 |       did_activate_pending_tree_(false), | 
 |       weak_ptr_factory_(this) { | 
 |   client_->DidInitializeCompositor(this); | 
 | } | 
 |  | 
 | SynchronousCompositorHost::~SynchronousCompositorHost() { | 
 |   client_->DidDestroyCompositor(this); | 
 |   if (weak_ptr_factory_.HasWeakPtrs()) | 
 |     UpdateStateTask(); | 
 | } | 
 |  | 
 | bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) { | 
 |   bool handled = true; | 
 |   IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorHost, message) | 
 |     IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_UpdateState, ProcessCommonParams) | 
 |     IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_OverScroll, OnOverScroll) | 
 |     IPC_MESSAGE_UNHANDLED(handled = false) | 
 |   IPC_END_MESSAGE_MAP() | 
 |   return handled; | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::DidBecomeCurrent() { | 
 |   client_->DidBecomeCurrent(this); | 
 | } | 
 |  | 
 | scoped_ptr<cc::CompositorFrame> SynchronousCompositorHost::DemandDrawHw( | 
 |     const gfx::Size& surface_size, | 
 |     const gfx::Transform& transform, | 
 |     const gfx::Rect& viewport, | 
 |     const gfx::Rect& clip, | 
 |     const gfx::Rect& viewport_rect_for_tile_priority, | 
 |     const gfx::Transform& transform_for_tile_priority) { | 
 |   SyncCompositorDemandDrawHwParams params(surface_size, transform, viewport, | 
 |                                           clip, viewport_rect_for_tile_priority, | 
 |                                           transform_for_tile_priority); | 
 |   scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   if (!sender_->Send(new SyncCompositorMsg_DemandDrawHw( | 
 |           routing_id_, common_browser_params, params, &common_renderer_params, | 
 |           frame.get()))) { | 
 |     return nullptr; | 
 |   } | 
 |   ProcessCommonParams(common_renderer_params); | 
 |   if (!frame->delegated_frame_data) { | 
 |     // This can happen if compositor did not swap in this draw. | 
 |     frame.reset(); | 
 |   } | 
 |   if (frame) | 
 |     UpdateFrameMetaData(frame->metadata); | 
 |   return frame; | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::UpdateFrameMetaData( | 
 |     const cc::CompositorFrameMetadata& frame_metadata) { | 
 |   rwhva_->SynchronousFrameMetadata(frame_metadata); | 
 | } | 
 |  | 
 | class SynchronousCompositorHost::ScopedSendZeroMemory { | 
 |  public: | 
 |   ScopedSendZeroMemory(SynchronousCompositorHost* host) : host_(host) {} | 
 |   ~ScopedSendZeroMemory() { host_->SendZeroMemory(); } | 
 |  | 
 |  private: | 
 |   SynchronousCompositorHost* const host_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(ScopedSendZeroMemory); | 
 | }; | 
 |  | 
 | struct SynchronousCompositorHost::SharedMemoryWithSize { | 
 |   base::SharedMemory shm; | 
 |   const size_t stride; | 
 |   const size_t buffer_size; | 
 |  | 
 |   SharedMemoryWithSize(size_t stride, size_t buffer_size) | 
 |       : stride(stride), buffer_size(buffer_size) {} | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(SharedMemoryWithSize); | 
 | }; | 
 |  | 
 | bool SynchronousCompositorHost::DemandDrawSw(SkCanvas* canvas) { | 
 |   SyncCompositorDemandDrawSwParams params; | 
 |   params.size = gfx::Size(canvas->getBaseLayerSize().width(), | 
 |                           canvas->getBaseLayerSize().height()); | 
 |   SkIRect canvas_clip; | 
 |   canvas->getClipDeviceBounds(&canvas_clip); | 
 |   params.clip = gfx::SkIRectToRect(canvas_clip); | 
 |   params.transform.matrix() = canvas->getTotalMatrix(); | 
 |   if (params.size.IsEmpty()) | 
 |     return true; | 
 |  | 
 |   SkImageInfo info = | 
 |       SkImageInfo::MakeN32Premul(params.size.width(), params.size.height()); | 
 |   DCHECK_EQ(kRGBA_8888_SkColorType, info.colorType()); | 
 |   size_t stride = info.minRowBytes(); | 
 |   size_t buffer_size = info.getSafeSize(stride); | 
 |   if (!buffer_size) | 
 |     return false;  // Overflow. | 
 |  | 
 |   SetSoftwareDrawSharedMemoryIfNeeded(stride, buffer_size); | 
 |   if (!software_draw_shm_) | 
 |     return false; | 
 |  | 
 |   scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   bool success = false; | 
 |   if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( | 
 |           routing_id_, common_browser_params, params, &success, | 
 |           &common_renderer_params, frame.get()))) { | 
 |     return false; | 
 |   } | 
 |   ScopedSendZeroMemory send_zero_memory(this); | 
 |   if (!success) | 
 |     return false; | 
 |  | 
 |   ProcessCommonParams(common_renderer_params); | 
 |   UpdateFrameMetaData(frame->metadata); | 
 |  | 
 |   SkBitmap bitmap; | 
 |   if (!bitmap.installPixels(info, software_draw_shm_->shm.memory(), stride)) | 
 |     return false; | 
 |  | 
 |   { | 
 |     TRACE_EVENT0("browser", "DrawBitmap"); | 
 |     canvas->save(); | 
 |     canvas->resetMatrix(); | 
 |     canvas->drawBitmap(bitmap, 0, 0); | 
 |     canvas->restore(); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::SetSoftwareDrawSharedMemoryIfNeeded( | 
 |     size_t stride, | 
 |     size_t buffer_size) { | 
 |   if (software_draw_shm_ && software_draw_shm_->stride == stride && | 
 |       software_draw_shm_->buffer_size == buffer_size) | 
 |     return; | 
 |   software_draw_shm_.reset(); | 
 |   scoped_ptr<SharedMemoryWithSize> software_draw_shm( | 
 |       new SharedMemoryWithSize(stride, buffer_size)); | 
 |   { | 
 |     TRACE_EVENT1("browser", "AllocateSharedMemory", "buffer_size", buffer_size); | 
 |     if (!software_draw_shm->shm.CreateAndMapAnonymous(buffer_size)) | 
 |       return; | 
 |   } | 
 |  | 
 |   SyncCompositorSetSharedMemoryParams set_shm_params; | 
 |   set_shm_params.buffer_size = buffer_size; | 
 |   base::ProcessHandle renderer_process_handle = | 
 |       rwhva_->GetRenderWidgetHost()->GetProcess()->GetHandle(); | 
 |   if (!software_draw_shm->shm.ShareToProcess(renderer_process_handle, | 
 |                                              &set_shm_params.shm_handle)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   bool success = false; | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   if (!sender_->Send(new SyncCompositorMsg_SetSharedMemory( | 
 |           routing_id_, common_browser_params, set_shm_params, &success, | 
 |           &common_renderer_params)) || | 
 |       !success) { | 
 |     return; | 
 |   } | 
 |   software_draw_shm_ = std::move(software_draw_shm); | 
 |   ProcessCommonParams(common_renderer_params); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::SendZeroMemory() { | 
 |   // No need to check return value. | 
 |   sender_->Send(new SyncCompositorMsg_ZeroSharedMemory(routing_id_)); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::ReturnResources( | 
 |     const cc::CompositorFrameAck& frame_ack) { | 
 |   returned_resources_.insert(returned_resources_.end(), | 
 |                              frame_ack.resources.begin(), | 
 |                              frame_ack.resources.end()); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::SetMemoryPolicy(size_t bytes_limit) { | 
 |   if (bytes_limit_ == bytes_limit) | 
 |     return; | 
 |   bytes_limit_ = bytes_limit; | 
 |   SendAsyncCompositorStateIfNeeded(); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::DidChangeRootLayerScrollOffset( | 
 |     const gfx::ScrollOffset& root_offset) { | 
 |   if (root_scroll_offset_ == root_offset) | 
 |     return; | 
 |   root_scroll_offset_updated_by_browser_ = true; | 
 |   root_scroll_offset_ = root_offset; | 
 |   SendAsyncCompositorStateIfNeeded(); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::SendAsyncCompositorStateIfNeeded() { | 
 |   if (weak_ptr_factory_.HasWeakPtrs()) | 
 |     return; | 
 |  | 
 |   ui_task_runner_->PostTask( | 
 |       FROM_HERE, base::Bind(&SynchronousCompositorHost::UpdateStateTask, | 
 |                             weak_ptr_factory_.GetWeakPtr())); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::UpdateStateTask() { | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   sender_->Send( | 
 |       new SyncCompositorMsg_UpdateState(routing_id_, common_browser_params)); | 
 |   DCHECK(!weak_ptr_factory_.HasWeakPtrs()); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::SetIsActive(bool is_active) { | 
 |   if (is_active_ == is_active) | 
 |     return; | 
 |   is_active_ = is_active; | 
 |   UpdateNeedsBeginFrames(); | 
 |   SendAsyncCompositorStateIfNeeded(); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::OnComputeScroll( | 
 |     base::TimeTicks animation_time) { | 
 |   if (!need_animate_scroll_) | 
 |     return; | 
 |   need_animate_scroll_ = false; | 
 |  | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   sender_->Send(new SyncCompositorMsg_ComputeScroll( | 
 |       routing_id_, common_browser_params, animation_time)); | 
 | } | 
 |  | 
 | InputEventAckState SynchronousCompositorHost::HandleInputEvent( | 
 |     const blink::WebInputEvent& input_event) { | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   InputEventAckState ack = INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | 
 |   if (!sender_->Send(new SyncCompositorMsg_HandleInputEvent( | 
 |           routing_id_, common_browser_params, &input_event, | 
 |           &common_renderer_params, &ack))) { | 
 |     return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | 
 |   } | 
 |   ProcessCommonParams(common_renderer_params); | 
 |   return ack; | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::BeginFrame(const cc::BeginFrameArgs& args) { | 
 |   if (!is_active_ || !need_begin_frame_) | 
 |     return; | 
 |  | 
 |   SyncCompositorCommonBrowserParams common_browser_params; | 
 |   PopulateCommonParams(&common_browser_params); | 
 |   SyncCompositorCommonRendererParams common_renderer_params; | 
 |   if (!sender_->Send( | 
 |           new SyncCompositorMsg_BeginFrame(routing_id_, common_browser_params, | 
 |                                            args, &common_renderer_params))) { | 
 |     return; | 
 |   } | 
 |   ProcessCommonParams(common_renderer_params); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::OnOverScroll( | 
 |     const SyncCompositorCommonRendererParams& params, | 
 |     const DidOverscrollParams& over_scroll_params) { | 
 |   ProcessCommonParams(params); | 
 |   client_->DidOverscroll(over_scroll_params.accumulated_overscroll, | 
 |                          over_scroll_params.latest_overscroll_delta, | 
 |                          over_scroll_params.current_fling_velocity); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::PopulateCommonParams( | 
 |     SyncCompositorCommonBrowserParams* params) { | 
 |   DCHECK(params); | 
 |   DCHECK(params->ack.resources.empty()); | 
 |   params->bytes_limit = bytes_limit_; | 
 |   params->ack.resources.swap(returned_resources_); | 
 |   if (root_scroll_offset_updated_by_browser_) { | 
 |     params->root_scroll_offset = root_scroll_offset_; | 
 |     params->update_root_scroll_offset = root_scroll_offset_updated_by_browser_; | 
 |     root_scroll_offset_updated_by_browser_ = false; | 
 |   } | 
 |   params->begin_frame_source_paused = !is_active_; | 
 |  | 
 |   weak_ptr_factory_.InvalidateWeakPtrs(); | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::ProcessCommonParams( | 
 |     const SyncCompositorCommonRendererParams& params) { | 
 |   // Ignore if |renderer_param_version_| is newer than |params.version|. This | 
 |   // comparison takes into account when the unsigned int wraps. | 
 |   if ((renderer_param_version_ - params.version) < 0x80000000) { | 
 |     return; | 
 |   } | 
 |   renderer_param_version_ = params.version; | 
 |   need_animate_scroll_ = params.need_animate_scroll; | 
 |   if (need_begin_frame_ != params.need_begin_frame) { | 
 |     need_begin_frame_ = params.need_begin_frame; | 
 |     UpdateNeedsBeginFrames(); | 
 |   } | 
 |   need_invalidate_ = need_invalidate_ || params.need_invalidate; | 
 |   did_activate_pending_tree_ = | 
 |       did_activate_pending_tree_ || params.did_activate_pending_tree; | 
 |   root_scroll_offset_ = params.total_scroll_offset; | 
 |  | 
 |   if (need_invalidate_) { | 
 |     need_invalidate_ = false; | 
 |     client_->PostInvalidate(); | 
 |   } | 
 |  | 
 |   if (did_activate_pending_tree_) { | 
 |     did_activate_pending_tree_ = false; | 
 |     client_->DidUpdateContent(); | 
 |   } | 
 |  | 
 |   // Ensure only valid values from compositor are sent to client. | 
 |   // Compositor has page_scale_factor set to 0 before initialization, so check | 
 |   // for that case here. | 
 |   if (params.page_scale_factor) { | 
 |     client_->UpdateRootLayerState( | 
 |         gfx::ScrollOffsetToVector2dF(params.total_scroll_offset), | 
 |         gfx::ScrollOffsetToVector2dF(params.max_scroll_offset), | 
 |         params.scrollable_size, params.page_scale_factor, | 
 |         params.min_page_scale_factor, params.max_page_scale_factor); | 
 |   } | 
 | } | 
 |  | 
 | void SynchronousCompositorHost::UpdateNeedsBeginFrames() { | 
 |   rwhva_->OnSetNeedsBeginFrames(is_active_ && need_begin_frame_); | 
 | } | 
 |  | 
 | }  // namespace content |