blob: e3e7fb802deaafded7efbfedb3db00915beafda1 [file] [log] [blame]
// Copyright (c) 2012 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/renderer_host/render_widget_host_view_android.h"
#include <android/bitmap.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/worker_pool.h"
#include "cc/base/latency_info_swap_promise.h"
#include "cc/layers/delegated_frame_provider.h"
#include "cc/layers/delegated_renderer_layer.h"
#include "cc/layers/layer.h"
#include "cc/layers/texture_layer.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
#include "cc/resources/single_release_callback.h"
#include "cc/trees/layer_tree_host.h"
#include "content/browser/accessibility/browser_accessibility_manager_android.h"
#include "content/browser/android/content_view_core_impl.h"
#include "content/browser/android/in_process/synchronous_compositor_impl.h"
#include "content/browser/android/overscroll_glow.h"
#include "content/browser/devtools/render_view_devtools_agent_host.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.h"
#include "content/browser/gpu/gpu_surface_tracker.h"
#include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/browser/renderer_host/dip_util.h"
#include "content/browser/renderer_host/image_transport_factory_android.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target_android.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/common/gpu/client/gl_helper.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/common/content_switches.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "skia/ext/image_operations.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/base/android/window_android.h"
#include "ui/gfx/android/device_display_info.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
namespace content {
namespace {
const int kUndefinedOutputSurfaceId = -1;
void InsertSyncPointAndAckForCompositor(
int renderer_host_id,
uint32 output_surface_id,
int route_id,
const gpu::Mailbox& return_mailbox,
const gfx::Size return_size) {
cc::CompositorFrameAck ack;
ack.gl_frame_data.reset(new cc::GLFrameData());
if (!return_mailbox.IsZero()) {
ack.gl_frame_data->mailbox = return_mailbox;
ack.gl_frame_data->size = return_size;
ack.gl_frame_data->sync_point =
ImageTransportFactoryAndroid::GetInstance()->InsertSyncPoint();
}
RenderWidgetHostImpl::SendSwapCompositorFrameAck(
route_id, output_surface_id, renderer_host_id, ack);
}
// Sends an acknowledgement to the renderer of a processed IME event.
void SendImeEventAck(RenderWidgetHostImpl* host) {
host->Send(new ViewMsg_ImeEventAck(host->GetRoutingID()));
}
void CopyFromCompositingSurfaceFinished(
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::SingleReleaseCallback> release_callback,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result) {
bitmap_pixels_lock.reset();
release_callback->Run(0, false);
callback.Run(result, *bitmap);
}
bool UsingDelegatedRenderer() {
return CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableDelegatedRenderer);
}
} // anonymous namespace
RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid(
RenderWidgetHostImpl* widget_host,
ContentViewCoreImpl* content_view_core)
: host_(widget_host),
needs_begin_frame_(false),
are_layers_attached_(true),
content_view_core_(NULL),
ime_adapter_android_(this),
cached_background_color_(SK_ColorWHITE),
texture_id_in_layer_(0),
last_output_surface_id_(kUndefinedOutputSurfaceId),
weak_ptr_factory_(this),
overscroll_effect_enabled_(
!CommandLine::ForCurrentProcess()->
HasSwitch(switches::kDisableOverscrollEdgeEffect)),
overscroll_effect_(OverscrollGlow::Create(overscroll_effect_enabled_)),
flush_input_requested_(false),
accelerated_surface_route_id_(0),
using_synchronous_compositor_(SynchronousCompositorImpl::FromID(
widget_host->GetProcess()->GetID(),
widget_host->GetRoutingID()) != NULL) {
if (!UsingDelegatedRenderer()) {
texture_layer_ = cc::TextureLayer::Create(NULL);
layer_ = texture_layer_;
}
host_->SetView(this);
SetContentViewCore(content_view_core);
ImageTransportFactoryAndroid::AddObserver(this);
}
RenderWidgetHostViewAndroid::~RenderWidgetHostViewAndroid() {
ImageTransportFactoryAndroid::RemoveObserver(this);
SetContentViewCore(NULL);
DCHECK(ack_callbacks_.empty());
if (texture_id_in_layer_) {
ImageTransportFactoryAndroid::GetInstance()->DeleteTexture(
texture_id_in_layer_);
}
if (texture_layer_.get())
texture_layer_->ClearClient();
if (resource_collection_.get())
resource_collection_->SetClient(NULL);
}
bool RenderWidgetHostViewAndroid::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewAndroid, message)
IPC_MESSAGE_HANDLER(ViewHostMsg_StartContentIntent, OnStartContentIntent)
IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeBodyBackgroundColor,
OnDidChangeBodyBackgroundColor)
IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrame,
OnSetNeedsBeginFrame)
IPC_MESSAGE_HANDLER(ViewHostMsg_TextInputStateChanged,
OnTextInputStateChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_SmartClipDataExtracted,
OnSmartClipDataExtracted)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void RenderWidgetHostViewAndroid::InitAsChild(gfx::NativeView parent_view) {
NOTIMPLEMENTED();
}
void RenderWidgetHostViewAndroid::InitAsPopup(
RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
NOTIMPLEMENTED();
}
void RenderWidgetHostViewAndroid::InitAsFullscreen(
RenderWidgetHostView* reference_host_view) {
NOTIMPLEMENTED();
}
RenderWidgetHost*
RenderWidgetHostViewAndroid::GetRenderWidgetHost() const {
return host_;
}
void RenderWidgetHostViewAndroid::WasShown() {
if (!host_ || !host_->is_hidden())
return;
host_->WasShown();
if (content_view_core_ && !using_synchronous_compositor_)
content_view_core_->GetWindowAndroid()->AddObserver(this);
}
void RenderWidgetHostViewAndroid::WasHidden() {
RunAckCallbacks();
if (!host_ || host_->is_hidden())
return;
// Inform the renderer that we are being hidden so it can reduce its resource
// utilization.
host_->WasHidden();
if (content_view_core_ && !using_synchronous_compositor_)
content_view_core_->GetWindowAndroid()->RemoveObserver(this);
}
void RenderWidgetHostViewAndroid::WasResized() {
host_->WasResized();
}
void RenderWidgetHostViewAndroid::SetSize(const gfx::Size& size) {
// Ignore the given size as only the Java code has the power to
// resize the view on Android.
default_size_ = size;
WasResized();
}
void RenderWidgetHostViewAndroid::SetBounds(const gfx::Rect& rect) {
SetSize(rect.size());
}
blink::WebGLId RenderWidgetHostViewAndroid::GetScaledContentTexture(
float scale,
gfx::Size* out_size) {
gfx::Size size(gfx::ToCeiledSize(
gfx::ScaleSize(texture_size_in_layer_, scale)));
if (!CompositorImpl::IsInitialized() ||
texture_id_in_layer_ == 0 ||
texture_size_in_layer_.IsEmpty() ||
size.IsEmpty()) {
if (out_size)
out_size->SetSize(0, 0);
return 0;
}
if (out_size)
*out_size = size;
GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
return helper->CopyAndScaleTexture(texture_id_in_layer_,
texture_size_in_layer_,
size,
true,
GLHelper::SCALER_QUALITY_FAST);
}
bool RenderWidgetHostViewAndroid::PopulateBitmapWithContents(jobject jbitmap) {
if (!CompositorImpl::IsInitialized() ||
texture_id_in_layer_ == 0 ||
texture_size_in_layer_.IsEmpty())
return false;
gfx::JavaBitmap bitmap(jbitmap);
// TODO(dtrainor): Eventually add support for multiple formats here.
DCHECK(bitmap.format() == ANDROID_BITMAP_FORMAT_RGBA_8888);
GLHelper* helper = ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
blink::WebGLId texture = helper->CopyAndScaleTexture(
texture_id_in_layer_,
texture_size_in_layer_,
bitmap.size(),
true,
GLHelper::SCALER_QUALITY_FAST);
if (texture == 0)
return false;
helper->ReadbackTextureSync(texture,
gfx::Rect(bitmap.size()),
static_cast<unsigned char*> (bitmap.pixels()));
blink::WebGraphicsContext3D* context =
ImageTransportFactoryAndroid::GetInstance()->GetContext3D();
context->deleteTexture(texture);
return true;
}
bool RenderWidgetHostViewAndroid::HasValidFrame() const {
if (!content_view_core_)
return false;
if (texture_size_in_layer_.IsEmpty())
return false;
if (UsingDelegatedRenderer()) {
if (!delegated_renderer_layer_.get())
return false;
} else {
if (texture_id_in_layer_ == 0)
return false;
}
return true;
}
gfx::NativeView RenderWidgetHostViewAndroid::GetNativeView() const {
return content_view_core_->GetViewAndroid();
}
gfx::NativeViewId RenderWidgetHostViewAndroid::GetNativeViewId() const {
return reinterpret_cast<gfx::NativeViewId>(
const_cast<RenderWidgetHostViewAndroid*>(this));
}
gfx::NativeViewAccessible
RenderWidgetHostViewAndroid::GetNativeViewAccessible() {
NOTIMPLEMENTED();
return NULL;
}
void RenderWidgetHostViewAndroid::MovePluginWindows(
const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) {
// We don't have plugin windows on Android. Do nothing. Note: this is called
// from RenderWidgetHost::OnUpdateRect which is itself invoked while
// processing the corresponding message from Renderer.
}
void RenderWidgetHostViewAndroid::Focus() {
host_->Focus();
host_->SetInputMethodActive(true);
ResetClipping();
if (overscroll_effect_enabled_)
overscroll_effect_->Enable();
}
void RenderWidgetHostViewAndroid::Blur() {
host_->ExecuteEditCommand("Unselect", "");
host_->SetInputMethodActive(false);
host_->Blur();
overscroll_effect_->Disable();
}
bool RenderWidgetHostViewAndroid::HasFocus() const {
if (!content_view_core_)
return false; // ContentViewCore not created yet.
return content_view_core_->HasFocus();
}
bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const {
return HasValidFrame();
}
void RenderWidgetHostViewAndroid::Show() {
if (are_layers_attached_)
return;
are_layers_attached_ = true;
AttachLayers();
WasShown();
}
void RenderWidgetHostViewAndroid::Hide() {
if (!are_layers_attached_)
return;
are_layers_attached_ = false;
RemoveLayers();
WasHidden();
}
bool RenderWidgetHostViewAndroid::IsShowing() {
// ContentViewCoreImpl represents the native side of the Java
// ContentViewCore. It being NULL means that it is not attached
// to the View system yet, so we treat this RWHVA as hidden.
return are_layers_attached_ && content_view_core_;
}
gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const {
if (!content_view_core_)
return gfx::Rect(default_size_);
gfx::Size size = content_view_core_->GetViewportSizeDip();
gfx::Size offset = content_view_core_->GetViewportSizeOffsetDip();
size.Enlarge(-offset.width(), -offset.height());
return gfx::Rect(size);
}
gfx::Size RenderWidgetHostViewAndroid::GetPhysicalBackingSize() const {
if (!content_view_core_)
return gfx::Size();
return content_view_core_->GetPhysicalBackingSize();
}
float RenderWidgetHostViewAndroid::GetOverdrawBottomHeight() const {
if (!content_view_core_)
return 0.f;
return content_view_core_->GetOverdrawBottomHeightDip();
}
void RenderWidgetHostViewAndroid::UpdateCursor(const WebCursor& cursor) {
// There are no cursors on Android.
}
void RenderWidgetHostViewAndroid::SetIsLoading(bool is_loading) {
// Do nothing. The UI notification is handled through ContentViewClient which
// is TabContentsDelegate.
}
void RenderWidgetHostViewAndroid::TextInputTypeChanged(
ui::TextInputType type,
ui::TextInputMode input_mode,
bool can_compose_inline) {
// Unused on Android, which uses OnTextInputChanged instead.
}
int RenderWidgetHostViewAndroid::GetNativeImeAdapter() {
return reinterpret_cast<int>(&ime_adapter_android_);
}
void RenderWidgetHostViewAndroid::OnTextInputStateChanged(
const ViewHostMsg_TextInputState_Params& params) {
// If an acknowledgement is required for this event, regardless of how we exit
// from this method, we must acknowledge that we processed the input state
// change.
base::ScopedClosureRunner ack_caller;
if (params.require_ack)
ack_caller.Reset(base::Bind(&SendImeEventAck, host_));
if (!IsShowing())
return;
content_view_core_->UpdateImeAdapter(
GetNativeImeAdapter(),
static_cast<int>(params.type),
params.value, params.selection_start, params.selection_end,
params.composition_start, params.composition_end,
params.show_ime_if_needed, params.require_ack);
}
void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor(
SkColor color) {
if (cached_background_color_ == color)
return;
cached_background_color_ = color;
if (content_view_core_)
content_view_core_->OnBackgroundColorChanged(color);
}
void RenderWidgetHostViewAndroid::SendBeginFrame(
const cc::BeginFrameArgs& args) {
TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame");
if (!host_)
return;
if (flush_input_requested_) {
flush_input_requested_ = false;
host_->FlushInput();
content_view_core_->RemoveBeginFrameSubscriber();
}
host_->Send(new ViewMsg_BeginFrame(host_->GetRoutingID(), args));
}
void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(
bool enabled) {
TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame",
"enabled", enabled);
// ContentViewCoreImpl handles multiple subscribers to the BeginFrame, so
// we have to make sure calls to ContentViewCoreImpl's
// {Add,Remove}BeginFrameSubscriber are balanced, even if
// RenderWidgetHostViewAndroid's may not be.
if (content_view_core_ && needs_begin_frame_ != enabled) {
if (enabled)
content_view_core_->AddBeginFrameSubscriber();
else
content_view_core_->RemoveBeginFrameSubscriber();
needs_begin_frame_ = enabled;
}
}
void RenderWidgetHostViewAndroid::OnStartContentIntent(
const GURL& content_url) {
if (content_view_core_)
content_view_core_->StartContentIntent(content_url);
}
void RenderWidgetHostViewAndroid::OnSmartClipDataExtracted(
const string16& result) {
// Custom serialization over IPC isn't allowed normally for security reasons.
// Since this feature is only used in (single-process) WebView, there are no
// security issues. Enforce that it's only called in single process mode.
CHECK(RenderProcessHost::run_renderer_in_process());
if (content_view_core_)
content_view_core_->OnSmartClipDataExtracted(result);
}
void RenderWidgetHostViewAndroid::ImeCancelComposition() {
ime_adapter_android_.CancelComposition();
}
void RenderWidgetHostViewAndroid::DidUpdateBackingStore(
const gfx::Rect& scroll_rect,
const gfx::Vector2d& scroll_delta,
const std::vector<gfx::Rect>& copy_rects,
const ui::LatencyInfo& latency_info) {
NOTIMPLEMENTED();
}
void RenderWidgetHostViewAndroid::RenderProcessGone(
base::TerminationStatus status, int error_code) {
Destroy();
}
void RenderWidgetHostViewAndroid::Destroy() {
RemoveLayers();
SetContentViewCore(NULL);
// The RenderWidgetHost's destruction led here, so don't call it.
host_ = NULL;
delete this;
}
void RenderWidgetHostViewAndroid::SetTooltipText(
const base::string16& tooltip_text) {
// Tooltips don't makes sense on Android.
}
void RenderWidgetHostViewAndroid::SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) {
RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
if (text.empty() || range.is_empty() || !content_view_core_)
return;
size_t pos = range.GetMin() - offset;
size_t n = range.length();
DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
if (pos >= text.length()) {
NOTREACHED() << "The text can not cover range.";
return;
}
std::string utf8_selection = UTF16ToUTF8(text.substr(pos, n));
content_view_core_->OnSelectionChanged(utf8_selection);
}
void RenderWidgetHostViewAndroid::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params& params) {
if (content_view_core_) {
content_view_core_->OnSelectionBoundsChanged(params);
}
}
void RenderWidgetHostViewAndroid::ScrollOffsetChanged() {
}
BackingStore* RenderWidgetHostViewAndroid::AllocBackingStore(
const gfx::Size& size) {
NOTIMPLEMENTED();
return NULL;
}
void RenderWidgetHostViewAndroid::SetBackground(const SkBitmap& background) {
RenderWidgetHostViewBase::SetBackground(background);
host_->Send(new ViewMsg_SetBackground(host_->GetRoutingID(), background));
}
void RenderWidgetHostViewAndroid::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
const base::Callback<void(bool, const SkBitmap&)>& callback) {
if (!using_synchronous_compositor_ && !IsSurfaceAvailableForCopy()) {
callback.Run(false, SkBitmap());
return;
}
const gfx::Display& display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
float device_scale_factor = display.device_scale_factor();
DCHECK_EQ(device_scale_factor,
ui::GetImageScale(GetScaleFactorForView(this)));
const gfx::Size& dst_size_in_pixel = ConvertViewSizeToPixel(this, dst_size);
gfx::Rect src_subrect_in_pixel =
ConvertRectToPixel(device_scale_factor, src_subrect);
if (using_synchronous_compositor_) {
SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback);
return;
}
scoped_ptr<cc::CopyOutputRequest> request;
if (src_subrect_in_pixel.size() == dst_size_in_pixel) {
request = cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
&RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult,
dst_size_in_pixel,
callback));
} else {
request = cc::CopyOutputRequest::CreateRequest(base::Bind(
&RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult,
dst_size_in_pixel,
callback));
}
request->set_area(src_subrect_in_pixel);
layer_->RequestCopyOfOutput(request.Pass());
}
void RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceToVideoFrame(
const gfx::Rect& src_subrect,
const scoped_refptr<media::VideoFrame>& target,
const base::Callback<void(bool)>& callback) {
NOTIMPLEMENTED();
callback.Run(false);
}
bool RenderWidgetHostViewAndroid::CanCopyToVideoFrame() const {
return false;
}
void RenderWidgetHostViewAndroid::ShowDisambiguationPopup(
const gfx::Rect& target_rect, const SkBitmap& zoomed_bitmap) {
if (!content_view_core_)
return;
content_view_core_->ShowDisambiguationPopup(target_rect, zoomed_bitmap);
}
scoped_ptr<SyntheticGestureTarget>
RenderWidgetHostViewAndroid::CreateSyntheticGestureTarget() {
return scoped_ptr<SyntheticGestureTarget>(new SyntheticGestureTargetAndroid(
host_, content_view_core_->CreateTouchEventSynthesizer()));
}
void RenderWidgetHostViewAndroid::OnAcceleratedCompositingStateChange() {
}
void RenderWidgetHostViewAndroid::SendDelegatedFrameAck(
uint32 output_surface_id) {
cc::CompositorFrameAck ack;
if (resource_collection_.get())
resource_collection_->TakeUnusedResourcesForChildCompositor(&ack.resources);
RenderWidgetHostImpl::SendSwapCompositorFrameAck(host_->GetRoutingID(),
output_surface_id,
host_->GetProcess()->GetID(),
ack);
}
void RenderWidgetHostViewAndroid::UnusedResourcesAreAvailable() {
// TODO(danakj): If no ack is pending, collect and send resources now.
}
void RenderWidgetHostViewAndroid::DestroyDelegatedContent() {
if (are_layers_attached_)
RemoveLayers();
frame_provider_ = NULL;
delegated_renderer_layer_ = NULL;
layer_ = NULL;
}
void RenderWidgetHostViewAndroid::SwapDelegatedFrame(
uint32 output_surface_id,
scoped_ptr<cc::DelegatedFrameData> frame_data) {
bool has_content = !texture_size_in_layer_.IsEmpty();
if (output_surface_id != last_output_surface_id_) {
// TODO(danakj): Lose all resources and send them back here, such as:
// resource_collection_->LoseAllResources();
// SendReturnedDelegatedResources(last_output_surface_id_);
// Drop the cc::DelegatedFrameResourceCollection so that we will not return
// any resources from the old output surface with the new output surface id.
if (resource_collection_.get()) {
resource_collection_->SetClient(NULL);
resource_collection_ = NULL;
}
DestroyDelegatedContent();
last_output_surface_id_ = output_surface_id;
}
if (!has_content) {
DestroyDelegatedContent();
} else {
if (!resource_collection_.get()) {
resource_collection_ = new cc::DelegatedFrameResourceCollection;
resource_collection_->SetClient(this);
}
if (!frame_provider_ ||
texture_size_in_layer_ != frame_provider_->frame_size()) {
if (are_layers_attached_)
RemoveLayers();
frame_provider_ = new cc::DelegatedFrameProvider(
resource_collection_.get(), frame_data.Pass());
delegated_renderer_layer_ =
cc::DelegatedRendererLayer::Create(frame_provider_);
layer_ = delegated_renderer_layer_;
if (are_layers_attached_)
AttachLayers();
} else {
frame_provider_->SetFrameData(frame_data.Pass());
}
}
if (delegated_renderer_layer_.get()) {
delegated_renderer_layer_->SetDisplaySize(texture_size_in_layer_);
delegated_renderer_layer_->SetIsDrawable(true);
delegated_renderer_layer_->SetContentsOpaque(true);
delegated_renderer_layer_->SetBounds(content_size_in_layer_);
delegated_renderer_layer_->SetNeedsDisplay();
}
base::Closure ack_callback =
base::Bind(&RenderWidgetHostViewAndroid::SendDelegatedFrameAck,
weak_ptr_factory_.GetWeakPtr(),
output_surface_id);
if (host_->is_hidden())
ack_callback.Run();
else
ack_callbacks_.push(ack_callback);
}
void RenderWidgetHostViewAndroid::ComputeContentsSize(
const cc::CompositorFrameMetadata& frame_metadata) {
// Calculate the content size. This should be 0 if the texture_size is 0.
gfx::Vector2dF offset;
if (texture_size_in_layer_.GetArea() > 0)
offset = frame_metadata.location_bar_content_translation;
offset.set_y(offset.y() + frame_metadata.overdraw_bottom_height);
offset.Scale(frame_metadata.device_scale_factor);
content_size_in_layer_ =
gfx::Size(texture_size_in_layer_.width() - offset.x(),
texture_size_in_layer_.height() - offset.y());
// Content size changes should be reflected in associated animation effects.
UpdateAnimationSize(frame_metadata);
}
void RenderWidgetHostViewAndroid::OnSwapCompositorFrame(
uint32 output_surface_id,
scoped_ptr<cc::CompositorFrame> frame) {
// Always let ContentViewCore know about the new frame first, so it can decide
// to schedule a Draw immediately when it sees the texture layer invalidation.
UpdateContentViewCoreFrameMetadata(frame->metadata);
if (frame->delegated_frame_data) {
DCHECK(UsingDelegatedRenderer());
DCHECK(frame->delegated_frame_data);
DCHECK(!frame->delegated_frame_data->render_pass_list.empty());
cc::RenderPass* root_pass =
frame->delegated_frame_data->render_pass_list.back();
texture_size_in_layer_ = root_pass->output_rect.size();
ComputeContentsSize(frame->metadata);
SwapDelegatedFrame(output_surface_id, frame->delegated_frame_data.Pass());
return;
}
DCHECK(!UsingDelegatedRenderer());
if (!frame->gl_frame_data || frame->gl_frame_data->mailbox.IsZero())
return;
if (output_surface_id != last_output_surface_id_) {
current_mailbox_ = gpu::Mailbox();
last_output_surface_id_ = kUndefinedOutputSurfaceId;
}
base::Closure callback = base::Bind(&InsertSyncPointAndAckForCompositor,
host_->GetProcess()->GetID(),
output_surface_id,
host_->GetRoutingID(),
current_mailbox_,
texture_size_in_layer_);
ImageTransportFactoryAndroid::GetInstance()->WaitSyncPoint(
frame->gl_frame_data->sync_point);
texture_size_in_layer_ = frame->gl_frame_data->size;
ComputeContentsSize(frame->metadata);
if (layer_->layer_tree_host()) {
scoped_ptr<cc::SwapPromise> swap_promise(
new cc::LatencyInfoSwapPromise(frame->metadata.latency_info));
layer_->layer_tree_host()->QueueSwapPromise(swap_promise.Pass());
}
BuffersSwapped(frame->gl_frame_data->mailbox, output_surface_id, callback);
}
void RenderWidgetHostViewAndroid::SynchronousFrameMetadata(
const cc::CompositorFrameMetadata& frame_metadata) {
// This is a subset of OnSwapCompositorFrame() used in the synchronous
// compositor flow.
UpdateContentViewCoreFrameMetadata(frame_metadata);
ComputeContentsSize(frame_metadata);
// DevTools ScreenCast support for Android WebView.
if (DevToolsAgentHost::HasFor(RenderViewHost::From(GetRenderWidgetHost()))) {
scoped_refptr<DevToolsAgentHost> dtah =
DevToolsAgentHost::GetOrCreateFor(
RenderViewHost::From(GetRenderWidgetHost()));
// Unblock the compositor.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame,
static_cast<RenderViewDevToolsAgentHost*>(dtah.get()),
frame_metadata));
}
}
void RenderWidgetHostViewAndroid::SynchronousCopyContents(
const gfx::Rect& src_subrect_in_pixel,
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback) {
SynchronousCompositor* compositor =
SynchronousCompositorImpl::FromID(host_->GetProcess()->GetID(),
host_->GetRoutingID());
if (!compositor) {
callback.Run(false, SkBitmap());
return;
}
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
dst_size_in_pixel.width(),
dst_size_in_pixel.height());
bitmap.allocPixels();
SkCanvas canvas(bitmap);
canvas.scale(
(float)dst_size_in_pixel.width() / (float)src_subrect_in_pixel.width(),
(float)dst_size_in_pixel.height() / (float)src_subrect_in_pixel.height());
compositor->DemandDrawSw(&canvas);
callback.Run(true, bitmap);
}
void RenderWidgetHostViewAndroid::UpdateContentViewCoreFrameMetadata(
const cc::CompositorFrameMetadata& frame_metadata) {
if (content_view_core_) {
// All offsets and sizes are in CSS pixels.
content_view_core_->UpdateFrameInfo(
frame_metadata.root_scroll_offset,
frame_metadata.page_scale_factor,
gfx::Vector2dF(frame_metadata.min_page_scale_factor,
frame_metadata.max_page_scale_factor),
frame_metadata.root_layer_size,
frame_metadata.viewport_size,
frame_metadata.location_bar_offset,
frame_metadata.location_bar_content_translation,
frame_metadata.overdraw_bottom_height);
}
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceInitialized(int host_id,
int route_id) {
accelerated_surface_route_id_ = route_id;
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params,
int gpu_host_id) {
NOTREACHED() << "Need --composite-to-mailbox or --enable-delegated-renderer";
}
void RenderWidgetHostViewAndroid::BuffersSwapped(
const gpu::Mailbox& mailbox,
uint32_t output_surface_id,
const base::Closure& ack_callback) {
ImageTransportFactoryAndroid* factory =
ImageTransportFactoryAndroid::GetInstance();
if (!texture_id_in_layer_) {
texture_id_in_layer_ = factory->CreateTexture();
texture_layer_->SetTextureId(texture_id_in_layer_);
texture_layer_->SetIsDrawable(true);
texture_layer_->SetContentsOpaque(true);
}
ImageTransportFactoryAndroid::GetInstance()->AcquireTexture(
texture_id_in_layer_, mailbox.name);
ResetClipping();
current_mailbox_ = mailbox;
last_output_surface_id_ = output_surface_id;
if (host_->is_hidden())
ack_callback.Run();
else
ack_callbacks_.push(ack_callback);
}
void RenderWidgetHostViewAndroid::AttachLayers() {
if (!content_view_core_)
return;
if (!layer_.get())
return;
content_view_core_->AttachLayer(layer_);
if (overscroll_effect_enabled_)
overscroll_effect_->Enable();
}
void RenderWidgetHostViewAndroid::RemoveLayers() {
if (!content_view_core_)
return;
if (!layer_.get())
return;
content_view_core_->RemoveLayer(layer_);
overscroll_effect_->Disable();
}
bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
return overscroll_effect_->Animate(frame_time);
}
void RenderWidgetHostViewAndroid::UpdateAnimationSize(
const cc::CompositorFrameMetadata& frame_metadata) {
// Disable edge effects for axes on which scrolling is impossible.
gfx::SizeF ceiled_viewport_size =
gfx::ToCeiledSize(frame_metadata.viewport_size);
overscroll_effect_->set_horizontal_overscroll_enabled(
ceiled_viewport_size.width() < frame_metadata.root_layer_size.width());
overscroll_effect_->set_vertical_overscroll_enabled(
ceiled_viewport_size.height() < frame_metadata.root_layer_size.height());
overscroll_effect_->set_size(content_size_in_layer_);
}
void RenderWidgetHostViewAndroid::AcceleratedSurfacePostSubBuffer(
const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params,
int gpu_host_id) {
NOTREACHED();
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceSuspend() {
NOTREACHED();
}
void RenderWidgetHostViewAndroid::AcceleratedSurfaceRelease() {
// This tells us we should free the frontbuffer.
if (texture_id_in_layer_) {
texture_layer_->SetTextureId(0);
texture_layer_->SetIsDrawable(false);
ImageTransportFactoryAndroid::GetInstance()->DeleteTexture(
texture_id_in_layer_);
texture_id_in_layer_ = 0;
current_mailbox_ = gpu::Mailbox();
last_output_surface_id_ = kUndefinedOutputSurfaceId;
}
if (delegated_renderer_layer_.get())
DestroyDelegatedContent();
}
bool RenderWidgetHostViewAndroid::HasAcceleratedSurface(
const gfx::Size& desired_size) {
NOTREACHED();
return false;
}
void RenderWidgetHostViewAndroid::GetScreenInfo(blink::WebScreenInfo* result) {
// ScreenInfo isn't tied to the widget on Android. Always return the default.
RenderWidgetHostViewBase::GetDefaultScreenInfo(result);
}
// TODO(jrg): Find out the implications and answer correctly here,
// as we are returning the WebView and not root window bounds.
gfx::Rect RenderWidgetHostViewAndroid::GetBoundsInRootWindow() {
return GetViewBounds();
}
gfx::GLSurfaceHandle RenderWidgetHostViewAndroid::GetCompositingSurface() {
gfx::GLSurfaceHandle handle =
gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
if (CompositorImpl::IsInitialized()) {
handle.parent_client_id =
ImageTransportFactoryAndroid::GetInstance()->GetChannelID();
}
return handle;
}
void RenderWidgetHostViewAndroid::ProcessAckedTouchEvent(
const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) {
if (content_view_core_)
content_view_core_->ConfirmTouchEvent(ack_result);
}
void RenderWidgetHostViewAndroid::SetHasHorizontalScrollbar(
bool has_horizontal_scrollbar) {
// intentionally empty, like RenderWidgetHostViewViews
}
void RenderWidgetHostViewAndroid::SetScrollOffsetPinning(
bool is_pinned_to_left, bool is_pinned_to_right) {
// intentionally empty, like RenderWidgetHostViewViews
}
void RenderWidgetHostViewAndroid::UnhandledWheelEvent(
const blink::WebMouseWheelEvent& event) {
// intentionally empty, like RenderWidgetHostViewViews
}
void RenderWidgetHostViewAndroid::GestureEventAck(
int gesture_event_type,
InputEventAckState ack_result) {
if (gesture_event_type == blink::WebInputEvent::GestureScrollUpdate &&
ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) {
content_view_core_->OnScrollUpdateGestureConsumed();
}
if (gesture_event_type == blink::WebInputEvent::GestureFlingStart &&
ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) {
content_view_core_->UnhandledFlingStartEvent();
}
}
InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent(
const blink::WebInputEvent& input_event) {
if (!host_)
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
if (input_event.type == blink::WebInputEvent::GestureTapDown ||
input_event.type == blink::WebInputEvent::TouchStart) {
GpuDataManagerImpl* gpu_data = GpuDataManagerImpl::GetInstance();
GpuProcessHostUIShim* shim = GpuProcessHostUIShim::GetOneInstance();
if (shim && gpu_data && accelerated_surface_route_id_ &&
gpu_data->IsDriverBugWorkaroundActive(gpu::WAKE_UP_GPU_BEFORE_DRAWING))
shim->Send(
new AcceleratedSurfaceMsg_WakeUpGpu(accelerated_surface_route_id_));
}
SynchronousCompositorImpl* compositor =
SynchronousCompositorImpl::FromID(host_->GetProcess()->GetID(),
host_->GetRoutingID());
if (compositor)
return compositor->HandleInputEvent(input_event);
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
}
void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() {
if (flush_input_requested_ || !content_view_core_)
return;
flush_input_requested_ = true;
content_view_core_->AddBeginFrameSubscriber();
}
void RenderWidgetHostViewAndroid::OnAccessibilityEvents(
const std::vector<AccessibilityHostMsg_EventParams>& params) {
if (!host_ || host_->accessibility_mode() != AccessibilityModeComplete)
return;
if (!GetBrowserAccessibilityManager()) {
base::android::ScopedJavaLocalRef<jobject> obj;
if (content_view_core_)
obj = content_view_core_->GetJavaObject();
SetBrowserAccessibilityManager(
new BrowserAccessibilityManagerAndroid(
obj, BrowserAccessibilityManagerAndroid::GetEmptyDocument(), this));
}
GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
}
void RenderWidgetHostViewAndroid::SetAccessibilityFocus(int acc_obj_id) {
if (!host_)
return;
host_->AccessibilitySetFocus(acc_obj_id);
}
void RenderWidgetHostViewAndroid::AccessibilityDoDefaultAction(int acc_obj_id) {
if (!host_)
return;
host_->AccessibilityDoDefaultAction(acc_obj_id);
}
void RenderWidgetHostViewAndroid::AccessibilityScrollToMakeVisible(
int acc_obj_id, gfx::Rect subfocus) {
if (!host_)
return;
host_->AccessibilityScrollToMakeVisible(acc_obj_id, subfocus);
}
void RenderWidgetHostViewAndroid::AccessibilityScrollToPoint(
int acc_obj_id, gfx::Point point) {
if (!host_)
return;
host_->AccessibilityScrollToPoint(acc_obj_id, point);
}
void RenderWidgetHostViewAndroid::AccessibilitySetTextSelection(
int acc_obj_id, int start_offset, int end_offset) {
if (!host_)
return;
host_->AccessibilitySetTextSelection(
acc_obj_id, start_offset, end_offset);
}
gfx::Point RenderWidgetHostViewAndroid::GetLastTouchEventLocation() const {
NOTIMPLEMENTED();
// Only used on Win8
return gfx::Point();
}
void RenderWidgetHostViewAndroid::FatalAccessibilityTreeError() {
if (!host_)
return;
host_->FatalAccessibilityTreeError();
SetBrowserAccessibilityManager(NULL);
}
bool RenderWidgetHostViewAndroid::LockMouse() {
NOTIMPLEMENTED();
return false;
}
void RenderWidgetHostViewAndroid::UnlockMouse() {
NOTIMPLEMENTED();
}
// Methods called from the host to the render
void RenderWidgetHostViewAndroid::SendKeyEvent(
const NativeWebKeyboardEvent& event) {
if (host_)
host_->ForwardKeyboardEvent(event);
}
void RenderWidgetHostViewAndroid::SendTouchEvent(
const blink::WebTouchEvent& event) {
if (host_)
host_->ForwardTouchEventWithLatencyInfo(event, ui::LatencyInfo());
}
void RenderWidgetHostViewAndroid::SendMouseEvent(
const blink::WebMouseEvent& event) {
if (host_)
host_->ForwardMouseEvent(event);
}
void RenderWidgetHostViewAndroid::SendMouseWheelEvent(
const blink::WebMouseWheelEvent& event) {
if (host_)
host_->ForwardWheelEvent(event);
}
void RenderWidgetHostViewAndroid::SendGestureEvent(
const blink::WebGestureEvent& event) {
// Sending a gesture that may trigger overscroll should resume the effect.
if (overscroll_effect_enabled_)
overscroll_effect_->Enable();
if (host_)
host_->ForwardGestureEvent(event);
}
void RenderWidgetHostViewAndroid::SelectRange(const gfx::Point& start,
const gfx::Point& end) {
if (host_)
host_->SelectRange(start, end);
}
void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) {
if (host_)
host_->MoveCaret(point);
}
void RenderWidgetHostViewAndroid::RequestContentClipping(
const gfx::Rect& clipping,
const gfx::Size& content_size) {
// A focused view provides its own clipping.
if (HasFocus())
return;
ClipContents(clipping, content_size);
}
void RenderWidgetHostViewAndroid::ResetClipping() {
ClipContents(gfx::Rect(gfx::Point(), content_size_in_layer_),
content_size_in_layer_);
}
void RenderWidgetHostViewAndroid::ClipContents(const gfx::Rect& clipping,
const gfx::Size& content_size) {
if (!texture_id_in_layer_ || content_size_in_layer_.IsEmpty())
return;
gfx::Size clipped_content(content_size_in_layer_);
clipped_content.SetToMin(clipping.size());
texture_layer_->SetBounds(clipped_content);
texture_layer_->SetNeedsDisplay();
if (texture_size_in_layer_.IsEmpty()) {
texture_layer_->SetUV(gfx::PointF(), gfx::PointF());
return;
}
gfx::PointF offset(
clipping.x() + content_size_in_layer_.width() - content_size.width(),
clipping.y() + content_size_in_layer_.height() - content_size.height());
offset.SetToMax(gfx::PointF());
gfx::Vector2dF uv_scale(1.f / texture_size_in_layer_.width(),
1.f / texture_size_in_layer_.height());
texture_layer_->SetUV(
gfx::PointF(offset.x() * uv_scale.x(),
offset.y() * uv_scale.y()),
gfx::PointF((offset.x() + clipped_content.width()) * uv_scale.x(),
(offset.y() + clipped_content.height()) * uv_scale.y()));
}
SkColor RenderWidgetHostViewAndroid::GetCachedBackgroundColor() const {
return cached_background_color_;
}
void RenderWidgetHostViewAndroid::OnOverscrolled(
gfx::Vector2dF accumulated_overscroll,
gfx::Vector2dF current_fling_velocity) {
if (!content_view_core_ || !are_layers_attached_)
return;
if (overscroll_effect_->OnOverscrolled(content_view_core_->GetLayer(),
base::TimeTicks::Now(),
accumulated_overscroll,
current_fling_velocity)) {
content_view_core_->SetNeedsAnimate();
}
}
void RenderWidgetHostViewAndroid::SetContentViewCore(
ContentViewCoreImpl* content_view_core) {
RunAckCallbacks();
if (are_layers_attached_)
RemoveLayers();
if (content_view_core_ && !using_synchronous_compositor_)
content_view_core_->GetWindowAndroid()->RemoveObserver(this);
content_view_core_ = content_view_core;
if (GetBrowserAccessibilityManager()) {
base::android::ScopedJavaLocalRef<jobject> obj;
if (content_view_core_)
obj = content_view_core_->GetJavaObject();
GetBrowserAccessibilityManager()->ToBrowserAccessibilityManagerAndroid()->
SetContentViewCore(obj);
}
if (are_layers_attached_) {
AttachLayers();
if (content_view_core_ && !using_synchronous_compositor_)
content_view_core_->GetWindowAndroid()->AddObserver(this);
}
// Ensure ContentsViewCore is aware of the current touch handling state, eg.
// in case we've already been running JS for the page as part of preload.
if (content_view_core_ && host_)
content_view_core_->HasTouchEventHandlers(host_->has_touch_handler());
}
void RenderWidgetHostViewAndroid::RunAckCallbacks() {
while (!ack_callbacks_.empty()) {
ack_callbacks_.front().Run();
ack_callbacks_.pop();
}
}
void RenderWidgetHostViewAndroid::HasTouchEventHandlers(
bool need_touch_events) {
if (content_view_core_)
content_view_core_->HasTouchEventHandlers(need_touch_events);
}
void RenderWidgetHostViewAndroid::OnCompositingDidCommit() {
RunAckCallbacks();
}
void RenderWidgetHostViewAndroid::OnDetachCompositor() {
DCHECK(content_view_core_);
DCHECK(!using_synchronous_compositor_);
RunAckCallbacks();
}
void RenderWidgetHostViewAndroid::OnLostResources() {
if (texture_layer_.get())
texture_layer_->SetIsDrawable(false);
if (delegated_renderer_layer_.get())
DestroyDelegatedContent();
texture_id_in_layer_ = 0;
RunAckCallbacks();
}
// static
void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasTexture());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty())
return;
scoped_ptr<SkBitmap> bitmap(new SkBitmap);
bitmap->setConfig(SkBitmap::kARGB_8888_Config,
dst_size_in_pixel.width(), dst_size_in_pixel.height(),
0, kOpaque_SkAlphaType);
if (!bitmap->allocPixels())
return;
ImageTransportFactoryAndroid* factory =
ImageTransportFactoryAndroid::GetInstance();
GLHelper* gl_helper = factory->GetGLHelper();
if (!gl_helper)
return;
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
new SkAutoLockPixels(*bitmap));
uint8* pixels = static_cast<uint8*>(bitmap->getPixels());
cc::TextureMailbox texture_mailbox;
scoped_ptr<cc::SingleReleaseCallback> release_callback;
result->TakeTexture(&texture_mailbox, &release_callback);
DCHECK(texture_mailbox.IsTexture());
if (!texture_mailbox.IsTexture())
return;
ignore_result(scoped_callback_runner.Release());
gl_helper->CropScaleReadbackAndCleanMailbox(
texture_mailbox.name(),
texture_mailbox.sync_point(),
result->size(),
gfx::Rect(result->size()),
dst_size_in_pixel,
pixels,
base::Bind(&CopyFromCompositingSurfaceFinished,
callback,
base::Passed(&release_callback),
base::Passed(&bitmap),
base::Passed(&bitmap_pixels_lock)));
}
// static
void RenderWidgetHostViewAndroid::PrepareBitmapCopyOutputResult(
const gfx::Size& dst_size_in_pixel,
const base::Callback<void(bool, const SkBitmap&)>& callback,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasBitmap());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
if (!result->HasBitmap() || result->IsEmpty() || result->size().IsEmpty())
return;
scoped_ptr<SkBitmap> source = result->TakeBitmap();
DCHECK(source);
if (!source)
return;
DCHECK_EQ(source->width(), dst_size_in_pixel.width());
DCHECK_EQ(source->height(), dst_size_in_pixel.height());
ignore_result(scoped_callback_runner.Release());
callback.Run(true, *source);
}
// static
void RenderWidgetHostViewPort::GetDefaultScreenInfo(
blink::WebScreenInfo* results) {
const gfx::Display& display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
results->rect = display.bounds();
// TODO(husky): Remove any system controls from availableRect.
results->availableRect = display.work_area();
results->deviceScaleFactor = display.device_scale_factor();
gfx::DeviceDisplayInfo info;
results->depth = info.GetBitsPerPixel();
results->depthPerComponent = info.GetBitsPerComponent();
results->isMonochrome = (results->depthPerComponent == 0);
}
////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostView, public:
// static
RenderWidgetHostView*
RenderWidgetHostView::CreateViewForWidget(RenderWidgetHost* widget) {
RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget);
return new RenderWidgetHostViewAndroid(rwhi, NULL);
}
} // namespace content