// Copyright 2015 The Chromium Embedded Framework 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 "cef/libcef/browser/osr/browser_platform_delegate_osr.h" #include #include "base/task/current_thread.h" #include "cef/libcef/browser/image_impl.h" #include "cef/libcef/browser/osr/osr_accessibility_util.h" #include "cef/libcef/browser/osr/render_widget_host_view_osr.h" #include "cef/libcef/browser/osr/touch_selection_controller_client_osr.h" #include "cef/libcef/browser/osr/web_contents_view_osr.h" #include "cef/libcef/browser/views/view_util.h" #include "cef/libcef/common/drag_data_impl.h" #include "components/input/render_widget_host_input_event_router.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/render_view_host.h" #include "ui/display/screen.h" #include "ui/events/base_event_utils.h" CefBrowserPlatformDelegateOsr::CefBrowserPlatformDelegateOsr( std::unique_ptr native_delegate, bool use_shared_texture, bool use_external_begin_frame) : native_delegate_(std::move(native_delegate)), use_shared_texture_(use_shared_texture), use_external_begin_frame_(use_external_begin_frame) { native_delegate_->set_windowless_handler(this); } void CefBrowserPlatformDelegateOsr::CreateViewForWebContents( raw_ptr* view, raw_ptr* delegate_view) { DCHECK(!view_osr_); // Use the OSR view instead of the default platform view. view_osr_ = new CefWebContentsViewOSR( GetBackgroundColor(), use_shared_texture_, use_external_begin_frame_); *view = view_osr_; *delegate_view = view_osr_; } void CefBrowserPlatformDelegateOsr::WebContentsCreated( content::WebContents* web_contents, bool owned) { CefBrowserPlatformDelegateAlloy::WebContentsCreated(web_contents, owned); DCHECK(view_osr_); DCHECK(!view_osr_->web_contents()); // Associate the WebContents with the OSR view. view_osr_->WebContentsCreated(web_contents); } void CefBrowserPlatformDelegateOsr::WebContentsDestroyed( content::WebContents* web_contents) { DCHECK_EQ(web_contents, web_contents_); if (auto* view = GetOSRHostView()) { view->ReleaseCompositor(); } CefBrowserPlatformDelegateAlloy::WebContentsDestroyed(web_contents); } void CefBrowserPlatformDelegateOsr::RenderViewCreated( content::RenderViewHost* render_view_host) { if (view_osr_) { view_osr_->RenderViewCreated(); } } void CefBrowserPlatformDelegateOsr::BrowserCreated( CefBrowserHostBase* browser) { CefBrowserPlatformDelegateAlloy::BrowserCreated(browser); if (browser->IsPopup()) { // Associate the RenderWidget host view with the browser now because the // browser wasn't known at the time that the host view was created. // |view| will be null if the popup is a DevTools window. if (auto* view = GetOSRHostView()) { view->set_browser_impl(AlloyBrowserHostImpl::FromBaseChecked(browser)); } } } void CefBrowserPlatformDelegateOsr::BrowserDestroyed( CefBrowserHostBase* browser) { view_osr_ = nullptr; current_rvh_for_drag_ = nullptr; CefBrowserPlatformDelegateAlloy::BrowserDestroyed(browser); } SkColor CefBrowserPlatformDelegateOsr::GetBackgroundColor() const { return native_delegate_->GetBackgroundColor(); } void CefBrowserPlatformDelegateOsr::WasResized() { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->WasResized(); } } void CefBrowserPlatformDelegateOsr::SendKeyEvent(const CefKeyEvent& event) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (!view) { return; } input::NativeWebKeyboardEvent web_event = native_delegate_->TranslateWebKeyEvent(event); view->SendKeyEvent(web_event); } void CefBrowserPlatformDelegateOsr::SendMouseClickEvent( const CefMouseEvent& event, CefBrowserHost::MouseButtonType type, bool mouseUp, int clickCount) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (!view) { return; } blink::WebMouseEvent web_event = native_delegate_->TranslateWebClickEvent( event, type, mouseUp, clickCount); view->SendMouseEvent(web_event); } void CefBrowserPlatformDelegateOsr::SendMouseMoveEvent( const CefMouseEvent& event, bool mouseLeave) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (!view) { return; } blink::WebMouseEvent web_event = native_delegate_->TranslateWebMoveEvent(event, mouseLeave); view->SendMouseEvent(web_event); } void CefBrowserPlatformDelegateOsr::SendMouseWheelEvent( const CefMouseEvent& event, int deltaX, int deltaY) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (!view) { return; } blink::WebMouseWheelEvent web_event = native_delegate_->TranslateWebWheelEvent(event, deltaX, deltaY); view->SendMouseWheelEvent(web_event); } void CefBrowserPlatformDelegateOsr::SendTouchEvent(const CefTouchEvent& event) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->SendTouchEvent(event); } } void CefBrowserPlatformDelegateOsr::SetFocus(bool setFocus) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->SetFocus(setFocus); } } gfx::Point CefBrowserPlatformDelegateOsr::GetScreenPoint( const gfx::Point& view, bool want_dip_coords) const { CefRefPtr handler = browser_->client()->GetRenderHandler(); if (handler.get()) { int screenX = 0, screenY = 0; if (handler->GetScreenPoint(browser_.get(), view.x(), view.y(), screenX, screenY)) { gfx::Point screen_point(screenX, screenY); #if !BUILDFLAG(IS_MAC) // Mac always operates in DIP coordinates so |want_dip_coords| is ignored. if (want_dip_coords) { // Convert to DIP coordinates. const auto& display = view_util::GetDisplayNearestPoint( screen_point, /*input_pixel_coords=*/true); view_util::ConvertPointFromPixels(&screen_point, display.device_scale_factor()); } #endif return screen_point; } } return view; } void CefBrowserPlatformDelegateOsr::ViewText(const std::string& text) { native_delegate_->ViewText(text); } bool CefBrowserPlatformDelegateOsr::HandleKeyboardEvent( const input::NativeWebKeyboardEvent& event) { return native_delegate_->HandleKeyboardEvent(event); } CefEventHandle CefBrowserPlatformDelegateOsr::GetEventHandle( const input::NativeWebKeyboardEvent& event) const { return native_delegate_->GetEventHandle(event); } std::unique_ptr CefBrowserPlatformDelegateOsr::CreateJavaScriptDialogRunner() { return native_delegate_->CreateJavaScriptDialogRunner(); } std::unique_ptr CefBrowserPlatformDelegateOsr::CreateMenuRunner() { return native_delegate_->CreateMenuRunner(); } bool CefBrowserPlatformDelegateOsr::IsWindowless() const { return true; } void CefBrowserPlatformDelegateOsr::WasHidden(bool hidden) { // The WebContentsImpl will notify the OSR view. content::WebContentsImpl* web_contents = static_cast(web_contents_); if (web_contents) { if (hidden) { web_contents->WasHidden(); } else { web_contents->WasShown(); } } } bool CefBrowserPlatformDelegateOsr::IsHidden() const { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { return view->is_hidden(); } return true; } void CefBrowserPlatformDelegateOsr::NotifyScreenInfoChanged() { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->OnScreenInfoChanged(); } } void CefBrowserPlatformDelegateOsr::Invalidate(cef_paint_element_type_t type) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->Invalidate(type); } } void CefBrowserPlatformDelegateOsr::SendExternalBeginFrame() { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->SendExternalBeginFrame(); } } void CefBrowserPlatformDelegateOsr::SetWindowlessFrameRate(int frame_rate) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->UpdateFrameRate(); } } void CefBrowserPlatformDelegateOsr::ImeSetComposition( const CefString& text, const std::vector& underlines, const CefRange& replacement_range, const CefRange& selection_range) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->ImeSetComposition(text, underlines, replacement_range, selection_range); } } void CefBrowserPlatformDelegateOsr::ImeCommitText( const CefString& text, const CefRange& replacement_range, int relative_cursor_pos) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->ImeCommitText(text, replacement_range, relative_cursor_pos); } } void CefBrowserPlatformDelegateOsr::ImeFinishComposingText( bool keep_selection) { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->ImeFinishComposingText(keep_selection); } } void CefBrowserPlatformDelegateOsr::ImeCancelComposition() { CefRenderWidgetHostViewOSR* view = GetOSRHostView(); if (view) { view->ImeCancelComposition(); } } void CefBrowserPlatformDelegateOsr::DragTargetDragEnter( CefRefPtr drag_data, const CefMouseEvent& event, cef_drag_operations_mask_t allowed_ops) { content::WebContentsImpl* web_contents = static_cast(web_contents_); if (!web_contents) { return; } if (current_rvh_for_drag_) { DragTargetDragLeave(); } const gfx::Point client_pt(event.x, event.y); gfx::PointF transformed_pt; // Some random crashes occured when GetWeakPtr is called on a null pointer // that is the return of GetRenderWidgetHostViewInputAtPoint. As the root // cause is not yet understood (no reproducible scenario yet), the current fix // is only a protection against null pointer dereferencing. auto* view = web_contents->GetInputEventRouter()->GetRenderWidgetHostViewInputAtPoint( web_contents->GetRenderViewHost()->GetWidget()->GetView(), gfx::PointF(client_pt), &transformed_pt); if (!view) { return; } auto* target_rwh = content::RenderWidgetHostImpl::From( static_cast(view) ->GetRenderWidgetHost()); current_rwh_for_drag_ = target_rwh->GetWeakPtr(); current_rvh_for_drag_ = web_contents->GetRenderViewHost(); drag_data_ = drag_data; drag_allowed_ops_ = allowed_ops; CefDragDataImpl* data_impl = static_cast(drag_data.get()); base::AutoLock lock_scope(data_impl->lock()); content::DropData* drop_data = data_impl->drop_data(); drop_data->document_is_handling_drag = document_is_handling_drag_; const gfx::Point& screen_pt = GetScreenPoint(client_pt, /*want_dip_coords=*/false); blink::DragOperationsMask ops = static_cast(allowed_ops); int modifiers = TranslateWebEventModifiers(event.modifiers); current_rwh_for_drag_->FilterDropData(drop_data); // Give the delegate an opportunity to cancel the drag. if (web_contents->GetDelegate() && !web_contents->GetDelegate()->CanDragEnter( web_contents, *drop_data, ops)) { drag_data_ = nullptr; return; } current_rwh_for_drag_->DragTargetDragEnter(*drop_data, transformed_pt, gfx::PointF(screen_pt), ops, modifiers, base::DoNothing()); } void CefBrowserPlatformDelegateOsr::DragTargetDragOver( const CefMouseEvent& event, cef_drag_operations_mask_t allowed_ops) { if (!drag_data_) { return; } content::WebContentsImpl* web_contents = static_cast(web_contents_); if (!web_contents) { return; } const gfx::Point client_pt(event.x, event.y); const gfx::Point& screen_pt = GetScreenPoint(client_pt, /*want_dip_coords=*/false); gfx::PointF transformed_pt; auto* view = web_contents->GetInputEventRouter()->GetRenderWidgetHostViewInputAtPoint( web_contents->GetRenderViewHost()->GetWidget()->GetView(), gfx::PointF(client_pt), &transformed_pt); auto* target_rwh = content::RenderWidgetHostImpl::From( static_cast(view) ->GetRenderWidgetHost()); if (target_rwh != current_rwh_for_drag_.get()) { if (current_rwh_for_drag_) { gfx::PointF transformed_leave_point(client_pt); gfx::PointF transformed_screen_point(screen_pt); static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(client_pt), static_cast( current_rwh_for_drag_->GetView()), &transformed_leave_point); static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(screen_pt), static_cast( current_rwh_for_drag_->GetView()), &transformed_screen_point); current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point, transformed_screen_point); } DragTargetDragEnter(drag_data_, event, drag_allowed_ops_); } if (!drag_data_) { return; } blink::DragOperationsMask ops = static_cast(allowed_ops); int modifiers = TranslateWebEventModifiers(event.modifiers); target_rwh->DragTargetDragOver(transformed_pt, gfx::PointF(screen_pt), ops, modifiers, base::DoNothing()); } void CefBrowserPlatformDelegateOsr::DragTargetDragLeave() { if (current_rvh_for_drag_ != web_contents_->GetRenderViewHost() || !drag_data_) { return; } if (current_rwh_for_drag_) { current_rwh_for_drag_->DragTargetDragLeave(gfx::PointF(), gfx::PointF()); current_rwh_for_drag_.reset(); } drag_data_ = nullptr; } void CefBrowserPlatformDelegateOsr::DragTargetDrop(const CefMouseEvent& event) { if (!drag_data_) { return; } content::WebContentsImpl* web_contents = static_cast(web_contents_); if (!web_contents) { return; } gfx::Point client_pt(event.x, event.y); const gfx::Point& screen_pt = GetScreenPoint(client_pt, /*want_dip_coords=*/false); gfx::PointF transformed_pt; auto* view = web_contents->GetInputEventRouter()->GetRenderWidgetHostViewInputAtPoint( web_contents->GetRenderViewHost()->GetWidget()->GetView(), gfx::PointF(client_pt), &transformed_pt); auto* target_rwh = content::RenderWidgetHostImpl::From( static_cast(view) ->GetRenderWidgetHost()); if (target_rwh != current_rwh_for_drag_.get()) { if (current_rwh_for_drag_) { gfx::PointF transformed_leave_point(client_pt); gfx::PointF transformed_screen_point(screen_pt); static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(client_pt), static_cast( current_rwh_for_drag_->GetView()), &transformed_leave_point); static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(screen_pt), static_cast( current_rwh_for_drag_->GetView()), &transformed_screen_point); current_rwh_for_drag_->DragTargetDragLeave(transformed_leave_point, transformed_screen_point); } DragTargetDragEnter(drag_data_, event, drag_allowed_ops_); } if (!drag_data_) { return; } { CefDragDataImpl* data_impl = static_cast(drag_data_.get()); base::AutoLock lock_scope(data_impl->lock()); content::DropData* drop_data = data_impl->drop_data(); drop_data->document_is_handling_drag = document_is_handling_drag_; int modifiers = TranslateWebEventModifiers(event.modifiers); target_rwh->DragTargetDrop(*drop_data, transformed_pt, gfx::PointF(screen_pt), modifiers, base::DoNothing()); } drag_data_ = nullptr; } void CefBrowserPlatformDelegateOsr::StartDragging( const content::DropData& drop_data, blink::DragOperationsMask allowed_ops, const gfx::ImageSkia& image, const gfx::Vector2d& image_offset, const blink::mojom::DragEventSourceInfo& event_info, content::RenderWidgetHostImpl* source_rwh) { drag_start_rwh_ = source_rwh->GetWeakPtr(); bool handled = false; CefRefPtr handler = browser_->GetClient()->GetRenderHandler(); if (handler.get()) { CefRefPtr cef_image(new CefImageImpl(image)); CefPoint cef_image_pos(image_offset.x(), image_offset.y()); CefRefPtr drag_data( new CefDragDataImpl(drop_data, cef_image, cef_image_pos)); drag_data->SetReadOnly(true); base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; handled = handler->StartDragging( browser_.get(), drag_data.get(), static_cast(allowed_ops), event_info.location.x(), event_info.location.y()); } if (!handled) { DragSourceSystemDragEnded(); } } void CefBrowserPlatformDelegateOsr::UpdateDragOperation( ui::mojom::DragOperation operation, bool document_is_handling_drag) { document_is_handling_drag_ = document_is_handling_drag; CefRefPtr handler = browser_->GetClient()->GetRenderHandler(); if (handler.get()) { handler->UpdateDragCursor( browser_.get(), static_cast(operation)); } } void CefBrowserPlatformDelegateOsr::DragSourceEndedAt( int x, int y, cef_drag_operations_mask_t op) { if (!drag_start_rwh_) { return; } content::WebContentsImpl* web_contents = static_cast(web_contents_); if (!web_contents) { return; } content::RenderWidgetHostImpl* source_rwh = drag_start_rwh_.get(); const gfx::Point client_loc(gfx::Point(x, y)); const gfx::Point& screen_loc = GetScreenPoint(client_loc, /*want_dip_coords=*/false); ui::mojom::DragOperation drag_op = static_cast(op); // |client_loc| and |screen_loc| are in the root coordinate space, for // non-root RenderWidgetHosts they need to be transformed. gfx::PointF transformed_point(client_loc); gfx::PointF transformed_screen_point(screen_loc); if (source_rwh && web_contents->GetRenderWidgetHostView()) { static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(client_loc), static_cast( source_rwh->GetView()), &transformed_point); static_cast( web_contents->GetRenderWidgetHostView()) ->TransformPointToCoordSpaceForView( gfx::PointF(screen_loc), static_cast( source_rwh->GetView()), &transformed_screen_point); } web_contents->DragSourceEndedAt(transformed_point.x(), transformed_point.y(), transformed_screen_point.x(), transformed_screen_point.y(), drag_op, source_rwh); } void CefBrowserPlatformDelegateOsr::DragSourceSystemDragEnded() { if (!drag_start_rwh_) { return; } content::WebContentsImpl* web_contents = static_cast(web_contents_); if (!web_contents) { return; } web_contents->SystemDragEnded(drag_start_rwh_.get()); drag_start_rwh_ = nullptr; } void CefBrowserPlatformDelegateOsr::AccessibilityEventReceived( const ui::AXUpdatesAndEvents& details) { if (auto handler = browser_->client()->GetRenderHandler()) { if (auto acc_handler = handler->GetAccessibilityHandler()) { acc_handler->OnAccessibilityTreeChange( osr_accessibility_util::ParseAccessibilityEventData(details)); } } } void CefBrowserPlatformDelegateOsr::AccessibilityLocationChangesReceived( const ui::AXTreeID& tree_id, ui::AXLocationAndScrollUpdates& details) { if (auto handler = browser_->client()->GetRenderHandler()) { if (auto acc_handler = handler->GetAccessibilityHandler()) { acc_handler->OnAccessibilityLocationChange( osr_accessibility_util::ParseAccessibilityLocationData(tree_id, details)); } } } CefWindowHandle CefBrowserPlatformDelegateOsr::GetParentWindowHandle() const { return GetHostWindowHandle(); } gfx::Point CefBrowserPlatformDelegateOsr::GetParentScreenPoint( const gfx::Point& view, bool want_dip_coords) const { return GetScreenPoint(view, want_dip_coords); } CefRenderWidgetHostViewOSR* CefBrowserPlatformDelegateOsr::GetOSRHostView() const { if (!web_contents_) { return nullptr; } content::RenderViewHost* host = web_contents_->GetRenderViewHost(); if (host) { return static_cast( host->GetWidget()->GetView()); } return nullptr; }