| // 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 <utility> |
| |
| #include "base/android/build_info.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/metrics/histogram.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/sys_info.h" |
| #include "base/threading/worker_pool.h" |
| #include "cc/layers/layer.h" |
| #include "cc/layers/surface_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/output/latency_info_swap_promise.h" |
| #include "cc/output/viewport_selection_bound.h" |
| #include "cc/resources/single_release_callback.h" |
| #include "cc/surfaces/surface.h" |
| #include "cc/surfaces/surface_factory.h" |
| #include "cc/surfaces/surface_id_allocator.h" |
| #include "cc/surfaces/surface_manager.h" |
| #include "cc/trees/layer_tree_host.h" |
| #include "content/browser/accessibility/browser_accessibility_manager_android.h" |
| #include "content/browser/android/composited_touch_handle_drawable.h" |
| #include "content/browser/android/content_view_core_impl.h" |
| #include "content/browser/android/overscroll_controller_android.h" |
| #include "content/browser/android/popup_touch_handle_drawable.h" |
| #include "content/browser/android/synchronous_compositor_base.h" |
| #include "content/browser/devtools/render_frame_devtools_agent_host.h" |
| #include "content/browser/gpu/browser_gpu_channel_host_factory.h" |
| #include "content/browser/gpu/compositor_util.h" |
| #include "content/browser/gpu/gpu_data_manager_impl.h" |
| #include "content/browser/gpu/gpu_process_host_ui_shim.h" |
| #include "content/browser/media/android/media_web_contents_observer_android.h" |
| #include "content/browser/renderer_host/compositor_impl_android.h" |
| #include "content/browser/renderer_host/dip_util.h" |
| #include "content/browser/renderer_host/frame_metadata_util.h" |
| #include "content/browser/renderer_host/input/synthetic_gesture_target_android.h" |
| #include "content/browser/renderer_host/input/ui_touch_selection_helper.h" |
| #include "content/browser/renderer_host/input/web_input_event_builders_android.h" |
| #include "content/browser/renderer_host/input/web_input_event_util.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/browser/renderer_host/render_view_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/client/webgraphicscontext3d_command_buffer_impl.h" |
| #include "content/common/gpu/gpu_host_messages.h" |
| #include "content/common/gpu/gpu_process_launch_causes.h" |
| #include "content/common/input/did_overscroll_params.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/browser/android/compositor.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_agent_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_iterator.h" |
| #include "content/public/common/content_switches.h" |
| #include "gpu/command_buffer/client/gles2_implementation.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/config/gpu_driver_bug_workaround_type.h" |
| #include "ipc/ipc_message_macros.h" |
| #include "ipc/ipc_message_start.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/android/window_android.h" |
| #include "ui/android/window_android_compositor.h" |
| #include "ui/events/blink/blink_event_util.h" |
| #include "ui/events/gesture_detection/gesture_provider_config_helper.h" |
| #include "ui/events/gesture_detection/motion_event.h" |
| #include "ui/gfx/android/device_display_info.h" |
| #include "ui/gfx/android/java_bitmap.h" |
| #include "ui/gfx/android/view_configuration.h" |
| #include "ui/gfx/display.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gfx/screen.h" |
| #include "ui/touch_selection/touch_selection_controller.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| void SatisfyCallback(cc::SurfaceManager* manager, |
| cc::SurfaceSequence sequence) { |
| std::vector<uint32_t> sequences; |
| sequences.push_back(sequence.sequence); |
| manager->DidSatisfySequences(sequence.id_namespace, &sequences); |
| } |
| |
| void RequireCallback(cc::SurfaceManager* manager, |
| cc::SurfaceId id, |
| cc::SurfaceSequence sequence) { |
| cc::Surface* surface = manager->GetSurfaceForId(id); |
| if (!surface) { |
| LOG(ERROR) << "Attempting to require callback on nonexistent surface"; |
| return; |
| } |
| surface->AddDestructionDependency(sequence); |
| } |
| |
| const int kUndefinedOutputSurfaceId = -1; |
| |
| static const char kAsyncReadBackString[] = "Compositing.CopyFromSurfaceTime"; |
| |
| class GLHelperHolder |
| : public blink::WebGraphicsContext3D::WebGraphicsContextLostCallback { |
| public: |
| static GLHelperHolder* Create(); |
| ~GLHelperHolder() override; |
| |
| void Initialize(); |
| |
| // WebGraphicsContextLostCallback implementation. |
| void onContextLost() override; |
| |
| GLHelper* GetGLHelper() { return gl_helper_.get(); } |
| bool IsLost() { |
| return !context_ || |
| context_->GetGLInterface()->GetGraphicsResetStatusKHR() != |
| GL_NO_ERROR; |
| } |
| |
| private: |
| GLHelperHolder(); |
| static scoped_ptr<WebGraphicsContext3DCommandBufferImpl> CreateContext3D(); |
| |
| scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context_; |
| scoped_ptr<GLHelper> gl_helper_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GLHelperHolder); |
| }; |
| |
| GLHelperHolder* GLHelperHolder::Create() { |
| GLHelperHolder* holder = new GLHelperHolder; |
| holder->Initialize(); |
| |
| return holder; |
| } |
| |
| GLHelperHolder::GLHelperHolder() { |
| } |
| |
| GLHelperHolder::~GLHelperHolder() { |
| } |
| |
| void GLHelperHolder::Initialize() { |
| context_ = CreateContext3D(); |
| if (context_) { |
| context_->setContextLostCallback(this); |
| gl_helper_.reset(new GLHelper(context_->GetImplementation(), |
| context_->GetContextSupport())); |
| } |
| } |
| |
| void GLHelperHolder::onContextLost() { |
| // Need to post a task because the command buffer client cannot be deleted |
| // from within this callback. |
| LOG(ERROR) << "Context lost."; |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&RenderWidgetHostViewAndroid::OnContextLost)); |
| } |
| |
| scoped_ptr<WebGraphicsContext3DCommandBufferImpl> |
| GLHelperHolder::CreateContext3D() { |
| BrowserGpuChannelHostFactory* factory = |
| BrowserGpuChannelHostFactory::instance(); |
| scoped_refptr<GpuChannelHost> gpu_channel_host(factory->GetGpuChannel()); |
| // GLHelper can only be used in asynchronous APIs for postprocessing after |
| // Browser Compositor operations (i.e. readback). |
| if (!gpu_channel_host.get()) { |
| // The Browser Compositor is in charge of reestablishing the channel. |
| return scoped_ptr<WebGraphicsContext3DCommandBufferImpl>(); |
| } |
| |
| blink::WebGraphicsContext3D::Attributes attrs; |
| attrs.shareResources = true; |
| GURL url("chrome://gpu/RenderWidgetHostViewAndroid"); |
| static const size_t kBytesPerPixel = 4; |
| gfx::DeviceDisplayInfo display_info; |
| size_t full_screen_texture_size_in_bytes = display_info.GetDisplayHeight() * |
| display_info.GetDisplayWidth() * |
| kBytesPerPixel; |
| WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits limits; |
| limits.command_buffer_size = 64 * 1024; |
| limits.start_transfer_buffer_size = 64 * 1024; |
| limits.min_transfer_buffer_size = 64 * 1024; |
| limits.max_transfer_buffer_size = std::min( |
| 3 * full_screen_texture_size_in_bytes, kDefaultMaxTransferBufferSize); |
| limits.mapped_memory_reclaim_limit = |
| WebGraphicsContext3DCommandBufferImpl::kNoLimit; |
| bool lose_context_when_out_of_memory = false; |
| scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context( |
| new WebGraphicsContext3DCommandBufferImpl( |
| gpu::kNullSurfaceHandle, // offscreen |
| url, gpu_channel_host.get(), attrs, lose_context_when_out_of_memory, |
| limits, nullptr)); |
| context->SetContextType(BROWSER_OFFSCREEN_MAINTHREAD_CONTEXT); |
| if (context->InitializeOnCurrentThread()) { |
| context->GetImplementation()->TraceBeginCHROMIUM( |
| "gpu_toplevel", |
| base::StringPrintf("CmdBufferImageTransportFactory-%p", context.get()) |
| .c_str()); |
| } else { |
| context.reset(); |
| } |
| |
| return context; |
| } |
| |
| // This can only be used for readback postprocessing. It may return null if the |
| // channel was lost and not reestablished yet. |
| GLHelper* GetPostReadbackGLHelper() { |
| static GLHelperHolder* g_readback_helper_holder = nullptr; |
| |
| if (g_readback_helper_holder && g_readback_helper_holder->IsLost()) { |
| delete g_readback_helper_holder; |
| g_readback_helper_holder = nullptr; |
| } |
| |
| if (!g_readback_helper_holder) |
| g_readback_helper_holder = GLHelperHolder::Create(); |
| |
| return g_readback_helper_holder->GetGLHelper(); |
| } |
| |
| void CopyFromCompositingSurfaceFinished( |
| const ReadbackRequestCallback& callback, |
| scoped_ptr<cc::SingleReleaseCallback> release_callback, |
| scoped_ptr<SkBitmap> bitmap, |
| const base::TimeTicks& start_time, |
| scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock, |
| bool result) { |
| TRACE_EVENT0( |
| "cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceFinished"); |
| bitmap_pixels_lock.reset(); |
| gpu::SyncToken sync_token; |
| if (result) { |
| GLHelper* gl_helper = GetPostReadbackGLHelper(); |
| if (gl_helper) |
| gl_helper->GenerateSyncToken(&sync_token); |
| } |
| const bool lost_resource = !sync_token.HasData(); |
| release_callback->Run(sync_token, lost_resource); |
| UMA_HISTOGRAM_TIMES(kAsyncReadBackString, |
| base::TimeTicks::Now() - start_time); |
| ReadbackResponse response = result ? READBACK_SUCCESS : READBACK_FAILED; |
| callback.Run(*bitmap, response); |
| } |
| |
| scoped_ptr<ui::TouchSelectionController> CreateSelectionController( |
| ui::TouchSelectionControllerClient* client, |
| ContentViewCore* content_view_core) { |
| DCHECK(client); |
| DCHECK(content_view_core); |
| ui::TouchSelectionController::Config config; |
| config.max_tap_duration = base::TimeDelta::FromMilliseconds( |
| gfx::ViewConfiguration::GetLongPressTimeoutInMs()); |
| config.tap_slop = gfx::ViewConfiguration::GetTouchSlopInDips(); |
| config.show_on_tap_for_empty_editable = false; |
| config.enable_adaptive_handle_orientation = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableAdaptiveSelectionHandleOrientation); |
| config.enable_longpress_drag_selection = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableLongpressDragSelection); |
| return make_scoped_ptr(new ui::TouchSelectionController(client, config)); |
| } |
| |
| scoped_ptr<OverscrollControllerAndroid> CreateOverscrollController( |
| ContentViewCoreImpl* content_view_core) { |
| return make_scoped_ptr(new OverscrollControllerAndroid(content_view_core)); |
| } |
| |
| gfx::RectF GetSelectionRect(const ui::TouchSelectionController& controller) { |
| gfx::RectF rect = controller.GetRectBetweenBounds(); |
| if (rect.IsEmpty()) |
| return rect; |
| |
| rect.Union(controller.GetStartHandleRect()); |
| rect.Union(controller.GetEndHandleRect()); |
| return rect; |
| } |
| |
| } // anonymous namespace |
| |
| RenderWidgetHostViewAndroid::LastFrameInfo::LastFrameInfo( |
| uint32_t output_id, |
| scoped_ptr<cc::CompositorFrame> output_frame) |
| : output_surface_id(output_id), frame(std::move(output_frame)) {} |
| |
| RenderWidgetHostViewAndroid::LastFrameInfo::~LastFrameInfo() {} |
| |
| void RenderWidgetHostViewAndroid::OnContextLost() { |
| scoped_ptr<RenderWidgetHostIterator> widgets( |
| RenderWidgetHostImpl::GetAllRenderWidgetHosts()); |
| while (RenderWidgetHost* widget = widgets->GetNextHost()) { |
| if (widget->GetView()) { |
| static_cast<RenderWidgetHostViewAndroid*>(widget->GetView()) |
| ->OnLostResources(); |
| } |
| } |
| } |
| |
| RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid( |
| RenderWidgetHostImpl* widget_host, |
| ContentViewCoreImpl* content_view_core) |
| : host_(widget_host), |
| outstanding_vsync_requests_(0), |
| is_showing_(!widget_host->is_hidden()), |
| is_window_visible_(true), |
| is_window_activity_started_(true), |
| content_view_core_(nullptr), |
| ime_adapter_android_(this), |
| cached_background_color_(SK_ColorWHITE), |
| last_output_surface_id_(kUndefinedOutputSurfaceId), |
| gesture_provider_(ui::GetGestureProviderConfig( |
| ui::GestureProviderConfigType::CURRENT_PLATFORM), |
| this), |
| stylus_text_selector_(this), |
| using_browser_compositor_(CompositorImpl::IsInitialized()), |
| frame_evictor_(new DelegatedFrameEvictor(this)), |
| locks_on_frame_count_(0), |
| observing_root_window_(false), |
| weak_ptr_factory_(this) { |
| if (CompositorImpl::GetSurfaceManager()) |
| id_allocator_ = CompositorImpl::CreateSurfaceIdAllocator(); |
| host_->SetView(this); |
| SetContentViewCore(content_view_core); |
| } |
| |
| RenderWidgetHostViewAndroid::~RenderWidgetHostViewAndroid() { |
| if (content_view_core_) |
| content_view_core_->RemoveObserver(this); |
| SetContentViewCore(NULL); |
| DCHECK(ack_callbacks_.empty()); |
| DCHECK(!surface_factory_); |
| DCHECK(surface_id_.is_null()); |
| } |
| |
| void RenderWidgetHostViewAndroid::Blur() { |
| host_->Blur(); |
| if (overscroll_controller_) |
| overscroll_controller_->Disable(); |
| } |
| |
| bool RenderWidgetHostViewAndroid::OnMessageReceived( |
| const IPC::Message& message) { |
| if (IPC_MESSAGE_ID_CLASS(message.type()) == SyncCompositorMsgStart) { |
| return SyncCompositorOnMessageReceived(message); |
| } |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewAndroid, message) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_StartContentIntent, OnStartContentIntent) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrames, |
| OnSetNeedsBeginFrames) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_SmartClipDataExtracted, |
| OnSmartClipDataExtracted) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShowUnhandledTapUIIfNeeded, |
| OnShowUnhandledTapUIIfNeeded) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| bool RenderWidgetHostViewAndroid::SyncCompositorOnMessageReceived( |
| const IPC::Message& message) { |
| DCHECK(!content_view_core_ || sync_compositor_) << !!content_view_core_; |
| return sync_compositor_ && sync_compositor_->OnMessageReceived(message); |
| } |
| |
| 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::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; |
| } |
| |
| void RenderWidgetHostViewAndroid::SetBounds(const gfx::Rect& rect) { |
| SetSize(rect.size()); |
| } |
| |
| void RenderWidgetHostViewAndroid::GetScaledContentBitmap( |
| float scale, |
| SkColorType preferred_color_type, |
| gfx::Rect src_subrect, |
| const ReadbackRequestCallback& result_callback) { |
| if (!host_ || host_->is_hidden() || !IsSurfaceAvailableForCopy()) { |
| result_callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE); |
| return; |
| } |
| gfx::Size bounds = layer_->bounds(); |
| if (src_subrect.IsEmpty()) |
| src_subrect = gfx::Rect(bounds); |
| DCHECK_LE(src_subrect.width() + src_subrect.x(), bounds.width()); |
| DCHECK_LE(src_subrect.height() + src_subrect.y(), bounds.height()); |
| const gfx::Display& display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); |
| float device_scale_factor = display.device_scale_factor(); |
| DCHECK_GT(device_scale_factor, 0); |
| gfx::Size dst_size( |
| gfx::ScaleToCeiledSize(src_subrect.size(), scale / device_scale_factor)); |
| src_subrect = gfx::ConvertRectToDIP(device_scale_factor, src_subrect); |
| |
| CopyFromCompositingSurface(src_subrect, dst_size, result_callback, |
| preferred_color_type); |
| } |
| |
| scoped_refptr<cc::Layer> RenderWidgetHostViewAndroid::CreateDelegatedLayer() |
| const { |
| scoped_refptr<cc::Layer> delegated_layer; |
| DCHECK(!surface_id_.is_null()); |
| cc::SurfaceManager* manager = CompositorImpl::GetSurfaceManager(); |
| DCHECK(manager); |
| // manager must outlive compositors using it. |
| scoped_refptr<cc::SurfaceLayer> surface_layer = cc::SurfaceLayer::Create( |
| base::Bind(&SatisfyCallback, base::Unretained(manager)), |
| base::Bind(&RequireCallback, base::Unretained(manager))); |
| surface_layer->SetSurfaceId(surface_id_, 1.f, texture_size_in_layer_); |
| delegated_layer = surface_layer; |
| delegated_layer->SetBounds(texture_size_in_layer_); |
| delegated_layer->SetIsDrawable(true); |
| delegated_layer->SetContentsOpaque(true); |
| |
| return delegated_layer; |
| } |
| |
| bool RenderWidgetHostViewAndroid::HasValidFrame() const { |
| if (!content_view_core_) |
| return false; |
| if (!layer_.get()) |
| return false; |
| |
| if (texture_size_in_layer_.IsEmpty()) |
| return false; |
| // This tell us whether a valid frame has arrived or not. |
| if (!frame_evictor_->HasFrame()) |
| return false; |
| |
| return true; |
| } |
| |
| gfx::Vector2dF RenderWidgetHostViewAndroid::GetLastScrollOffset() const { |
| return last_scroll_offset_; |
| } |
| |
| gfx::NativeView RenderWidgetHostViewAndroid::GetNativeView() const { |
| return content_view_core_; |
| } |
| |
| 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 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(); |
| if (overscroll_controller_) |
| overscroll_controller_->Enable(); |
| if (content_view_core_) { |
| WebContentsImpl* web_contents_impl = |
| static_cast<WebContentsImpl*>(content_view_core_->GetWebContents()); |
| if (web_contents_impl->ShowingInterstitialPage()) |
| content_view_core_->ForceUpdateImeAdapter(GetNativeImeAdapter()); |
| } |
| } |
| |
| bool RenderWidgetHostViewAndroid::HasFocus() const { |
| if (!content_view_core_) |
| return false; // ContentViewCore not created yet. |
| |
| return content_view_core_->HasFocus(); |
| } |
| |
| bool RenderWidgetHostViewAndroid::IsSurfaceAvailableForCopy() const { |
| return !using_browser_compositor_ || HasValidFrame(); |
| } |
| |
| void RenderWidgetHostViewAndroid::Show() { |
| if (is_showing_) |
| return; |
| |
| is_showing_ = true; |
| ShowInternal(); |
| } |
| |
| void RenderWidgetHostViewAndroid::Hide() { |
| if (!is_showing_) |
| return; |
| |
| is_showing_ = false; |
| HideInternal(); |
| } |
| |
| 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 is_showing_ && content_view_core_; |
| } |
| |
| void RenderWidgetHostViewAndroid::LockCompositingSurface() { |
| DCHECK(HasValidFrame()); |
| DCHECK(host_); |
| DCHECK(frame_evictor_->HasFrame()); |
| frame_evictor_->LockFrame(); |
| locks_on_frame_count_++; |
| } |
| |
| void RenderWidgetHostViewAndroid::UnlockCompositingSurface() { |
| if (!frame_evictor_->HasFrame() || locks_on_frame_count_ == 0) |
| return; |
| |
| DCHECK(HasValidFrame()); |
| frame_evictor_->UnlockFrame(); |
| locks_on_frame_count_--; |
| |
| if (locks_on_frame_count_ == 0) { |
| if (last_frame_info_) { |
| InternalSwapCompositorFrame(last_frame_info_->output_surface_id, |
| std::move(last_frame_info_->frame)); |
| last_frame_info_.reset(); |
| } |
| |
| if (!is_showing_ && layer_.get()) |
| layer_->SetHideLayerAndSubtree(true); |
| } |
| } |
| |
| void RenderWidgetHostViewAndroid::SetTextSurroundingSelectionCallback( |
| const TextSurroundingSelectionCallback& callback) { |
| // Only one outstanding request is allowed at any given time. |
| DCHECK(!callback.is_null()); |
| text_surrounding_selection_callback_ = callback; |
| } |
| |
| void RenderWidgetHostViewAndroid::OnTextSurroundingSelectionResponse( |
| const base::string16& content, |
| size_t start_offset, |
| size_t end_offset) { |
| if (text_surrounding_selection_callback_.is_null()) |
| return; |
| text_surrounding_selection_callback_.Run(content, start_offset, end_offset); |
| text_surrounding_selection_callback_.Reset(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnShowUnhandledTapUIIfNeeded(int x_dip, |
| int y_dip) { |
| if (!content_view_core_) |
| return; |
| // Validate the coordinates are within the viewport. |
| gfx::Size viewport_size = content_view_core_->GetViewportSizeDip(); |
| if (x_dip < 0 || x_dip > viewport_size.width() || |
| y_dip < 0 || y_dip > viewport_size.height()) |
| return; |
| content_view_core_->OnShowUnhandledTapUIIfNeeded(x_dip, y_dip); |
| } |
| |
| void RenderWidgetHostViewAndroid::ReleaseLocksOnSurface() { |
| if (!frame_evictor_->HasFrame()) { |
| DCHECK_EQ(locks_on_frame_count_, 0u); |
| return; |
| } |
| while (locks_on_frame_count_ > 0) { |
| UnlockCompositingSurface(); |
| } |
| RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED); |
| } |
| |
| gfx::Rect RenderWidgetHostViewAndroid::GetViewBounds() const { |
| if (!content_view_core_) |
| return gfx::Rect(default_size_); |
| |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kEnableOSKOverscroll)) |
| return gfx::Rect(content_view_core_->GetViewSizeWithOSKHidden()); |
| |
| return gfx::Rect(content_view_core_->GetViewSize()); |
| } |
| |
| gfx::Size RenderWidgetHostViewAndroid::GetVisibleViewportSize() const { |
| if (!content_view_core_) |
| return gfx::Rect(default_size_).size(); |
| |
| return gfx::Rect(content_view_core_->GetViewSize()).size(); |
| } |
| |
| gfx::Size RenderWidgetHostViewAndroid::GetPhysicalBackingSize() const { |
| if (!content_view_core_) |
| return gfx::Size(); |
| |
| return content_view_core_->GetPhysicalBackingSize(); |
| } |
| |
| bool RenderWidgetHostViewAndroid::DoTopControlsShrinkBlinkSize() const { |
| if (!content_view_core_) |
| return false; |
| |
| // Whether or not Blink's viewport size should be shrunk by the height of the |
| // URL-bar. |
| return content_view_core_->DoTopControlsShrinkBlinkSize(); |
| } |
| |
| float RenderWidgetHostViewAndroid::GetTopControlsHeight() const { |
| if (!content_view_core_) |
| return 0.f; |
| |
| // The height of the top controls. |
| return content_view_core_->GetTopControlsHeightDip(); |
| } |
| |
| 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. |
| } |
| |
| long RenderWidgetHostViewAndroid::GetNativeImeAdapter() { |
| return reinterpret_cast<intptr_t>(&ime_adapter_android_); |
| } |
| |
| void RenderWidgetHostViewAndroid::TextInputStateChanged( |
| const ViewHostMsg_TextInputState_Params& params) { |
| if (params.is_non_ime_change) { |
| // Sends an acknowledgement to the renderer of a processed IME event. |
| host_->Send(new InputMsg_ImeEventAck(host_->GetRoutingID())); |
| } |
| |
| if (!content_view_core_) |
| return; |
| |
| content_view_core_->UpdateImeAdapter( |
| GetNativeImeAdapter(), |
| static_cast<int>(params.type), params.flags, |
| params.value, params.selection_start, params.selection_end, |
| params.composition_start, params.composition_end, |
| params.show_ime_if_needed, params.is_non_ime_change); |
| } |
| |
| void RenderWidgetHostViewAndroid::UpdateBackgroundColor(SkColor color) { |
| if (cached_background_color_ == color) |
| return; |
| |
| cached_background_color_ = color; |
| if (content_view_core_) |
| content_view_core_->OnBackgroundColorChanged(color); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrames(bool enabled) { |
| TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrames", |
| "enabled", enabled); |
| if (enabled) |
| RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME); |
| else |
| outstanding_vsync_requests_ &= ~PERSISTENT_BEGIN_FRAME; |
| } |
| |
| void RenderWidgetHostViewAndroid::OnStartContentIntent( |
| const GURL& content_url, bool is_main_frame) { |
| if (content_view_core_) |
| content_view_core_->StartContentIntent(content_url, is_main_frame); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnSmartClipDataExtracted( |
| const base::string16& text, |
| const base::string16& html, |
| const gfx::Rect rect) { |
| if (content_view_core_) |
| content_view_core_->OnSmartClipDataExtracted(text, html, rect); |
| } |
| |
| bool RenderWidgetHostViewAndroid::OnTouchEvent( |
| const ui::MotionEvent& event) { |
| if (!host_) |
| return false; |
| |
| // If a browser-based widget consumes the touch event, it's critical that |
| // touch event interception be disabled. This avoids issues with |
| // double-handling for embedder-detected gestures like side swipe. |
| if (selection_controller_ && |
| selection_controller_->WillHandleTouchEvent(event)) { |
| RequestDisallowInterceptTouchEvent(); |
| return true; |
| } |
| |
| if (stylus_text_selector_.OnTouchEvent(event)) { |
| RequestDisallowInterceptTouchEvent(); |
| return true; |
| } |
| |
| ui::FilteredGestureProvider::TouchHandlingResult result = |
| gesture_provider_.OnTouchEvent(event); |
| if (!result.succeeded) |
| return false; |
| |
| blink::WebTouchEvent web_event = ui::CreateWebTouchEventFromMotionEvent( |
| event, result.moved_beyond_slop_region); |
| host_->ForwardTouchEventWithLatencyInfo(web_event, ui::LatencyInfo()); |
| |
| // Send a proactive BeginFrame for this vsync to reduce scroll latency for |
| // scroll-inducing touch events. Note that Android's Choreographer ensures |
| // that BeginFrame requests made during ACTION_MOVE dispatch will be honored |
| // in the same vsync phase. |
| if (observing_root_window_ && result.moved_beyond_slop_region) |
| RequestVSyncUpdate(BEGIN_FRAME); |
| |
| return true; |
| } |
| |
| bool RenderWidgetHostViewAndroid::OnTouchHandleEvent( |
| const ui::MotionEvent& event) { |
| return selection_controller_ && |
| selection_controller_->WillHandleTouchEvent(event); |
| } |
| |
| void RenderWidgetHostViewAndroid::ResetGestureDetection() { |
| const ui::MotionEvent* current_down_event = |
| gesture_provider_.GetCurrentDownEvent(); |
| if (!current_down_event) { |
| // A hard reset ensures prevention of any timer-based events that might fire |
| // after a touch sequence has ended. |
| gesture_provider_.ResetDetection(); |
| return; |
| } |
| |
| scoped_ptr<ui::MotionEvent> cancel_event = current_down_event->Cancel(); |
| if (gesture_provider_.OnTouchEvent(*cancel_event).succeeded) { |
| bool causes_scrolling = false; |
| host_->ForwardTouchEventWithLatencyInfo( |
| ui::CreateWebTouchEventFromMotionEvent(*cancel_event, causes_scrolling), |
| ui::LatencyInfo()); |
| } |
| } |
| |
| void RenderWidgetHostViewAndroid::OnDidNavigateMainFrameToNewPage() { |
| ResetGestureDetection(); |
| } |
| |
| void RenderWidgetHostViewAndroid::SetDoubleTapSupportEnabled(bool enabled) { |
| gesture_provider_.SetDoubleTapSupportForPlatformEnabled(enabled); |
| } |
| |
| void RenderWidgetHostViewAndroid::SetMultiTouchZoomSupportEnabled( |
| bool enabled) { |
| gesture_provider_.SetMultiTouchZoomSupportEnabled(enabled); |
| } |
| |
| void RenderWidgetHostViewAndroid::ImeCancelComposition() { |
| ime_adapter_android_.CancelComposition(); |
| } |
| |
| void RenderWidgetHostViewAndroid::ImeCompositionRangeChanged( |
| const gfx::Range& range, |
| const std::vector<gfx::Rect>& character_bounds) { |
| std::vector<gfx::RectF> character_bounds_float; |
| for (const gfx::Rect& rect : character_bounds) { |
| character_bounds_float.emplace_back(rect); |
| } |
| ime_adapter_android_.SetCharacterBounds(character_bounds_float); |
| } |
| |
| void RenderWidgetHostViewAndroid::FocusedNodeChanged(bool is_editable_node) { |
| ime_adapter_android_.FocusedNodeChanged(is_editable_node); |
| } |
| |
| void RenderWidgetHostViewAndroid::RenderProcessGone( |
| base::TerminationStatus status, int error_code) { |
| Destroy(); |
| } |
| |
| void RenderWidgetHostViewAndroid::Destroy() { |
| RemoveLayers(); |
| SetContentViewCore(NULL); |
| |
| if (!surface_id_.is_null()) { |
| DCHECK(surface_factory_.get()); |
| surface_factory_->Destroy(surface_id_); |
| surface_id_ = cc::SurfaceId(); |
| } |
| surface_factory_.reset(); |
| |
| // 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 (!content_view_core_) |
| return; |
| if (range.is_empty()) { |
| content_view_core_->OnSelectionChanged(""); |
| return; |
| } |
| |
| DCHECK(!text.empty()); |
| 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 = base::UTF16ToUTF8(text.substr(pos, n)); |
| |
| content_view_core_->OnSelectionChanged(utf8_selection); |
| } |
| |
| void RenderWidgetHostViewAndroid::SelectionBoundsChanged( |
| const ViewHostMsg_SelectionBounds_Params& params) { |
| NOTREACHED() << "Selection bounds should be routed through the compositor."; |
| } |
| |
| void RenderWidgetHostViewAndroid::SetBackgroundColor(SkColor color) { |
| RenderWidgetHostViewBase::SetBackgroundColor(color); |
| host_->SetBackgroundOpaque(GetBackgroundOpaque()); |
| UpdateBackgroundColor(color); |
| } |
| |
| void RenderWidgetHostViewAndroid::CopyFromCompositingSurface( |
| const gfx::Rect& src_subrect, |
| const gfx::Size& dst_size, |
| const ReadbackRequestCallback& callback, |
| const SkColorType preferred_color_type) { |
| TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurface"); |
| if (!host_ || host_->is_hidden()) { |
| callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE); |
| return; |
| } |
| base::TimeTicks start_time = base::TimeTicks::Now(); |
| if (!IsSurfaceAvailableForCopy()) { |
| callback.Run(SkBitmap(), READBACK_SURFACE_UNAVAILABLE); |
| return; |
| } |
| const gfx::Display& display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); |
| float device_scale_factor = display.device_scale_factor(); |
| gfx::Size dst_size_in_pixel = |
| gfx::ConvertRectToPixel(device_scale_factor, gfx::Rect(dst_size)).size(); |
| gfx::Rect src_subrect_in_pixel = |
| gfx::ConvertRectToPixel(device_scale_factor, src_subrect); |
| |
| if (!using_browser_compositor_) { |
| SynchronousCopyContents(src_subrect_in_pixel, dst_size_in_pixel, callback, |
| preferred_color_type); |
| UMA_HISTOGRAM_TIMES("Compositing.CopyFromSurfaceTimeSynchronous", |
| base::TimeTicks::Now() - start_time); |
| return; |
| } |
| |
| if (!content_view_core_ || !(content_view_core_->GetWindowAndroid())) { |
| callback.Run(SkBitmap(), READBACK_FAILED); |
| return; |
| } |
| ui::WindowAndroidCompositor* compositor = |
| content_view_core_->GetWindowAndroid()->GetCompositor(); |
| DCHECK(compositor); |
| DCHECK(!surface_id_.is_null()); |
| scoped_ptr<cc::CopyOutputRequest> request = |
| cc::CopyOutputRequest::CreateRequest( |
| base::Bind(&PrepareTextureCopyOutputResult, |
| dst_size_in_pixel, preferred_color_type, start_time, |
| callback)); |
| if (!src_subrect_in_pixel.IsEmpty()) |
| request->set_area(src_subrect_in_pixel); |
| layer_->RequestCopyOfOutput(std::move(request)); |
| } |
| |
| void RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceToVideoFrame( |
| const gfx::Rect& src_subrect, |
| const scoped_refptr<media::VideoFrame>& target, |
| const base::Callback<void(const gfx::Rect&, bool)>& callback) { |
| NOTIMPLEMENTED(); |
| callback.Run(gfx::Rect(), false); |
| } |
| |
| bool RenderWidgetHostViewAndroid::CanCopyToVideoFrame() const { |
| return false; |
| } |
| |
| void RenderWidgetHostViewAndroid::ShowDisambiguationPopup( |
| const gfx::Rect& rect_pixels, const SkBitmap& zoomed_bitmap) { |
| if (!content_view_core_) |
| return; |
| |
| content_view_core_->ShowDisambiguationPopup(rect_pixels, zoomed_bitmap); |
| } |
| |
| scoped_ptr<SyntheticGestureTarget> |
| RenderWidgetHostViewAndroid::CreateSyntheticGestureTarget() { |
| return scoped_ptr<SyntheticGestureTarget>(new SyntheticGestureTargetAndroid( |
| host_, content_view_core_->CreateMotionEventSynthesizer())); |
| } |
| |
| void RenderWidgetHostViewAndroid::SendDelegatedFrameAck( |
| uint32_t output_surface_id) { |
| DCHECK(host_); |
| cc::CompositorFrameAck ack; |
| if (!surface_returned_resources_.empty()) |
| ack.resources.swap(surface_returned_resources_); |
| host_->Send(new ViewMsg_SwapCompositorFrameAck(host_->GetRoutingID(), |
| output_surface_id, ack)); |
| } |
| |
| void RenderWidgetHostViewAndroid::SendReturnedDelegatedResources( |
| uint32_t output_surface_id) { |
| DCHECK(host_); |
| cc::CompositorFrameAck ack; |
| DCHECK(!surface_returned_resources_.empty()); |
| ack.resources.swap(surface_returned_resources_); |
| |
| host_->Send(new ViewMsg_ReclaimCompositorResources(host_->GetRoutingID(), |
| output_surface_id, ack)); |
| } |
| |
| void RenderWidgetHostViewAndroid::ReturnResources( |
| const cc::ReturnedResourceArray& resources) { |
| if (resources.empty()) |
| return; |
| std::copy(resources.begin(), resources.end(), |
| std::back_inserter(surface_returned_resources_)); |
| if (!ack_callbacks_.size()) |
| SendReturnedDelegatedResources(last_output_surface_id_); |
| } |
| |
| void RenderWidgetHostViewAndroid::SetBeginFrameSource( |
| cc::BeginFrameSource* begin_frame_source) { |
| // TODO(tansell): Hook this up. |
| } |
| |
| void RenderWidgetHostViewAndroid::DestroyDelegatedContent() { |
| RemoveLayers(); |
| if (!surface_id_.is_null()) { |
| DCHECK(surface_factory_.get()); |
| surface_factory_->Destroy(surface_id_); |
| surface_id_ = cc::SurfaceId(); |
| } |
| layer_ = NULL; |
| } |
| |
| void RenderWidgetHostViewAndroid::CheckOutputSurfaceChanged( |
| uint32_t output_surface_id) { |
| if (output_surface_id == last_output_surface_id_) |
| return; |
| DestroyDelegatedContent(); |
| surface_factory_.reset(); |
| if (!surface_returned_resources_.empty()) |
| SendReturnedDelegatedResources(last_output_surface_id_); |
| |
| last_output_surface_id_ = output_surface_id; |
| } |
| |
| void RenderWidgetHostViewAndroid::SubmitCompositorFrame( |
| scoped_ptr<cc::CompositorFrame> frame) { |
| cc::SurfaceManager* manager = CompositorImpl::GetSurfaceManager(); |
| if (!surface_factory_) { |
| surface_factory_ = make_scoped_ptr(new cc::SurfaceFactory(manager, this)); |
| } |
| if (surface_id_.is_null() || |
| texture_size_in_layer_ != current_surface_size_ || |
| location_bar_content_translation_ != |
| frame->metadata.location_bar_content_translation || |
| current_viewport_selection_ != frame->metadata.selection) { |
| RemoveLayers(); |
| if (!surface_id_.is_null()) |
| surface_factory_->Destroy(surface_id_); |
| surface_id_ = id_allocator_->GenerateId(); |
| surface_factory_->Create(surface_id_); |
| layer_ = CreateDelegatedLayer(); |
| |
| DCHECK(layer_); |
| |
| current_surface_size_ = texture_size_in_layer_; |
| location_bar_content_translation_ = |
| frame->metadata.location_bar_content_translation; |
| current_viewport_selection_ = frame->metadata.selection; |
| AttachLayers(); |
| } |
| |
| cc::SurfaceFactory::DrawCallback ack_callback = |
| base::Bind(&RenderWidgetHostViewAndroid::RunAckCallbacks, |
| weak_ptr_factory_.GetWeakPtr()); |
| surface_factory_->SubmitCompositorFrame(surface_id_, std::move(frame), |
| ack_callback); |
| } |
| |
| void RenderWidgetHostViewAndroid::SwapDelegatedFrame( |
| uint32_t output_surface_id, |
| scoped_ptr<cc::CompositorFrame> frame) { |
| CheckOutputSurfaceChanged(output_surface_id); |
| bool has_content = !texture_size_in_layer_.IsEmpty(); |
| |
| // DelegatedRendererLayerImpl applies the inverse device_scale_factor of the |
| // renderer frame, assuming that the browser compositor will scale |
| // it back up to device scale. But on Android we put our browser layers in |
| // physical pixels and set our browser CC device_scale_factor to 1, so this |
| // suppresses the transform. This line may need to be removed when fixing |
| // http://crbug.com/384134 or http://crbug.com/310763 |
| frame->delegated_frame_data->device_scale_factor = 1.0f; |
| |
| base::Closure ack_callback = |
| base::Bind(&RenderWidgetHostViewAndroid::SendDelegatedFrameAck, |
| weak_ptr_factory_.GetWeakPtr(), |
| output_surface_id); |
| |
| ack_callbacks_.push(ack_callback); |
| |
| if (!has_content) { |
| DestroyDelegatedContent(); |
| } else { |
| SubmitCompositorFrame(std::move(frame)); |
| } |
| |
| if (layer_.get()) { |
| layer_->SetIsDrawable(true); |
| layer_->SetContentsOpaque(true); |
| layer_->SetBounds(texture_size_in_layer_); |
| } |
| |
| if (host_->is_hidden()) |
| RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED); |
| } |
| |
| void RenderWidgetHostViewAndroid::InternalSwapCompositorFrame( |
| uint32_t output_surface_id, |
| scoped_ptr<cc::CompositorFrame> frame) { |
| last_scroll_offset_ = frame->metadata.root_scroll_offset; |
| if (!frame->delegated_frame_data) { |
| LOG(ERROR) << "Non-delegated renderer path no longer supported"; |
| return; |
| } |
| |
| if (locks_on_frame_count_ > 0) { |
| DCHECK(HasValidFrame()); |
| RetainFrame(output_surface_id, std::move(frame)); |
| return; |
| } |
| |
| if (!CompositorImpl::GetSurfaceManager() && layer_.get() && |
| layer_->layer_tree_host()) { |
| for (size_t i = 0; i < frame->metadata.latency_info.size(); i++) { |
| scoped_ptr<cc::SwapPromise> swap_promise( |
| new cc::LatencyInfoSwapPromise(frame->metadata.latency_info[i])); |
| layer_->layer_tree_host()->QueueSwapPromise(std::move(swap_promise)); |
| } |
| } |
| |
| DCHECK(!frame->delegated_frame_data->render_pass_list.empty()); |
| |
| cc::RenderPass* root_pass = |
| frame->delegated_frame_data->render_pass_list.back().get(); |
| texture_size_in_layer_ = root_pass->output_rect.size(); |
| |
| cc::CompositorFrameMetadata metadata = frame->metadata; |
| |
| SwapDelegatedFrame(output_surface_id, std::move(frame)); |
| frame_evictor_->SwappedFrame(!host_->is_hidden()); |
| |
| // As the metadata update may trigger view invalidation, always call it after |
| // any potential compositor scheduling. |
| OnFrameMetadataUpdated(metadata); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnSwapCompositorFrame( |
| uint32_t output_surface_id, |
| scoped_ptr<cc::CompositorFrame> frame) { |
| InternalSwapCompositorFrame(output_surface_id, std::move(frame)); |
| } |
| |
| void RenderWidgetHostViewAndroid::ClearCompositorFrame() { |
| DestroyDelegatedContent(); |
| } |
| |
| void RenderWidgetHostViewAndroid::RetainFrame( |
| uint32_t output_surface_id, |
| scoped_ptr<cc::CompositorFrame> frame) { |
| DCHECK(locks_on_frame_count_); |
| |
| // Store the incoming frame so that it can be swapped when all the locks have |
| // been released. If there is already a stored frame, then replace and skip |
| // the previous one but make sure we still eventually send the ACK. Holding |
| // the ACK also blocks the renderer when its max_frames_pending is reached. |
| if (last_frame_info_) { |
| base::Closure ack_callback = |
| base::Bind(&RenderWidgetHostViewAndroid::SendDelegatedFrameAck, |
| weak_ptr_factory_.GetWeakPtr(), |
| last_frame_info_->output_surface_id); |
| |
| ack_callbacks_.push(ack_callback); |
| } |
| |
| last_frame_info_.reset( |
| new LastFrameInfo(output_surface_id, std::move(frame))); |
| } |
| |
| SynchronousCompositorBase* |
| RenderWidgetHostViewAndroid::GetSynchronousCompositor() { |
| return sync_compositor_.get(); |
| } |
| |
| void RenderWidgetHostViewAndroid::SynchronousFrameMetadata( |
| const cc::CompositorFrameMetadata& frame_metadata) { |
| if (!content_view_core_) |
| return; |
| |
| // This is a subset of OnSwapCompositorFrame() used in the synchronous |
| // compositor flow. |
| OnFrameMetadataUpdated(frame_metadata); |
| |
| // DevTools ScreenCast support for Android WebView. |
| WebContents* web_contents = content_view_core_->GetWebContents(); |
| if (DevToolsAgentHost::HasFor(web_contents)) { |
| scoped_refptr<DevToolsAgentHost> dtah = |
| DevToolsAgentHost::GetOrCreateFor(web_contents); |
| // Unblock the compositor. |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind( |
| &RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame, |
| static_cast<RenderFrameDevToolsAgentHost*>(dtah.get()), |
| frame_metadata)); |
| } |
| } |
| |
| void RenderWidgetHostViewAndroid::SetOverlayVideoMode(bool enabled) { |
| if (layer_.get()) |
| layer_->SetContentsOpaque(!enabled); |
| } |
| |
| bool RenderWidgetHostViewAndroid::SupportsAnimation() const { |
| // The synchronous (WebView) compositor does not have a proper browser |
| // compositor with which to drive animations. |
| return using_browser_compositor_; |
| } |
| |
| void RenderWidgetHostViewAndroid::SetNeedsAnimate() { |
| DCHECK(content_view_core_ && content_view_core_->GetWindowAndroid()); |
| DCHECK(using_browser_compositor_); |
| content_view_core_->GetWindowAndroid()->SetNeedsAnimate(); |
| } |
| |
| void RenderWidgetHostViewAndroid::MoveCaret(const gfx::PointF& position) { |
| MoveCaret(gfx::Point(position.x(), position.y())); |
| } |
| |
| void RenderWidgetHostViewAndroid::MoveRangeSelectionExtent( |
| const gfx::PointF& extent) { |
| DCHECK(content_view_core_); |
| content_view_core_->MoveRangeSelectionExtent(extent); |
| } |
| |
| void RenderWidgetHostViewAndroid::SelectBetweenCoordinates( |
| const gfx::PointF& base, |
| const gfx::PointF& extent) { |
| DCHECK(content_view_core_); |
| content_view_core_->SelectBetweenCoordinates(base, extent); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnSelectionEvent( |
| ui::SelectionEventType event) { |
| DCHECK(content_view_core_); |
| DCHECK(selection_controller_); |
| // If a selection drag has started, it has taken over the active touch |
| // sequence. Immediately cancel gesture detection and any downstream touch |
| // listeners (e.g., web content) to communicate this transfer. |
| if (event == ui::SELECTION_HANDLES_SHOWN && |
| gesture_provider_.GetCurrentDownEvent()) { |
| ResetGestureDetection(); |
| } |
| content_view_core_->OnSelectionEvent( |
| event, selection_controller_->GetStartPosition(), |
| GetSelectionRect(*selection_controller_)); |
| } |
| |
| scoped_ptr<ui::TouchHandleDrawable> |
| RenderWidgetHostViewAndroid::CreateDrawable() { |
| DCHECK(content_view_core_); |
| if (!using_browser_compositor_) |
| return PopupTouchHandleDrawable::Create(content_view_core_); |
| |
| return scoped_ptr<ui::TouchHandleDrawable>(new CompositedTouchHandleDrawable( |
| content_view_core_->GetLayer().get(), |
| content_view_core_->GetDpiScale(), |
| // Use the activity context (instead of the application context) to ensure |
| // proper handle theming. |
| content_view_core_->GetContext().obj())); |
| } |
| |
| void RenderWidgetHostViewAndroid::SynchronousCopyContents( |
| const gfx::Rect& src_subrect_in_pixel, |
| const gfx::Size& dst_size_in_pixel, |
| const ReadbackRequestCallback& callback, |
| const SkColorType color_type) { |
| gfx::Size input_size_in_pixel; |
| if (src_subrect_in_pixel.IsEmpty()) |
| input_size_in_pixel = texture_size_in_layer_; |
| else |
| input_size_in_pixel = src_subrect_in_pixel.size(); |
| |
| gfx::Size output_size_in_pixel; |
| if (dst_size_in_pixel.IsEmpty()) |
| output_size_in_pixel = input_size_in_pixel; |
| else |
| output_size_in_pixel = dst_size_in_pixel; |
| int output_width = output_size_in_pixel.width(); |
| int output_height = output_size_in_pixel.height(); |
| |
| if (!sync_compositor_) { |
| callback.Run(SkBitmap(), READBACK_FAILED); |
| return; |
| } |
| |
| SkBitmap bitmap; |
| bitmap.allocPixels(SkImageInfo::Make(output_width, |
| output_height, |
| color_type, |
| kPremul_SkAlphaType)); |
| SkCanvas canvas(bitmap); |
| canvas.scale( |
| (float)output_width / (float)input_size_in_pixel.width(), |
| (float)output_height / (float)input_size_in_pixel.height()); |
| sync_compositor_->DemandDrawSw(&canvas); |
| callback.Run(bitmap, READBACK_SUCCESS); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnFrameMetadataUpdated( |
| const cc::CompositorFrameMetadata& frame_metadata) { |
| bool is_mobile_optimized = IsMobileOptimizedFrame(frame_metadata); |
| gesture_provider_.SetDoubleTapSupportForPageEnabled(!is_mobile_optimized); |
| |
| if (!content_view_core_) |
| return; |
| |
| if (overscroll_controller_) |
| overscroll_controller_->OnFrameMetadataUpdated(frame_metadata); |
| |
| if (selection_controller_) { |
| selection_controller_->OnSelectionEditable( |
| frame_metadata.selection.is_editable); |
| selection_controller_->OnSelectionEmpty( |
| frame_metadata.selection.is_empty_text_form_control); |
| selection_controller_->OnSelectionBoundsChanged( |
| ConvertSelectionBound(frame_metadata.selection.start), |
| ConvertSelectionBound(frame_metadata.selection.end)); |
| |
| // Set parameters for adaptive handle orientation. |
| gfx::SizeF viewport_size(frame_metadata.scrollable_viewport_size); |
| viewport_size.Scale(frame_metadata.page_scale_factor); |
| gfx::RectF viewport_rect( |
| frame_metadata.location_bar_content_translation.x(), |
| frame_metadata.location_bar_content_translation.y(), |
| viewport_size.width(), viewport_size.height()); |
| selection_controller_->OnViewportChanged(viewport_rect); |
| } |
| |
| UpdateBackgroundColor(frame_metadata.root_background_color); |
| |
| // 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.scrollable_viewport_size, |
| frame_metadata.location_bar_offset, |
| frame_metadata.location_bar_content_translation, |
| is_mobile_optimized, |
| frame_metadata.selection.start); |
| #if defined(VIDEO_HOLE) |
| if (host_) { |
| WebContents* web_contents = |
| WebContents::FromRenderViewHost(RenderViewHostImpl::From(host_)); |
| if (web_contents) { |
| MediaWebContentsObserverAndroid::FromWebContents(web_contents) |
| ->OnFrameInfoUpdated(); |
| } |
| } |
| #endif // defined(VIDEO_HOLE) |
| } |
| |
| void RenderWidgetHostViewAndroid::ShowInternal() { |
| bool show = is_showing_ && is_window_activity_started_ && is_window_visible_; |
| if (!show) |
| return; |
| |
| if (!host_ || !host_->is_hidden()) |
| return; |
| |
| if (layer_.get()) |
| layer_->SetHideLayerAndSubtree(false); |
| |
| frame_evictor_->SetVisible(true); |
| |
| if (overscroll_controller_) |
| overscroll_controller_->Enable(); |
| |
| host_->WasShown(ui::LatencyInfo()); |
| |
| if (content_view_core_) { |
| StartObservingRootWindow(); |
| RequestVSyncUpdate(BEGIN_FRAME); |
| } |
| } |
| |
| void RenderWidgetHostViewAndroid::HideInternal() { |
| DCHECK(!is_showing_ || !is_window_activity_started_ || !is_window_visible_) |
| << "Hide called when the widget should be shown."; |
| |
| // Only preserve the frontbuffer if the activity was stopped while the |
| // window is still visible. This avoids visual artificts when transitioning |
| // between activities. |
| bool hide_frontbuffer = is_window_activity_started_ || !is_window_visible_; |
| |
| // Only stop observing the root window if the widget has been explicitly |
| // hidden and the frontbuffer is being cleared. This allows window visibility |
| // notifications to eventually clear the frontbuffer. |
| bool stop_observing_root_window = !is_showing_ && hide_frontbuffer; |
| |
| if (hide_frontbuffer) { |
| if (layer_.get() && locks_on_frame_count_ == 0) |
| layer_->SetHideLayerAndSubtree(true); |
| |
| frame_evictor_->SetVisible(false); |
| } |
| |
| if (stop_observing_root_window) { |
| DCHECK(!is_showing_); |
| StopObservingRootWindow(); |
| } |
| |
| if (!host_ || host_->is_hidden()) |
| return; |
| |
| if (overscroll_controller_) |
| overscroll_controller_->Disable(); |
| |
| RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED); |
| |
| // Inform the renderer that we are being hidden so it can reduce its resource |
| // utilization. |
| host_->WasHidden(); |
| } |
| |
| void RenderWidgetHostViewAndroid::AttachLayers() { |
| if (!content_view_core_) |
| return; |
| if (!layer_.get()) |
| return; |
| |
| content_view_core_->AttachLayer(layer_); |
| layer_->SetHideLayerAndSubtree(!is_showing_); |
| } |
| |
| void RenderWidgetHostViewAndroid::RemoveLayers() { |
| if (!content_view_core_) |
| return; |
| |
| if (!layer_.get()) |
| return; |
| |
| content_view_core_->RemoveLayer(layer_); |
| } |
| |
| void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32_t requests) { |
| bool should_request_vsync = !outstanding_vsync_requests_ && requests; |
| outstanding_vsync_requests_ |= requests; |
| |
| // If the host has been hidden, defer vsync requests until it is shown |
| // again via |Show()|. |
| if (!host_ || host_->is_hidden()) |
| return; |
| |
| // Note that if we're not currently observing the root window, outstanding |
| // vsync requests will be pushed if/when we resume observing in |
| // |StartObservingRootWindow()|. |
| if (observing_root_window_ && should_request_vsync) |
| content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); |
| } |
| |
| void RenderWidgetHostViewAndroid::StartObservingRootWindow() { |
| DCHECK(content_view_core_); |
| // TODO(yusufo): This will need to have a better fallback for cases where |
| // setContentViewCore is called with a valid ContentViewCore without a window. |
| DCHECK(content_view_core_->GetWindowAndroid()); |
| DCHECK(is_showing_); |
| if (observing_root_window_) |
| return; |
| |
| observing_root_window_ = true; |
| content_view_core_->GetWindowAndroid()->AddObserver(this); |
| |
| // Clear existing vsync requests to allow a request to the new window. |
| uint32_t outstanding_vsync_requests = outstanding_vsync_requests_; |
| outstanding_vsync_requests_ = 0; |
| RequestVSyncUpdate(outstanding_vsync_requests); |
| } |
| |
| void RenderWidgetHostViewAndroid::StopObservingRootWindow() { |
| if (!content_view_core_ || !(content_view_core_->GetWindowAndroid())) { |
| DCHECK(!observing_root_window_); |
| return; |
| } |
| |
| if (!observing_root_window_) |
| return; |
| |
| // Reset window state variables to their defaults. |
| is_window_activity_started_ = true; |
| is_window_visible_ = true; |
| observing_root_window_ = false; |
| content_view_core_->GetWindowAndroid()->RemoveObserver(this); |
| } |
| |
| void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time, |
| base::TimeDelta vsync_period) { |
| TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame", |
| "frame_time_us", frame_time.ToInternalValue()); |
| |
| if (using_browser_compositor_) { |
| // TODO(brianderson): Replace this hardcoded deadline after Android |
| // switches to Surfaces and the Browser's commit isn't in the critcal path. |
| base::TimeTicks deadline = frame_time + (vsync_period * 0.6); |
| |
| host_->Send(new ViewMsg_BeginFrame( |
| host_->GetRoutingID(), |
| cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, |
| vsync_period, cc::BeginFrameArgs::NORMAL))); |
| } else if (sync_compositor_) { |
| // The synchronous compositor synchronously does it's work in this call. |
| // It does not use a deadline. |
| sync_compositor_->BeginFrame(cc::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, frame_time, base::TimeTicks(), vsync_period, |
| cc::BeginFrameArgs::NORMAL)); |
| } |
| } |
| |
| bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) { |
| bool needs_animate = false; |
| if (overscroll_controller_) { |
| needs_animate |= overscroll_controller_->Animate( |
| frame_time, content_view_core_->GetLayer().get()); |
| } |
| if (selection_controller_) |
| needs_animate |= selection_controller_->Animate(frame_time); |
| return needs_animate; |
| } |
| |
| void RenderWidgetHostViewAndroid::RequestDisallowInterceptTouchEvent() { |
| if (content_view_core_) |
| content_view_core_->RequestDisallowInterceptTouchEvent(); |
| } |
| |
| void RenderWidgetHostViewAndroid::EvictDelegatedFrame() { |
| if (layer_.get()) |
| DestroyDelegatedContent(); |
| frame_evictor_->DiscardedFrame(); |
| } |
| |
| 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); |
| } |
| |
| bool RenderWidgetHostViewAndroid::GetScreenColorProfile( |
| std::vector<char>* color_profile) { |
| DCHECK(color_profile->empty()); |
| NOTREACHED(); |
| return false; |
| } |
| |
| // 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(); |
| } |
| |
| void RenderWidgetHostViewAndroid::ProcessAckedTouchEvent( |
| const TouchEventWithLatencyInfo& touch, InputEventAckState ack_result) { |
| const bool event_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED; |
| gesture_provider_.OnTouchEventAck(touch.event.uniqueTouchEventId, |
| event_consumed); |
| } |
| |
| void RenderWidgetHostViewAndroid::GestureEventAck( |
| const blink::WebGestureEvent& event, |
| InputEventAckState ack_result) { |
| if (overscroll_controller_) |
| overscroll_controller_->OnGestureEventAck(event, ack_result); |
| |
| if (content_view_core_) |
| content_view_core_->OnGestureEventAck(event, ack_result); |
| } |
| |
| InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent( |
| const blink::WebInputEvent& input_event) { |
| if (selection_controller_ && |
| blink::WebInputEvent::isGestureEventType(input_event.type)) { |
| const blink::WebGestureEvent& gesture_event = |
| static_cast<const blink::WebGestureEvent&>(input_event); |
| switch (gesture_event.type) { |
| case blink::WebInputEvent::GestureLongPress: |
| if (selection_controller_->WillHandleLongPressEvent( |
| base::TimeTicks() + |
| base::TimeDelta::FromSecondsD(input_event.timeStampSeconds), |
| gfx::PointF(gesture_event.x, gesture_event.y))) { |
| return INPUT_EVENT_ACK_STATE_CONSUMED; |
| } |
| break; |
| |
| case blink::WebInputEvent::GestureTap: |
| if (selection_controller_->WillHandleTapEvent( |
| gfx::PointF(gesture_event.x, gesture_event.y), |
| gesture_event.data.tap.tapCount)) { |
| return INPUT_EVENT_ACK_STATE_CONSUMED; |
| } |
| break; |
| |
| case blink::WebInputEvent::GestureScrollBegin: |
| selection_controller_->OnScrollBeginEvent(); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| if (overscroll_controller_ && |
| blink::WebInputEvent::isGestureEventType(input_event.type) && |
| overscroll_controller_->WillHandleGestureEvent( |
| static_cast<const blink::WebGestureEvent&>(input_event))) { |
| return INPUT_EVENT_ACK_STATE_CONSUMED; |
| } |
| |
| if (content_view_core_ && content_view_core_->FilterInputEvent(input_event)) |
| return INPUT_EVENT_ACK_STATE_CONSUMED; |
| |
| 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 && |
| gpu_data->IsDriverBugWorkaroundActive(gpu::WAKE_UP_GPU_BEFORE_DRAWING)) |
| shim->Send(new GpuMsg_WakeUpGpu); |
| } |
| |
| if (sync_compositor_) |
| return sync_compositor_->HandleInputEvent(input_event); |
| return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
| } |
| |
| void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() { |
| TRACE_EVENT0("input", "RenderWidgetHostViewAndroid::OnSetNeedsFlushInput"); |
| RequestVSyncUpdate(FLUSH_INPUT); |
| } |
| |
| BrowserAccessibilityManager* |
| RenderWidgetHostViewAndroid::CreateBrowserAccessibilityManager( |
| BrowserAccessibilityDelegate* delegate) { |
| base::android::ScopedJavaLocalRef<jobject> obj; |
| if (host_ && |
| host_->GetRootBrowserAccessibilityManager() && |
| content_view_core_) { |
| obj = content_view_core_->GetJavaObject(); |
| } |
| return new BrowserAccessibilityManagerAndroid( |
| obj, |
| BrowserAccessibilityManagerAndroid::GetEmptyDocument(), |
| delegate); |
| } |
| |
| 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_) |
| return; |
| |
| RenderWidgetHostImpl* target_host = host_; |
| |
| // If there are multiple widgets on the page (such as when there are |
| // out-of-process iframes), pick the one that should process this event. |
| if (host_->delegate()) |
| target_host = host_->delegate()->GetFocusedRenderWidgetHost(host_); |
| if (!target_host) |
| return; |
| |
| target_host->ForwardKeyboardEvent(event); |
| } |
| |
| 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_controller_) |
| overscroll_controller_->Enable(); |
| |
| if (host_) |
| host_->ForwardGestureEventWithLatencyInfo(event, ui::LatencyInfo()); |
| } |
| |
| void RenderWidgetHostViewAndroid::MoveCaret(const gfx::Point& point) { |
| if (host_) |
| host_->MoveCaret(point); |
| } |
| |
| void RenderWidgetHostViewAndroid::DismissTextHandles() { |
| if (selection_controller_) |
| selection_controller_->HideAndDisallowShowingAutomatically(); |
| } |
| |
| void RenderWidgetHostViewAndroid::SetTextHandlesTemporarilyHidden(bool hidden) { |
| if (selection_controller_) |
| selection_controller_->SetTemporarilyHidden(hidden); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnShowingPastePopup( |
| const gfx::PointF& point) { |
| if (!selection_controller_) |
| return; |
| |
| // As the paste popup may be triggered *before* the bounds and editability |
| // of the region have been updated, explicitly set the properties now. |
| // TODO(jdduke): Remove this workaround when auxiliary paste popup |
| // notifications are no longer required, crbug.com/398170. |
| ui::SelectionBound insertion_bound; |
| insertion_bound.set_type(ui::SelectionBound::CENTER); |
| insertion_bound.set_visible(true); |
| insertion_bound.SetEdge(point, point); |
| selection_controller_->HideAndDisallowShowingAutomatically(); |
| selection_controller_->OnSelectionEditable(true); |
| selection_controller_->OnSelectionEmpty(true); |
| selection_controller_->OnSelectionBoundsChanged(insertion_bound, |
| insertion_bound); |
| selection_controller_->AllowShowingFromCurrentSelection(); |
| } |
| |
| SkColor RenderWidgetHostViewAndroid::GetCachedBackgroundColor() const { |
| return cached_background_color_; |
| } |
| |
| void RenderWidgetHostViewAndroid::DidOverscroll( |
| const DidOverscrollParams& params) { |
| if (sync_compositor_) |
| sync_compositor_->DidOverscroll(params); |
| |
| if (!content_view_core_ || !layer_.get() || !is_showing_) |
| return; |
| |
| if (overscroll_controller_) |
| overscroll_controller_->OnOverscrolled(params); |
| } |
| |
| void RenderWidgetHostViewAndroid::DidStopFlinging() { |
| if (content_view_core_) |
| content_view_core_->DidStopFlinging(); |
| } |
| |
| uint32_t RenderWidgetHostViewAndroid::GetSurfaceIdNamespace() { |
| if (id_allocator_) |
| return id_allocator_->id_namespace(); |
| return 0; |
| } |
| |
| void RenderWidgetHostViewAndroid::SetContentViewCore( |
| ContentViewCoreImpl* content_view_core) { |
| DCHECK(!content_view_core || !content_view_core_ || |
| (content_view_core_ == content_view_core)); |
| RemoveLayers(); |
| StopObservingRootWindow(); |
| |
| bool resize = false; |
| if (content_view_core != content_view_core_) { |
| overscroll_controller_.reset(); |
| selection_controller_.reset(); |
| ReleaseLocksOnSurface(); |
| resize = true; |
| if (content_view_core_) |
| content_view_core_->RemoveObserver(this); |
| if (content_view_core) |
| content_view_core->AddObserver(this); |
| } |
| |
| content_view_core_ = content_view_core; |
| |
| BrowserAccessibilityManager* manager = NULL; |
| if (host_) |
| manager = host_->GetRootBrowserAccessibilityManager(); |
| if (manager) { |
| base::android::ScopedJavaLocalRef<jobject> obj; |
| if (content_view_core_) |
| obj = content_view_core_->GetJavaObject(); |
| manager->ToBrowserAccessibilityManagerAndroid()->SetContentViewCore(obj); |
| } |
| |
| AttachLayers(); |
| if (!content_view_core_) { |
| sync_compositor_.reset(); |
| return; |
| } |
| |
| if (is_showing_) |
| StartObservingRootWindow(); |
| |
| if (resize) |
| WasResized(); |
| |
| if (!selection_controller_) |
| selection_controller_ = CreateSelectionController(this, content_view_core_); |
| |
| if (!overscroll_controller_ && |
| content_view_core_->GetWindowAndroid()->GetCompositor()) { |
| overscroll_controller_ = CreateOverscrollController(content_view_core_); |
| } |
| |
| if (!sync_compositor_) { |
| sync_compositor_ = SynchronousCompositorBase::Create( |
| this, content_view_core_->GetWebContents()); |
| } |
| if (sync_compositor_) |
| sync_compositor_->DidBecomeCurrent(); |
| } |
| |
| void RenderWidgetHostViewAndroid::RunAckCallbacks( |
| cc::SurfaceDrawStatus status) { |
| while (!ack_callbacks_.empty()) { |
| ack_callbacks_.front().Run(); |
| ack_callbacks_.pop(); |
| } |
| } |
| |
| void RenderWidgetHostViewAndroid::OnGestureEvent( |
| const ui::GestureEventData& gesture) { |
| blink::WebGestureEvent web_gesture = |
| ui::CreateWebGestureEventFromGestureEventData(gesture); |
| // TODO(jdduke): Remove this workaround after Android fixes UiAutomator to |
| // stop providing shift meta values to synthetic MotionEvents. This prevents |
| // unintended shift+click interpretation of all accessibility clicks. |
| // See crbug.com/443247. |
| if (web_gesture.type == blink::WebInputEvent::GestureTap && |
| web_gesture.modifiers == blink::WebInputEvent::ShiftKey) { |
| web_gesture.modifiers = 0; |
| } |
| SendGestureEvent(web_gesture); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnContentViewCoreDestroyed() { |
| SetContentViewCore(NULL); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnCompositingDidCommit() { |
| RunAckCallbacks(cc::SurfaceDrawStatus::DRAWN); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnRootWindowVisibilityChanged(bool visible) { |
| TRACE_EVENT1("browser", |
| "RenderWidgetHostViewAndroid::OnRootWindowVisibilityChanged", |
| "visible", visible); |
| DCHECK(observing_root_window_); |
| if (is_window_visible_ == visible) |
| return; |
| |
| is_window_visible_ = visible; |
| |
| if (visible) |
| ShowInternal(); |
| else |
| HideInternal(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnAttachedToWindow() { |
| if (is_showing_) |
| StartObservingRootWindow(); |
| DCHECK(content_view_core_ && content_view_core_->GetWindowAndroid()); |
| if (content_view_core_->GetWindowAndroid()->GetCompositor()) |
| OnAttachCompositor(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnDetachedFromWindow() { |
| StopObservingRootWindow(); |
| OnDetachCompositor(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnAttachCompositor() { |
| DCHECK(content_view_core_); |
| if (!overscroll_controller_) |
| overscroll_controller_ = CreateOverscrollController(content_view_core_); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnDetachCompositor() { |
| DCHECK(content_view_core_); |
| DCHECK(using_browser_compositor_); |
| RunAckCallbacks(cc::SurfaceDrawStatus::DRAW_SKIPPED); |
| overscroll_controller_.reset(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time, |
| base::TimeDelta vsync_period) { |
| TRACE_EVENT0("cc,benchmark", "RenderWidgetHostViewAndroid::OnVSync"); |
| if (!host_ || host_->is_hidden()) |
| return; |
| |
| if (outstanding_vsync_requests_ & FLUSH_INPUT) { |
| outstanding_vsync_requests_ &= ~FLUSH_INPUT; |
| host_->FlushInput(); |
| } |
| |
| if (outstanding_vsync_requests_ & BEGIN_FRAME || |
| outstanding_vsync_requests_ & PERSISTENT_BEGIN_FRAME) { |
| outstanding_vsync_requests_ &= ~BEGIN_FRAME; |
| SendBeginFrame(frame_time, vsync_period); |
| } |
| |
| // This allows for SendBeginFrame and FlushInput to modify |
| // outstanding_vsync_requests. |
| uint32_t outstanding_vsync_requests = outstanding_vsync_requests_; |
| outstanding_vsync_requests_ = 0; |
| RequestVSyncUpdate(outstanding_vsync_requests); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnAnimate(base::TimeTicks begin_frame_time) { |
| if (Animate(begin_frame_time)) |
| SetNeedsAnimate(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnActivityStopped() { |
| TRACE_EVENT0("browser", "RenderWidgetHostViewAndroid::OnActivityStopped"); |
| DCHECK(observing_root_window_); |
| is_window_activity_started_ = false; |
| HideInternal(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnActivityStarted() { |
| TRACE_EVENT0("browser", "RenderWidgetHostViewAndroid::OnActivityStarted"); |
| DCHECK(observing_root_window_); |
| is_window_activity_started_ = true; |
| ShowInternal(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnLostResources() { |
| ReleaseLocksOnSurface(); |
| if (layer_.get()) |
| DestroyDelegatedContent(); |
| DCHECK(ack_callbacks_.empty()); |
| } |
| |
| // TODO(wjmaclean): There is significant overlap between |
| // PrepareTextureCopyOutputResult and CopyFromCompositingSurfaceFinished in |
| // this file, and the versions in surface_utils.cc. They should |
| // be merged. See https://crbug.com/582955 |
| |
| // static |
| void RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult( |
| const gfx::Size& dst_size_in_pixel, |
| SkColorType color_type, |
| const base::TimeTicks& start_time, |
| const ReadbackRequestCallback& callback, |
| scoped_ptr<cc::CopyOutputResult> result) { |
| base::ScopedClosureRunner scoped_callback_runner( |
| base::Bind(callback, SkBitmap(), READBACK_FAILED)); |
| TRACE_EVENT0("cc", |
| "RenderWidgetHostViewAndroid::PrepareTextureCopyOutputResult"); |
| |
| if (!result->HasTexture() || result->IsEmpty() || result->size().IsEmpty()) |
| return; |
| |
| gfx::Size output_size_in_pixel; |
| if (dst_size_in_pixel.IsEmpty()) |
| output_size_in_pixel = result->size(); |
| else |
| output_size_in_pixel = dst_size_in_pixel; |
| |
| GLHelper* gl_helper = GetPostReadbackGLHelper(); |
| if (!gl_helper) |
| return; |
| if (!gl_helper->IsReadbackConfigSupported(color_type)) |
| color_type = kRGBA_8888_SkColorType; |
| scoped_ptr<SkBitmap> bitmap(new SkBitmap); |
| if (!bitmap->tryAllocPixels(SkImageInfo::Make(output_size_in_pixel.width(), |
| output_size_in_pixel.height(), |
| color_type, |
| kOpaque_SkAlphaType))) { |
| scoped_callback_runner.Reset( |
| base::Bind(callback, SkBitmap(), READBACK_BITMAP_ALLOCATION_FAILURE)); |
| return; |
| } |
| |
| |
| scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock( |
| new SkAutoLockPixels(*bitmap)); |
| uint8_t* pixels = static_cast<uint8_t*>(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.mailbox(), texture_mailbox.sync_token(), result->size(), |
| gfx::Rect(result->size()), output_size_in_pixel, pixels, color_type, |
| base::Bind(&CopyFromCompositingSurfaceFinished, callback, |
| base::Passed(&release_callback), base::Passed(&bitmap), |
| start_time, base::Passed(&bitmap_pixels_lock)), |
| GLHelper::SCALER_QUALITY_GOOD); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnStylusSelectBegin(float x0, |
| float y0, |
| float x1, |
| float y1) { |
| SelectBetweenCoordinates(gfx::PointF(x0, y0), gfx::PointF(x1, y1)); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnStylusSelectUpdate(float x, float y) { |
| MoveRangeSelectionExtent(gfx::PointF(x, y)); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnStylusSelectEnd() { |
| if (selection_controller_) |
| selection_controller_->AllowShowingFromCurrentSelection(); |
| } |
| |
| void RenderWidgetHostViewAndroid::OnStylusSelectTap(base::TimeTicks time, |
| float x, |
| float y) { |
| // Treat the stylus tap as a long press, activating either a word selection or |
| // context menu depending on the targetted content. |
| blink::WebGestureEvent long_press = WebGestureEventBuilder::Build( |
| blink::WebInputEvent::GestureLongPress, |
| (time - base::TimeTicks()).InSecondsF(), x, y); |
| SendGestureEvent(long_press); |
| } |
| |
| // static |
| void RenderWidgetHostViewBase::GetDefaultScreenInfo( |
| blink::WebScreenInfo* results) { |
| const gfx::Display& display = gfx::Screen::GetScreen()->GetPrimaryDisplay(); |
| results->rect = display.bounds(); |
| // TODO(husky): Remove any system controls from availableRect. |
| results->availableRect = display.work_area(); |
| results->deviceScaleFactor = display.device_scale_factor(); |
| results->orientationAngle = display.RotationAsDegree(); |
| results->orientationType = |
| RenderWidgetHostViewBase::GetOrientationTypeForMobile(display); |
| gfx::DeviceDisplayInfo info; |
| results->depth = info.GetBitsPerPixel(); |
| results->depthPerComponent = info.GetBitsPerComponent(); |
| results->isMonochrome = (results->depthPerComponent == 0); |
| } |
| |
| } // namespace content |