diff --git a/cef3/cef.gyp b/cef3/cef.gyp index 3d6f63e99..c37f17545 100644 --- a/cef3/cef.gyp +++ b/cef3/cef.gyp @@ -86,7 +86,7 @@ '-lshlwapi.lib', '-lrpcrt4.lib', '-lopengl32.lib', - '-lglu32.lib', + '-lglu32.lib', ], }, 'sources': [ @@ -199,6 +199,7 @@ 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', ], }, 'sources': [ @@ -237,6 +238,7 @@ '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/icu/icu.gyp:icui18n', '<(DEPTH)/third_party/icu/icu.gyp:icuuc', + '<(DEPTH)/ui/ui.gyp:ui', 'cef_pak', 'libcef', 'libcef_dll_wrapper', @@ -258,6 +260,7 @@ 'tests/unittests/navigation_unittest.cc', 'tests/unittests/process_message_unittest.cc', 'tests/unittests/request_unittest.cc', + 'tests/cefclient/resource_util.h', 'tests/unittests/run_all_unittests.cc', 'tests/unittests/scheme_handler_unittest.cc', 'tests/unittests/stream_unittest.cc', @@ -280,6 +283,7 @@ 'tests/unittests/zip_reader_unittest.cc', ], 'mac_bundle_resources': [ + 'tests/cefclient/res/osr_test.html', 'tests/unittests/mac/unittests.icns', 'tests/unittests/mac/English.lproj/InfoPlist.strings', 'tests/unittests/mac/English.lproj/MainMenu.xib', @@ -299,10 +303,16 @@ }, 'include_dirs': [ '.', + # Necessary to allow resouce_util_* implementation files in cefclient to + # include 'cefclient/*' files, without the tests/ fragment + './tests' ], 'conditions': [ [ 'OS=="win"', { 'sources': [ + 'tests/cefclient/cefclient.rc', + 'tests/cefclient/res/osr_test.html', + 'tests/cefclient/resource_util_win.cpp', 'tests/unittests/os_rendering_unittest.cc', ], }], @@ -410,6 +420,11 @@ ], }, 'sources': [ + 'tests/cefclient/resource_util_mac.mm', + 'tests/cefclient/resource_util_posix.cpp', + 'tests/unittests/os_rendering_unittest.cc', + 'tests/unittests/os_rendering_unittest_mac.h', + 'tests/unittests/os_rendering_unittest_mac.mm', 'tests/unittests/run_all_unittests_mac.mm', ], }], @@ -999,8 +1014,8 @@ 'libcef/browser/javascript_dialog_win.cc', 'libcef/browser/menu_creator_runner_win.cc', 'libcef/browser/menu_creator_runner_win.h', - 'libcef/browser/render_widget_host_view_osr.h', 'libcef/browser/render_widget_host_view_osr.cc', + 'libcef/browser/render_widget_host_view_osr.h', 'libcef/browser/web_contents_view_osr.cc', 'libcef/browser/web_contents_view_osr.h', # Include sources for context menu implementation. @@ -1020,11 +1035,17 @@ '<@(includes_mac)', 'libcef/browser/application_mac.h', 'libcef/browser/application_mac.mm', + 'libcef/browser/backing_store_osr.cc', + 'libcef/browser/backing_store_osr.h', 'libcef/browser/browser_host_impl_mac.mm', 'libcef/browser/browser_main_mac.mm', 'libcef/browser/javascript_dialog_mac.mm', 'libcef/browser/menu_creator_runner_mac.h', 'libcef/browser/menu_creator_runner_mac.mm', + 'libcef/browser/render_widget_host_view_osr.cc', + 'libcef/browser/render_widget_host_view_osr.h', + 'libcef/browser/web_contents_view_osr.cc', + 'libcef/browser/web_contents_view_osr.h', # Include sources for context menu implementation. '<(DEPTH)/chrome/browser/ui/cocoa/event_utils.mm', '<(DEPTH)/chrome/browser/ui/cocoa/event_utils.h', diff --git a/cef3/cef_paths2.gypi b/cef3/cef_paths2.gypi index 0c930d71c..53ec1a402 100644 --- a/cef3/cef_paths2.gypi +++ b/cef3/cef_paths2.gypi @@ -134,13 +134,18 @@ 'tests/cefclient/osrenderer.cpp', 'tests/cefclient/resource.h', 'tests/cefclient/res/cefclient.ico', + 'tests/cefclient/res/osr_test.html', 'tests/cefclient/res/small.ico', 'tests/cefclient/resource_util_win.cpp', 'tests/cefclient/window_test_win.cpp', ], 'cefclient_sources_mac': [ 'tests/cefclient/cefclient_mac.mm', + 'tests/cefclient/cefclient_osr_widget_mac.h', + 'tests/cefclient/cefclient_osr_widget_mac.mm', 'tests/cefclient/client_handler_mac.mm', + 'tests/cefclient/osrenderer.h', + 'tests/cefclient/osrenderer.cpp', 'tests/cefclient/resource_util_mac.mm', 'tests/cefclient/resource_util_posix.cpp', 'tests/cefclient/window_test_mac.mm', @@ -184,6 +189,7 @@ 'tests/cefclient/mac/English.lproj/InfoPlist.strings', 'tests/cefclient/mac/English.lproj/MainMenu.xib', 'tests/cefclient/mac/Info.plist', + 'tests/cefclient/res/osr_test.html', '<@(cefclient_bundle_resources_common)', ], 'cefclient_sources_linux': [ diff --git a/cef3/include/capi/cef_browser_capi.h b/cef3/include/capi/cef_browser_capi.h index 1e16fab86..84908e5f6 100644 --- a/cef3/include/capi/cef_browser_capi.h +++ b/cef3/include/capi/cef_browser_capi.h @@ -346,6 +346,17 @@ typedef struct _cef_browser_host_t { /// void (CEF_CALLBACK *was_hidden)(struct _cef_browser_host_t* self, int hidden); + /// + // Send a notification to the browser that the screen info has changed. The + // browser will then call cef_render_handler_t::GetScreenInfo to update the + // screen information with the new values. This simulates moving the webview + // window from one display to another, or changing the properties of the + // current display. This function is only used when window rendering is + // disabled. + /// + void (CEF_CALLBACK *notify_screen_info_changed)( + struct _cef_browser_host_t* self); + /// // Invalidate the |dirtyRect| region of the view. The browser will call // cef_render_handler_t::OnPaint asynchronously with the updated regions. This diff --git a/cef3/include/capi/cef_render_handler_capi.h b/cef3/include/capi/cef_render_handler_capi.h index da57b65ec..0bd27ad04 100644 --- a/cef3/include/capi/cef_render_handler_capi.h +++ b/cef3/include/capi/cef_render_handler_capi.h @@ -77,6 +77,18 @@ typedef struct _cef_render_handler_t { struct _cef_browser_t* browser, int viewX, int viewY, int* screenX, int* screenY); + /// + // Called to allow the client to fill in the CefScreenInfo object with + // appropriate values. Return true (1) if the |screen_info| structure has been + // modified. + // + // If the screen info rectangle is left NULL the rectangle from GetViewRect + // will be used. If the rectangle is still NULL or invalid popups may not be + // drawn correctly. + /// + int (CEF_CALLBACK *get_screen_info)(struct _cef_render_handler_t* self, + struct _cef_browser_t* browser, struct _cef_screen_info_t* screen_info); + /// // Called when the browser wants to show or hide the popup widget. The popup // should be shown if |show| is true (1) and hidden if |show| is false (0). diff --git a/cef3/include/cef_browser.h b/cef3/include/cef_browser.h index 51d37bdba..c445a7de4 100644 --- a/cef3/include/cef_browser.h +++ b/cef3/include/cef_browser.h @@ -388,6 +388,17 @@ class CefBrowserHost : public virtual CefBase { /*--cef()--*/ virtual void WasHidden(bool hidden) =0; + /// + // Send a notification to the browser that the screen info has changed. The + // browser will then call CefRenderHandler::GetScreenInfo to update the + // screen information with the new values. This simulates moving the webview + // window from one display to another, or changing the properties of the + // current display. This method is only used when window rendering is + // disabled. + /// + /*--cef()--*/ + virtual void NotifyScreenInfoChanged() =0; + /// // Invalidate the |dirtyRect| region of the view. The browser will call // CefRenderHandler::OnPaint asynchronously with the updated regions. This diff --git a/cef3/include/cef_render_handler.h b/cef3/include/cef_render_handler.h index 5064950cd..cef29b339 100644 --- a/cef3/include/cef_render_handler.h +++ b/cef3/include/cef_render_handler.h @@ -78,6 +78,19 @@ class CefRenderHandler : public virtual CefBase { int& screenX, int& screenY) { return false; } + /// + // Called to allow the client to fill in the CefScreenInfo object with + // appropriate values. Return true if the |screen_info| structure has been + // modified. + // + // If the screen info rectangle is left empty the rectangle from GetViewRect + // will be used. If the rectangle is still empty or invalid popups may not be + // drawn correctly. + /// + /*--cef()--*/ + virtual bool GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { return false; } + /// // Called when the browser wants to show or hide the popup widget. The popup // should be shown if |show| is true and hidden if |show| is false. diff --git a/cef3/include/internal/cef_mac.h b/cef3/include/internal/cef_mac.h index ddedbce50..f4060b2f3 100644 --- a/cef3/include/internal/cef_mac.h +++ b/cef3/include/internal/cef_mac.h @@ -120,6 +120,8 @@ struct CefWindowInfoTraits { target->width = src->width; target->height = src->height; target->hidden = src->hidden; + target->transparent_painting = src->transparent_painting; + target->window_rendering_disabled = src->window_rendering_disabled; } }; @@ -141,6 +143,15 @@ class CefWindowInfo : public CefStructBase { this->height = height; hidden = false; } + + void SetTransparentPainting(bool transparentPainting) { + transparent_painting = transparentPainting; + } + + void SetAsOffScreen(NSView* view) { + window_rendering_disabled = true; + parent_view = view; + } }; #endif // OS_MACOSX diff --git a/cef3/include/internal/cef_types.h b/cef3/include/internal/cef_types.h index 988e5a0ba..e2e80738a 100644 --- a/cef3/include/internal/cef_types.h +++ b/cef3/include/internal/cef_types.h @@ -972,6 +972,62 @@ enum cef_jsdialog_type_t { JSDIALOGTYPE_PROMPT, }; +/// +// Screen information used when window rendering is disabled. This structure is +// passed as a parameter to CefRenderHandler::GetScreenInfo and should be filled +// in by the client. +/// +typedef struct _cef_screen_info_t { + /// + // Device scale factor. Specifies the ratio between physical and logical + // pixels. + /// + float device_scale_factor; + + /// + // The screen depth in bits per pixel. + /// + int depth; + + /// + // The bits per color component. This assumes that the colors are balanced + // equally. + /// + int depth_per_component; + + /// + // This can be true for black and white printers. + /// + bool is_monochrome; + + /// + // This is set from the rcMonitor member of MONITORINFOEX, to whit: + // "A RECT structure that specifies the display monitor rectangle, + // expressed in virtual-screen coordinates. Note that if the monitor + // is not the primary display monitor, some of the rectangle's + // coordinates may be negative values." + // + // The |rect| and |available_rect| properties are used to determine the + // available surface for rendering popup views. + /// + cef_rect_t rect; + + /// + // This is set from the rcWork member of MONITORINFOEX, to whit: + // "A RECT structure that specifies the work area rectangle of the + // display monitor that can be used by applications, expressed in + // virtual-screen coordinates. Windows uses this rectangle to + // maximize an application on the monitor. The rest of the area in + // rcMonitor contains system windows such as the task bar and side + // bars. Note that if the monitor is not the primary display monitor, + // some of the rectangle's coordinates may be negative values". + // + // The |rect| and |available_rect| properties are used to determine the + // available surface for rendering popup views. + /// + cef_rect_t available_rect; +} cef_screen_info_t; + /// // Supported menu IDs. Non-English translations can be provided for the // IDS_MENU_* strings in CefResourceBundleHandler::GetLocalizedString(). diff --git a/cef3/include/internal/cef_types_mac.h b/cef3/include/internal/cef_types_mac.h index 44863bd4b..d2ea2540b 100644 --- a/cef3/include/internal/cef_types_mac.h +++ b/cef3/include/internal/cef_types_mac.h @@ -83,6 +83,14 @@ typedef struct _cef_window_info_t { // NSView pointer for the parent view. cef_window_handle_t parent_view; + // If window rendering is disabled no browser window will be created. Set + // |parent_view| to the window that will act as the parent for popup menus, + // dialog boxes, etc. + bool window_rendering_disabled; + + // Set to true to enable transparent painting. + bool transparent_painting; + // NSView pointer for the new browser view. cef_window_handle_t view; } cef_window_info_t; diff --git a/cef3/include/internal/cef_types_wrappers.h b/cef3/include/internal/cef_types_wrappers.h index 410979c67..17c06eda6 100644 --- a/cef3/include/internal/cef_types_wrappers.h +++ b/cef3/include/internal/cef_types_wrappers.h @@ -174,6 +174,60 @@ inline bool operator!=(const CefRect& a, const CefRect& b) { return !(a == b); } +struct CefScreenInfoTraits { + typedef cef_screen_info_t struct_type; + + static inline void init(struct_type* s) {} + + static inline void clear(struct_type* s) {} + + static inline void set(const struct_type* src, struct_type* target, + bool copy) { + target->device_scale_factor = src->device_scale_factor; + target->depth = src->depth; + target->depth_per_component = src->depth_per_component; + target->is_monochrome = src->is_monochrome; + target->rect = src->rect; + target->available_rect = src->available_rect; + } +}; + +/// +// Class representing the virtual screen information for use when window rendering +// is disabled. +/// +class CefScreenInfo : public CefStructBase { + public: + typedef CefStructBase parent; + + CefScreenInfo() : parent() {} + CefScreenInfo(const cef_screen_info_t& r) : parent(r) {} // NOLINT(runtime/explicit) + CefScreenInfo(const CefScreenInfo& r) : parent(r) {} // NOLINT(runtime/explicit) + CefScreenInfo(float device_scale_factor, + int depth, + int depth_per_component, + bool is_monochrome, + const CefRect& rect, + const CefRect& available_rect) : parent() { + Set(device_scale_factor, depth, depth_per_component, + is_monochrome, rect, available_rect); + } + + void Set(float device_scale_factor, + int depth, + int depth_per_component, + bool is_monochrome, + const CefRect& rect, + const CefRect& available_rect) { + this->device_scale_factor = device_scale_factor; + this->depth = depth; + this->depth_per_component = depth_per_component; + this->is_monochrome = is_monochrome; + this->rect = rect; + this->available_rect = available_rect; + } +}; + struct CefKeyEventTraits { typedef cef_key_event_t struct_type; diff --git a/cef3/libcef/browser/backing_store_osr.cc b/cef3/libcef/browser/backing_store_osr.cc index 5ff6b0ab4..dd9974084 100644 --- a/cef3/libcef/browser/backing_store_osr.cc +++ b/cef3/libcef/browser/backing_store_osr.cc @@ -4,16 +4,76 @@ #include "libcef/browser/backing_store_osr.h" +#include + +#include "content/browser/renderer_host/dip_util.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_widget_host.h" #include "ui/gfx/rect.h" +#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/size_conversions.h" +#include "ui/gfx/vector2d_conversions.h" #include "ui/surface/transport_dib.h" +// Assume that somewhere along the line, someone will do width * height * 4 +// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 = +// 2**29 and floor(sqrt(2**29)) = 23170. + +// Max height and width for layers +static const int kMaxVideoLayerSize = 23170; + BackingStoreOSR::BackingStoreOSR(content::RenderWidgetHost* widget, - const gfx::Size& size) + const gfx::Size& size, + float scale_factor) : content::BackingStore(widget, size), - device_(SkBitmap::kARGB_8888_Config, size.width(), size.height(), true), - canvas_(&device_) { - canvas_.drawColor(SK_ColorWHITE); + device_scale_factor_(scale_factor) { + gfx::Size pixel_size = gfx::ToFlooredSize( + gfx::ScaleSize(size, device_scale_factor_)); + device_.reset(new SkDevice(SkBitmap::kARGB_8888_Config, + pixel_size.width(), + pixel_size.height(), + true)); + canvas_.reset(new SkCanvas(device_.get())); + + canvas_->drawColor(SK_ColorWHITE); +} + +void BackingStoreOSR::ScaleFactorChanged(float scale_factor) { + if (scale_factor == device_scale_factor_) + return; + + gfx::Size old_pixel_size = gfx::ToFlooredSize( + gfx::ScaleSize(size(), device_scale_factor_)); + device_scale_factor_ = scale_factor; + + gfx::Size pixel_size = gfx::ToFlooredSize( + gfx::ScaleSize(size(), device_scale_factor_)); + + scoped_ptr new_device(new SkDevice(SkBitmap::kARGB_8888_Config, + pixel_size.width(), + pixel_size.height(), + true)); + + scoped_ptr new_canvas(new SkCanvas(new_device.get())); + + // Copy old contents; a low-res flash is better than a black flash. + SkPaint copy_paint; + copy_paint.setXfermodeMode(SkXfermode::kSrc_Mode); + SkIRect src_rect = SkIRect::MakeWH(old_pixel_size.width(), + old_pixel_size.height()); + SkRect dst_rect = SkRect::MakeWH(pixel_size.width(), pixel_size.height()); + new_canvas.get()->drawBitmapRect(device_->accessBitmap(false), &src_rect, + dst_rect, ©_paint); + + canvas_.swap(new_canvas); + device_.swap(new_device); +} + +size_t BackingStoreOSR::MemorySize() { + // NOTE: The computation may be different when the canvas is a subrectangle of + // a larger bitmap. + return gfx::ToFlooredSize( + gfx::ScaleSize(size(), device_scale_factor_)).GetArea() * 4; } void BackingStoreOSR::PaintToBackingStore( @@ -25,36 +85,58 @@ void BackingStoreOSR::PaintToBackingStore( const base::Closure& completion_callback, bool* scheduled_completion_callback) { *scheduled_completion_callback = false; + if (bitmap_rect.IsEmpty()) + return; + + gfx::Rect pixel_bitmap_rect = gfx::ToEnclosedRect( + gfx::ScaleRect(bitmap_rect, scale_factor)); + + const int width = pixel_bitmap_rect.width(); + const int height = pixel_bitmap_rect.height(); + + if (width <= 0 || width > kMaxVideoLayerSize || + height <= 0 || height > kMaxVideoLayerSize) { + return; + } + TransportDIB* dib = process->GetTransportDIB(bitmap); if (!dib) return; SkBitmap src_bitmap; - src_bitmap.setConfig(SkBitmap::kARGB_8888_Config, - bitmap_rect.width(), - bitmap_rect.height()); + src_bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); src_bitmap.setPixels(dib->memory()); SkPaint copy_paint; copy_paint.setXfermodeMode(SkXfermode::kSrc_Mode); for (size_t i = 0; i < copy_rects.size(); i++) { - SkIRect src_rect = SkIRect::MakeXYWH(copy_rects[i].x() - bitmap_rect.x(), - copy_rects[i].y() - bitmap_rect.y(), - copy_rects[i].width(), - copy_rects[i].height()); - SkRect paint_rect = SkRect::MakeXYWH(copy_rects[i].x(), - copy_rects[i].y(), - copy_rects[i].width(), - copy_rects[i].height()); - canvas_.drawBitmapRect(src_bitmap, &src_rect, paint_rect, ©_paint); + const gfx::Rect pixel_copy_rect = gfx::ToEnclosingRect( + gfx::ScaleRect(copy_rects[i], scale_factor)); + SkIRect src_rect = + SkIRect::MakeXYWH(pixel_copy_rect.x() - pixel_bitmap_rect.x(), + pixel_copy_rect.y() - pixel_bitmap_rect.y(), + pixel_copy_rect.width(), + pixel_copy_rect.height()); + + const gfx::Rect pixel_copy_dst_rect = gfx::ToEnclosingRect( + gfx::ScaleRect(copy_rects[i], device_scale_factor_)); + SkRect paint_rect = SkRect::MakeXYWH(pixel_copy_dst_rect.x(), + pixel_copy_dst_rect.y(), + pixel_copy_dst_rect.width(), + pixel_copy_dst_rect.height()); + canvas_->drawBitmapRect(src_bitmap, &src_rect, paint_rect, ©_paint); } src_bitmap.setPixels(0); } bool BackingStoreOSR::CopyFromBackingStore(const gfx::Rect& rect, skia::PlatformBitmap* output) { - if (!output->Allocate(rect.width(), rect.height(), true)) + const int width = + std::min(size().width(), rect.width()) * device_scale_factor_; + const int height = + std::min(size().height(), rect.height()) * device_scale_factor_; + if (!output->Allocate(width, height, true)) return false; SkPaint copy_paint; @@ -62,21 +144,30 @@ bool BackingStoreOSR::CopyFromBackingStore(const gfx::Rect& rect, SkCanvas canvas(output->GetBitmap()); canvas.drawColor(SK_ColorWHITE); - canvas.drawBitmap(device_.accessBitmap(false), 0, 0, ©_paint); + canvas.drawBitmap(device_->accessBitmap(false), 0, 0, ©_paint); return true; } void BackingStoreOSR::ScrollBackingStore(const gfx::Vector2d& delta, const gfx::Rect& clip_rect, const gfx::Size& view_size) { - SkIRect subset_rect = SkIRect::MakeXYWH(clip_rect.x(), - clip_rect.y(), - clip_rect.width(), - clip_rect.height()); - device_.accessBitmap(true).scrollRect(&subset_rect, delta.x(), delta.y()); + gfx::Rect pixel_rect = gfx::ToEnclosingRect( + gfx::ScaleRect(clip_rect, device_scale_factor_)); + gfx::Vector2d pixel_delta = gfx::ToFlooredVector2d( + gfx::ScaleVector2d(delta, device_scale_factor_)); + + int x = std::min(pixel_rect.x(), pixel_rect.x() - pixel_delta.x()); + int y = std::min(pixel_rect.y(), pixel_rect.y() - pixel_delta.y()); + int w = pixel_rect.width() + abs(pixel_delta.x()); + int h = pixel_rect.height() + abs(pixel_delta.y()); + SkIRect rect = SkIRect::MakeXYWH(x, y, w, h); + + device_->accessBitmap(true).scrollRect(&rect, + pixel_delta.x(), + pixel_delta.y()); } const void* BackingStoreOSR::getPixels() const { - return const_cast(this)->device_. + return const_cast(this)->device_-> accessBitmap(false).getPixels(); } diff --git a/cef3/libcef/browser/backing_store_osr.h b/cef3/libcef/browser/backing_store_osr.h index 91446c27b..916c31973 100644 --- a/cef3/libcef/browser/backing_store_osr.h +++ b/cef3/libcef/browser/backing_store_osr.h @@ -19,6 +19,7 @@ class BackingStoreOSR : public content::BackingStore { } // BackingStore implementation. + virtual size_t MemorySize() OVERRIDE; virtual void PaintToBackingStore( content::RenderProcessHost* process, TransportDIB::Id bitmap, @@ -33,17 +34,22 @@ class BackingStoreOSR : public content::BackingStore { const gfx::Rect& clip_rect, const gfx::Size& view_size) OVERRIDE; + void ScaleFactorChanged(float scale_factor); + const void* getPixels() const; private: // Can be instantiated only within CefRenderWidgetHostViewOSR. friend class CefRenderWidgetHostViewOSR; + explicit BackingStoreOSR(content::RenderWidgetHost* widget, - const gfx::Size& size); + const gfx::Size& size, float scale_factor); virtual ~BackingStoreOSR() {} - SkDevice device_; - SkCanvas canvas_; + scoped_ptr device_; + scoped_ptr canvas_; + + float device_scale_factor_; DISALLOW_COPY_AND_ASSIGN(BackingStoreOSR); }; diff --git a/cef3/libcef/browser/browser_host_impl.cc b/cef3/libcef/browser/browser_host_impl.cc index 2450515de..d02b0b9cc 100644 --- a/cef3/libcef/browser/browser_host_impl.cc +++ b/cef3/libcef/browser/browser_host_impl.cc @@ -47,7 +47,7 @@ #include "content/public/common/file_chooser_params.h" #include "ui/shell_dialogs/selected_file_info.h" -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) #include "libcef/browser/render_widget_host_view_osr.h" #include "libcef/browser/web_contents_view_osr.h" #endif @@ -300,6 +300,9 @@ CefRefPtr CefBrowserHost::CreateBrowserSync( scoped_refptr info = CefContentBrowserClient::Get()->CreateBrowserInfo(); + + info->set_window_rendering_disabled( + CefBrowserHostImpl::IsWindowRenderingDisabled(windowInfo)); DCHECK(!info->is_popup()); CefRefPtr browser = CefBrowserHostImpl::Create(windowInfo, new_settings, client, NULL, info, @@ -346,7 +349,7 @@ CefRefPtr CefBrowserHostImpl::Create( // TODO(port): Implement this method to work on other platforms as part of // off-screen rendering support. -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) if (browser->IsWindowRenderingDisabled()) { CefRenderWidgetHostViewOSR* view = static_cast( @@ -354,7 +357,7 @@ CefRefPtr CefBrowserHostImpl::Create( if (view) view->set_browser_impl(browser); } -#endif // OS_WIN +#endif // defined(OS_WIN) || defined(OS_MACOSX) if (client.get()) { CefRefPtr handler = client->GetLifeSpanHandler(); @@ -667,6 +670,38 @@ void CefBrowserHostImpl::WasHidden(bool hidden) { widget->WasShown(); } +void CefBrowserHostImpl::NotifyScreenInfoChanged() { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefBrowserHostImpl::NotifyScreenInfoChanged, this)); + return; + } + + if (!IsWindowRenderingDisabled()) { + NOTREACHED() << "Window rendering is not disabled"; + return; + } + + if (!web_contents()) + return; + + content::RenderWidgetHostView* view = + web_contents()->GetRenderViewHost()->GetView(); + if (!view) + return; + +#if defined(OS_WIN) || defined(OS_MACOSX) + CefRenderWidgetHostViewOSR* orview = + static_cast(view); + + orview->OnScreenInfoChanged(); +#else + // TODO(port): Implement this method to work on other platforms as part of + // off-screen rendering support. + NOTREACHED(); +#endif +} + void CefBrowserHostImpl::Invalidate(const CefRect& dirtyRect, PaintElementType type) { if (!IsWindowRenderingDisabled()) { @@ -683,7 +718,7 @@ void CefBrowserHostImpl::Invalidate(const CefRect& dirtyRect, if (!web_contents()) return; -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) content::RenderWidgetHostView* view = web_contents()->GetRenderViewHost()->GetView(); CefRenderWidgetHostViewOSR* orview = @@ -716,7 +751,7 @@ void CefBrowserHostImpl::SendKeyEvent(const CefKeyEvent& event) { if (widget) widget->ForwardKeyboardEvent(web_event); } else { -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) if (!web_contents()) return; content::RenderWidgetHostView* view = @@ -780,7 +815,7 @@ void CefBrowserHostImpl::SendMouseWheelEvent(const CefMouseEvent& event, if (widget) widget->ForwardWheelEvent(web_event); } else { -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) if (!web_contents()) return; content::RenderWidgetHostView* view = @@ -834,7 +869,7 @@ void CefBrowserHostImpl::SendMouseEvent(const WebKit::WebMouseEvent& event) { if (widget) widget->ForwardMouseEvent(event); } else { -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) if (!web_contents()) return; content::RenderWidgetHostView* view = diff --git a/cef3/libcef/browser/browser_host_impl.h b/cef3/libcef/browser/browser_host_impl.h index 6b6b01813..929348050 100644 --- a/cef3/libcef/browser/browser_host_impl.h +++ b/cef3/libcef/browser/browser_host_impl.h @@ -130,6 +130,7 @@ class CefBrowserHostImpl : public CefBrowserHost, virtual bool IsWindowRenderingDisabled() OVERRIDE; virtual void WasResized() OVERRIDE; virtual void WasHidden(bool hidden) OVERRIDE; + virtual void NotifyScreenInfoChanged() OVERRIDE; virtual void Invalidate(const CefRect& dirtyRect, PaintElementType type) OVERRIDE; virtual void SendKeyEvent(const CefKeyEvent& event) OVERRIDE; @@ -430,6 +431,7 @@ class CefBrowserHostImpl : public CefBrowserHost, int deltaX, int deltaY); void PlatformTranslateMouseEvent(WebKit::WebMouseEvent& web_event, const CefMouseEvent& mouse_event); + int TranslateModifiers(uint32 cefKeyStates); void SendMouseEvent(const WebKit::WebMouseEvent& web_event); diff --git a/cef3/libcef/browser/browser_host_impl_mac.mm b/cef3/libcef/browser/browser_host_impl_mac.mm index e59512630..32a9caead 100644 --- a/cef3/libcef/browser/browser_host_impl_mac.mm +++ b/cef3/libcef/browser/browser_host_impl_mac.mm @@ -19,11 +19,13 @@ #include "content/public/common/file_chooser_params.h" #include "grit/ui_strings.h" #include "net/base/mime_util.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h" #import "ui/base/cocoa/underlay_opengl_hosting_window.h" +#include "ui/base/keycodes/keyboard_codes_posix.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/rect.h" - // Wrapper NSView for the native view. Necessary to destroy the browser when // the view is deleted. @interface CefBrowserHostView : NSView { @@ -142,7 +144,7 @@ void RunOpenFileDialog(const content::FileChooserParams& params, NSView* view, std::vector* files) { NSOpenPanel* openPanel = [NSOpenPanel openPanel]; - + string16 title; if (!params.title.empty()) { title = params.title; @@ -201,7 +203,7 @@ bool RunSaveFileDialog(const content::FileChooserParams& params, NSView* view, base::FilePath* file) { NSSavePanel* savePanel = [NSSavePanel savePanel]; - + string16 title; if (!params.title.empty()) title = params.title; @@ -338,7 +340,9 @@ void CefBrowserHostImpl::PlatformSizeTo(int width, int height) { } CefWindowHandle CefBrowserHostImpl::PlatformGetWindowHandle() { - return window_info_.view; + return IsWindowRenderingDisabled() ? + window_info_.parent_view : + window_info_.view; } void CefBrowserHostImpl::PlatformHandleKeyboardEvent( @@ -372,49 +376,214 @@ void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) { // static bool CefBrowserHostImpl::IsWindowRenderingDisabled(const CefWindowInfo& info) { - // TODO(port): Implement this method as part of off-screen rendering support. - return false; + return info.window_rendering_disabled ? true : false; +} + +static NSTimeInterval currentEventTimestamp() { + NSEvent* currentEvent = [NSApp currentEvent]; + if (currentEvent) + return [currentEvent timestamp]; + else { + // FIXME(API): In case there is no current event, the timestamp could be + // obtained by getting the time since the application started. This involves + // taking some more static functions from Chromium code. + // Another option is to have the timestamp as a field in CefEvent structures + // and let the client provide it. + return 0; + } } bool CefBrowserHostImpl::IsTransparent() { - return false; + return window_info_.transparent_painting != 0; +} + +static NSUInteger NativeModifiers(int cef_modifiers) { + NSUInteger native_modifiers = 0; + if (cef_modifiers & EVENTFLAG_SHIFT_DOWN) + native_modifiers |= NSShiftKeyMask; + if (cef_modifiers & EVENTFLAG_CONTROL_DOWN) + native_modifiers |= NSControlKeyMask; + if (cef_modifiers & EVENTFLAG_ALT_DOWN) + native_modifiers |= NSAlternateKeyMask; + if (cef_modifiers & EVENTFLAG_COMMAND_DOWN) + native_modifiers |= NSCommandKeyMask; + if (cef_modifiers & EVENTFLAG_CAPS_LOCK_ON) + native_modifiers |= NSAlphaShiftKeyMask; + if (cef_modifiers & EVENTFLAG_NUM_LOCK_ON) + native_modifiers |= NSNumericPadKeyMask; + + return native_modifiers; } void CefBrowserHostImpl::PlatformTranslateKeyEvent( content::NativeWebKeyboardEvent& native_event, - const CefKeyEvent& event) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + const CefKeyEvent& key_event) { + // Use a synthetic NSEvent in order to obtain the windowsKeyCode member from + // the NativeWebKeyboardEvent constructor. This is the only member which can + // not be easily translated (without hardcoding keyCodes) + // Determining whether a modifier key is left or right seems to be done + // through the key code as well. + + NSEventType event_type; + if (key_event.character == 0 && key_event.unmodified_character == 0) { + // Check if both character and unmodified_characther are empty to determine + // if this was a NSFlagsChanged event. + // A dead key will have an empty character, but a non-empty unmodified + // character + event_type = NSFlagsChanged; + } else { + switch (key_event.type) { + case KEYEVENT_RAWKEYDOWN: + case KEYEVENT_KEYDOWN: + case KEYEVENT_CHAR: + event_type = NSKeyDown; + break; + case KEYEVENT_KEYUP: + event_type = NSKeyUp; + break; + } + } + + NSString* charactersIgnoringModifiers = [[[NSString alloc] + initWithCharacters:&key_event.unmodified_character length:1] + autorelease]; + NSString* characters = [[[NSString alloc] + initWithCharacters:&key_event.character length:1] autorelease]; + + NSEvent* synthetic_event = + [NSEvent keyEventWithType:event_type + location:NSMakePoint(0, 0) + modifierFlags:NativeModifiers(key_event.modifiers) + timestamp:currentEventTimestamp() + windowNumber:0 + context:nil + characters:characters + charactersIgnoringModifiers:charactersIgnoringModifiers + isARepeat:NO + keyCode:key_event.native_key_code]; + + native_event = content::NativeWebKeyboardEvent(synthetic_event); + if (key_event.type == KEYEVENT_CHAR) + native_event.type = WebKit::WebInputEvent::Char; + + native_event.isSystemKey = key_event.is_system_key; } void CefBrowserHostImpl::PlatformTranslateClickEvent( - WebKit::WebMouseEvent& ev, + WebKit::WebMouseEvent& result, const CefMouseEvent& mouse_event, MouseButtonType type, bool mouseUp, int clickCount) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + PlatformTranslateMouseEvent(result, mouse_event); + + switch (type) { + case MBT_LEFT: + result.type = mouseUp ? WebKit::WebInputEvent::MouseUp : + WebKit::WebInputEvent::MouseDown; + result.button = WebKit::WebMouseEvent::ButtonLeft; + break; + case MBT_MIDDLE: + result.type = mouseUp ? WebKit::WebInputEvent::MouseUp : + WebKit::WebInputEvent::MouseDown; + result.button = WebKit::WebMouseEvent::ButtonMiddle; + break; + case MBT_RIGHT: + result.type = mouseUp ? WebKit::WebInputEvent::MouseUp : + WebKit::WebInputEvent::MouseDown; + result.button = WebKit::WebMouseEvent::ButtonRight; + break; + default: + NOTREACHED(); + } + + result.clickCount = clickCount; } void CefBrowserHostImpl::PlatformTranslateMoveEvent( - WebKit::WebMouseEvent& ev, + WebKit::WebMouseEvent& result, const CefMouseEvent& mouse_event, bool mouseLeave) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + PlatformTranslateMouseEvent(result, mouse_event); + + if (!mouseLeave) { + result.type = WebKit::WebInputEvent::MouseMove; + if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonLeft; + else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonMiddle; + else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonRight; + else + result.button = WebKit::WebMouseEvent::ButtonNone; + } else { + result.type = WebKit::WebInputEvent::MouseLeave; + result.button = WebKit::WebMouseEvent::ButtonNone; + } + + result.clickCount = 0; } void CefBrowserHostImpl::PlatformTranslateWheelEvent( - WebKit::WebMouseWheelEvent& ev, + WebKit::WebMouseWheelEvent& result, const CefMouseEvent& mouse_event, int deltaX, int deltaY) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + result = WebKit::WebMouseWheelEvent(); + PlatformTranslateMouseEvent(result, mouse_event); + + result.type = WebKit::WebInputEvent::MouseWheel; + + static const double scrollbarPixelsPerCocoaTick = 40.0; + result.deltaX = deltaX; + result.deltaY = deltaY; + result.wheelTicksX = result.deltaX / scrollbarPixelsPerCocoaTick; + result.wheelTicksY = result.deltaY / scrollbarPixelsPerCocoaTick; + result.hasPreciseScrollingDeltas = true; + + // Unless the phase and momentumPhase are passed in as parameters to this + // function, there is no way to know them + result.phase = WebKit::WebMouseWheelEvent::PhaseNone; + result.momentumPhase = WebKit::WebMouseWheelEvent::PhaseNone; + + if (mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonLeft; + else if (mouse_event.modifiers & EVENTFLAG_MIDDLE_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonMiddle; + else if (mouse_event.modifiers & EVENTFLAG_RIGHT_MOUSE_BUTTON) + result.button = WebKit::WebMouseEvent::ButtonRight; + else + result.button = WebKit::WebMouseEvent::ButtonNone; } void CefBrowserHostImpl::PlatformTranslateMouseEvent( - WebKit::WebMouseEvent& ev, + WebKit::WebMouseEvent& result, const CefMouseEvent& mouse_event) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + // position + result.x = mouse_event.x; + result.y = mouse_event.y; + result.windowX = result.x; + result.windowY = result.y; + result.globalX = result.x; + result.globalY = result.y; + + if (IsWindowRenderingDisabled()) { + GetClient()->GetRenderHandler()->GetScreenPoint(GetBrowser(), + result.x, result.y, + result.globalX, result.globalY); + } else { + NSView* view = window_info_.parent_view; + if (view) { + NSRect bounds = [view bounds]; + NSPoint view_pt = {result.x, bounds.size.height - result.y}; + NSPoint window_pt = [view convertPoint:view_pt toView:nil]; + NSPoint screen_pt = [[view window] convertBaseToScreen:window_pt]; + result.globalX = screen_pt.x; + result.globalY = screen_pt.y; + } + } + + // modifiers + result.modifiers |= TranslateModifiers(mouse_event.modifiers); + + // timestamp - Mac OSX specific + result.timeStampSeconds = currentEventTimestamp(); } diff --git a/cef3/libcef/browser/browser_info.cc b/cef3/libcef/browser/browser_info.cc index 458a10264..29d06d24f 100644 --- a/cef3/libcef/browser/browser_info.cc +++ b/cef3/libcef/browser/browser_info.cc @@ -8,6 +8,7 @@ CefBrowserInfo::CefBrowserInfo(int browser_id, bool is_popup) : browser_id_(browser_id), is_popup_(is_popup), + is_window_rendering_disabled_(false), render_process_id_(MSG_ROUTING_NONE), render_view_id_(MSG_ROUTING_NONE) { DCHECK_GT(browser_id, 0); @@ -16,6 +17,10 @@ CefBrowserInfo::CefBrowserInfo(int browser_id, bool is_popup) CefBrowserInfo::~CefBrowserInfo() { } +void CefBrowserInfo::set_window_rendering_disabled(bool disabled) { + is_window_rendering_disabled_ = disabled; +} + void CefBrowserInfo::set_render_ids( int render_process_id, int render_view_id) { base::AutoLock lock_scope(lock_); diff --git a/cef3/libcef/browser/browser_info.h b/cef3/libcef/browser/browser_info.h index 81f58f5e0..6c38abb78 100644 --- a/cef3/libcef/browser/browser_info.h +++ b/cef3/libcef/browser/browser_info.h @@ -22,6 +22,11 @@ class CefBrowserInfo : public base::RefCountedThreadSafe { int browser_id() const { return browser_id_; }; bool is_popup() const { return is_popup_; } + bool is_window_rendering_disabled() const { + return is_window_rendering_disabled_; + } + + void set_window_rendering_disabled(bool disabled); void set_render_ids(int render_process_id, int render_view_id); @@ -36,6 +41,7 @@ class CefBrowserInfo : public base::RefCountedThreadSafe { private: int browser_id_; bool is_popup_; + bool is_window_rendering_disabled_; base::Lock lock_; diff --git a/cef3/libcef/browser/browser_message_filter.cc b/cef3/libcef/browser/browser_message_filter.cc index 057bee82e..ffc1b9891 100644 --- a/cef3/libcef/browser/browser_message_filter.cc +++ b/cef3/libcef/browser/browser_message_filter.cc @@ -82,6 +82,7 @@ void CefBrowserMessageFilter::OnGetNewBrowserInfo( routing_id); params->browser_id = info->browser_id(); params->is_popup = info->is_popup(); + params->is_window_rendering_disabled = info->is_window_rendering_disabled(); } void CefBrowserMessageFilter::OnCreateWindow( diff --git a/cef3/libcef/browser/content_browser_client.cc b/cef3/libcef/browser/content_browser_client.cc index 124390a72..bf2bdc3de 100644 --- a/cef3/libcef/browser/content_browser_client.cc +++ b/cef3/libcef/browser/content_browser_client.cc @@ -37,7 +37,7 @@ #include "googleurl/src/gurl.h" #include "ui/base/ui_base_switches.h" -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) #include "libcef/browser/web_contents_view_osr.h" #endif @@ -375,7 +375,7 @@ CefContentBrowserClient::OverrideCreateWebContentsView( *render_view_host_delegate_view = NULL; // TODO(port): Implement this method to work on other platforms as part of // off-screen rendering support. -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) CefBrowserContext* browserContext = static_cast(web_contents->GetBrowserContext()); @@ -385,7 +385,7 @@ CefContentBrowserClient::OverrideCreateWebContentsView( *render_view_host_delegate_view = view_or; view = view_or; } -#endif // OS_WIN +#endif // defined(OS_WIN) || defined(OS_MACOSX) return view; } diff --git a/cef3/libcef/browser/menu_creator_runner_mac.mm b/cef3/libcef/browser/menu_creator_runner_mac.mm index 7f21ee115..381ed217c 100644 --- a/cef3/libcef/browser/menu_creator_runner_mac.mm +++ b/cef3/libcef/browser/menu_creator_runner_mac.mm @@ -35,7 +35,9 @@ bool CefMenuCreatorRunnerMac::RunContextMenu(CefMenuCreator* manager) { // [NSApp currentEvent] will return a valid event. NSEvent* currentEvent = [NSApp currentEvent]; NSWindow* window = [parent_view window]; + NSPoint position = [window mouseLocationOutsideOfEventStream]; + NSTimeInterval eventTime = [currentEvent timestamp]; NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown location:position @@ -59,9 +61,33 @@ bool CefMenuCreatorRunnerMac::RunContextMenu(CefMenuCreator* manager) { base::mac::ScopedSendingEvent sendingEventScoper; // Show the menu. Blocks until the menu is dismissed. - [NSMenu popUpContextMenu:[menu_controller_ menu] - withEvent:clickEvent - forView:parent_view]; + if (manager->browser()->IsWindowRenderingDisabled()) { + // Showing the menu in OSR is pretty much self contained, only using + // the initialized menu_controller_ in this function, and the scoped + // variables in this block. + int screenX = 0, screenY = 0; + CefRefPtr handler = + manager->browser()->GetClient()->GetRenderHandler(); + if (!handler->GetScreenPoint(manager->browser(), + manager->params().x, manager->params().y, + screenX, screenY)) { + return false; + } + + // Don't show the menu unless there is a parent native window to tie it to + if (!manager->browser()->GetWindowHandle()) + return false; + + NSPoint screen_position = + NSPointFromCGPoint(gfx::Point(screenX, screenY).ToCGPoint()); + [[menu_controller_ menu] popUpMenuPositioningItem:nil + atLocation:screen_position + inView:nil]; + } else { + [NSMenu popUpContextMenu:[menu_controller_ menu] + withEvent:clickEvent + forView:parent_view]; + } } return true; diff --git a/cef3/libcef/browser/render_widget_host_view_osr.cc b/cef3/libcef/browser/render_widget_host_view_osr.cc index a4fab5cea..6ac10c7a7 100644 --- a/cef3/libcef/browser/render_widget_host_view_osr.cc +++ b/cef3/libcef/browser/render_widget_host_view_osr.cc @@ -19,17 +19,40 @@ #endif #include "webkit/glue/webcursor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" + +namespace { + +const float kDefaultScaleFactor = 1.0; + +static WebKit::WebScreenInfo webScreenInfoFrom(const CefScreenInfo& src) { + WebKit::WebScreenInfo webScreenInfo; + webScreenInfo.deviceScaleFactor = src.device_scale_factor; + webScreenInfo.depth = src.depth; + webScreenInfo.depthPerComponent = src.depth_per_component; + webScreenInfo.isMonochrome = src.is_monochrome; + webScreenInfo.rect = WebKit::WebRect(src.rect.x, src.rect.y, + src.rect.width, src.rect.height); + webScreenInfo.availableRect = WebKit::WebRect(src.available_rect.x, + src.available_rect.y, + src.available_rect.width, + src.available_rect.height); + + return webScreenInfo; +} + +} // namespace /////////////////////////////////////////////////////////////////////////////// // CefRenderWidgetHostViewOSR, public: CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( content::RenderWidgetHost* widget) - : render_widget_host_(content::RenderWidgetHostImpl::From(widget)), - about_to_validate_and_paint_(false), + : weak_factory_(this), + render_widget_host_(content::RenderWidgetHostImpl::From(widget)), parent_host_view_(NULL), popup_host_view_(NULL), - weak_factory_(this) { + about_to_validate_and_paint_(false) { DCHECK(render_widget_host_); render_widget_host_->SetView(this); @@ -60,7 +83,7 @@ void CefRenderWidgetHostViewOSR::SetBounds(const gfx::Rect& rect) { } gfx::NativeView CefRenderWidgetHostViewOSR::GetNativeView() const { - return browser_impl_.get() ? browser_impl_->GetWindowHandle() : NULL; + return NULL; } gfx::NativeViewId CefRenderWidgetHostViewOSR::GetNativeViewId() const { @@ -165,6 +188,12 @@ void CefRenderWidgetHostViewOSR::UpdateCursor(const WebCursor& cursor) { HCURSOR hCursor = web_cursor.GetCursor((HINSTANCE)hModule); browser_impl_->GetClient()->GetRenderHandler()->OnCursorChange( browser_impl_->GetBrowser(), hCursor); +#elif defined(OS_MACOSX) + // cursor is const, and GetNativeCursor is not + WebCursor web_cursor = cursor; + NSCursor* native_cursor = web_cursor.GetNativeCursor(); + browser_impl_->GetClient()->GetRenderHandler()->OnCursorChange( + browser_impl_->GetBrowser(), native_cursor); #else // TODO(port): Implement this method to work on other platforms as part of // off-screen rendering support. @@ -215,11 +244,37 @@ void CefRenderWidgetHostViewOSR::WillWmDestroy() { } #endif -void CefRenderWidgetHostViewOSR::GetScreenInfo( - WebKit::WebScreenInfo* results) { -#if defined(OS_WIN) - *results = WebKit::WebScreenInfoFactory::screenInfo(GetNativeView()); -#endif +void CefRenderWidgetHostViewOSR::GetScreenInfo(WebKit::WebScreenInfo* results) { + if (!browser_impl_.get()) + return; + + CefScreenInfo screen_info( + kDefaultScaleFactor, 0, 0, false, CefRect(), CefRect()); + + CefRefPtr handler = + browser_impl_->client()->GetRenderHandler(); + if (!handler->GetScreenInfo(browser_impl_.get(), screen_info) || + screen_info.rect.width == 0 || + screen_info.rect.height == 0 || + screen_info.available_rect.width == 0 || + screen_info.available_rect.height == 0) { + // If a screen rectangle was not provided, try using the view rectangle + // instead. Otherwise, popup views may be drawn incorrectly, or not at all. + CefRect screenRect; + if (!handler->GetViewRect(browser_impl_.get(), screenRect)) { + NOTREACHED(); + screenRect = CefRect(); + } + + if (screen_info.rect.width == 0 && screen_info.rect.height == 0) + screen_info.rect = screenRect; + + if (screen_info.available_rect.width == 0 && + screen_info.available_rect.height == 0) + screen_info.available_rect = screenRect; + } + + *results = webScreenInfoFrom(screen_info); } gfx::Rect CefRenderWidgetHostViewOSR::GetBoundsInRootWindow() { @@ -270,7 +325,7 @@ void CefRenderWidgetHostViewOSR::ScrollOffsetChanged() { content::BackingStore* CefRenderWidgetHostViewOSR::AllocBackingStore( const gfx::Size& size) { return render_widget_host_ ? - new BackingStoreOSR(render_widget_host_, size) : + new BackingStoreOSR(render_widget_host_, size, GetDeviceScaleFactor()) : NULL; } @@ -550,3 +605,79 @@ void CefRenderWidgetHostViewOSR::SendMouseWheelEvent( return; render_widget_host_->ForwardWheelEvent(event); } + +void CefRenderWidgetHostViewOSR::OnScreenInfoChanged() { + if (!render_widget_host_) + return; + + BackingStoreOSR* backing_store = + BackingStoreOSR::From(render_widget_host_->GetBackingStore(true)); + if (backing_store) + backing_store->ScaleFactorChanged(GetDeviceScaleFactor()); + + // What could be taken from UpdateScreenInfo(window_) - updates the renderer + // cached rectangles + //render_widget_host_->SendScreenRects(); + + render_widget_host_->NotifyScreenInfoChanged(); + // We might want to change the cursor scale factor here as well - see the + // cache for the current_cursor_, as passed by UpdateCursor from the renderer + // in the rwhv_aura (current_cursor_.SetScaleFactor) +} + +float CefRenderWidgetHostViewOSR::GetDeviceScaleFactor() { + if (!browser_impl_.get()) + return kDefaultScaleFactor; + + CefScreenInfo screen_info( + kDefaultScaleFactor, 0, 0, false, CefRect(), CefRect()); + if (!browser_impl_->GetClient()->GetRenderHandler()->GetScreenInfo( + browser_impl_->GetBrowser(), screen_info)) { + // Use the default + return kDefaultScaleFactor; + } + + return screen_info.device_scale_factor; +} + +#if defined(OS_MACOSX) +void CefRenderWidgetHostViewOSR::AboutToWaitForBackingStoreMsg() { +} + +bool CefRenderWidgetHostViewOSR::PostProcessEventForPluginIme( + const content::NativeWebKeyboardEvent& event) { + return false; +} +#endif + +#if defined(OS_MACOSX) +void CefRenderWidgetHostViewOSR::SetActive(bool active) { +} + +void CefRenderWidgetHostViewOSR::SetTakesFocusOnlyOnMouseDown(bool flag) { +} + +void CefRenderWidgetHostViewOSR::SetWindowVisibility(bool visible) { +} + +void CefRenderWidgetHostViewOSR::WindowFrameChanged() { +} + +void CefRenderWidgetHostViewOSR::ShowDefinitionForSelection() { +} + + +bool CefRenderWidgetHostViewOSR::SupportsSpeech() const { + return false; +} + +void CefRenderWidgetHostViewOSR::SpeakSelection() { +} + +bool CefRenderWidgetHostViewOSR::IsSpeaking() const { + return false; +} + +void CefRenderWidgetHostViewOSR::StopSpeaking() { +} +#endif // defined(OS_MACOSX) diff --git a/cef3/libcef/browser/render_widget_host_view_osr.h b/cef3/libcef/browser/render_widget_host_view_osr.h index 5b369872c..3c99023dd 100644 --- a/cef3/libcef/browser/render_widget_host_view_osr.h +++ b/cef3/libcef/browser/render_widget_host_view_osr.h @@ -57,6 +57,19 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase { virtual void Hide() OVERRIDE; virtual bool IsShowing() OVERRIDE; virtual gfx::Rect GetViewBounds() const OVERRIDE; +#if defined(OS_MACOSX) + virtual void SetActive(bool active) OVERRIDE; + virtual void SetTakesFocusOnlyOnMouseDown(bool flag) OVERRIDE; + virtual void SetWindowVisibility(bool visible) OVERRIDE; + virtual void WindowFrameChanged() OVERRIDE; + + virtual void ShowDefinitionForSelection() OVERRIDE; + + virtual bool SupportsSpeech() const OVERRIDE; + virtual void SpeakSelection() OVERRIDE; + virtual bool IsSpeaking() const OVERRIDE; + virtual void StopSpeaking() OVERRIDE; +#endif // defined(OS_MACOSX) // RenderWidgetHostViewPort methods. virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, @@ -130,6 +143,13 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase { virtual void SetClickthroughRegion(SkRegion* region) OVERRIDE; #endif +#if defined(OS_MACOSX) + virtual void AboutToWaitForBackingStoreMsg() OVERRIDE; + + virtual bool PostProcessEventForPluginIme( + const content::NativeWebKeyboardEvent& event) OVERRIDE; +#endif + // RenderWidgetHostViewBase methods. virtual void SetBackground(const SkBitmap& background) OVERRIDE; @@ -142,6 +162,9 @@ class CefRenderWidgetHostViewOSR : public content::RenderWidgetHostViewBase { void Paint(const std::vector& copy_rects); bool InstallTransparency(); + void OnScreenInfoChanged(); + float GetDeviceScaleFactor(); + void CancelWidget(); void NotifyShowWidget(); void NotifyHideWidget(); diff --git a/cef3/libcef/common/cef_messages.h b/cef3/libcef/common/cef_messages.h index 040064658..21adde6f3 100644 --- a/cef3/libcef/common/cef_messages.h +++ b/cef3/libcef/common/cef_messages.h @@ -150,6 +150,7 @@ IPC_SYNC_MESSAGE_CONTROL0_1( IPC_STRUCT_BEGIN(CefProcessHostMsg_GetNewBrowserInfo_Params) IPC_STRUCT_MEMBER(int, browser_id) IPC_STRUCT_MEMBER(bool, is_popup) + IPC_STRUCT_MEMBER(bool, is_window_rendering_disabled) IPC_STRUCT_END() // Retrieve information about a newly created browser window. diff --git a/cef3/libcef/renderer/content_renderer_client.cc b/cef3/libcef/renderer/content_renderer_client.cc index 78ca2b17c..b42303924 100644 --- a/cef3/libcef/renderer/content_renderer_client.cc +++ b/cef3/libcef/renderer/content_renderer_client.cc @@ -431,6 +431,15 @@ void CefContentRendererClient::RenderViewCreated( ¶ms)); DCHECK_GT(params.browser_id, 0); +#if defined(OS_MACOSX) + // FIXME: It would be better if this API would be a callback from the + // WebKit layer, or if it would be exposed as an WebView instance method; the + // current implementation uses a static variable, and WebKit needs to be + // patched in order to make it work for each WebView instance + render_view->GetWebView()->setUseExternalPopupMenusThisInstance( + !params.is_window_rendering_disabled); +#endif + CefRefPtr browser = new CefBrowserImpl(render_view, params.browser_id, params.is_popup); browsers_.insert(std::make_pair(render_view, browser)); diff --git a/cef3/libcef_dll/cpptoc/browser_host_cpptoc.cc b/cef3/libcef_dll/cpptoc/browser_host_cpptoc.cc index fc4db80d8..d4dbd5bab 100644 --- a/cef3/libcef_dll/cpptoc/browser_host_cpptoc.cc +++ b/cef3/libcef_dll/cpptoc/browser_host_cpptoc.cc @@ -348,6 +348,18 @@ void CEF_CALLBACK browser_host_was_hidden(struct _cef_browser_host_t* self, hidden?true:false); } +void CEF_CALLBACK browser_host_notify_screen_info_changed( + struct _cef_browser_host_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefBrowserHostCppToC::Get(self)->NotifyScreenInfoChanged(); +} + void CEF_CALLBACK browser_host_invalidate(struct _cef_browser_host_t* self, const cef_rect_t* dirtyRect, enum cef_paint_element_type_t type) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -518,6 +530,8 @@ CefBrowserHostCppToC::CefBrowserHostCppToC(CefBrowserHost* cls) browser_host_is_window_rendering_disabled; struct_.struct_.was_resized = browser_host_was_resized; struct_.struct_.was_hidden = browser_host_was_hidden; + struct_.struct_.notify_screen_info_changed = + browser_host_notify_screen_info_changed; struct_.struct_.invalidate = browser_host_invalidate; struct_.struct_.send_key_event = browser_host_send_key_event; struct_.struct_.send_mouse_click_event = browser_host_send_mouse_click_event; diff --git a/cef3/libcef_dll/cpptoc/render_handler_cpptoc.cc b/cef3/libcef_dll/cpptoc/render_handler_cpptoc.cc index c77bed238..61397bb90 100644 --- a/cef3/libcef_dll/cpptoc/render_handler_cpptoc.cc +++ b/cef3/libcef_dll/cpptoc/render_handler_cpptoc.cc @@ -127,6 +127,41 @@ int CEF_CALLBACK render_handler_get_screen_point( return _retval; } +int CEF_CALLBACK render_handler_get_screen_info( + struct _cef_render_handler_t* self, cef_browser_t* browser, + struct _cef_screen_info_t* screen_info) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return 0; + // Verify param: screen_info; type: struct_byref + DCHECK(screen_info); + if (!screen_info) + return 0; + + // Translate param: screen_info; type: struct_byref + CefScreenInfo screen_infoObj; + if (screen_info) + screen_infoObj.AttachTo(*screen_info); + + // Execute + bool _retval = CefRenderHandlerCppToC::Get(self)->GetScreenInfo( + CefBrowserCToCpp::Wrap(browser), + screen_infoObj); + + // Restore param: screen_info; type: struct_byref + if (screen_info) + screen_infoObj.DetachTo(*screen_info); + + // Return type: bool + return _retval; +} + void CEF_CALLBACK render_handler_on_popup_show( struct _cef_render_handler_t* self, cef_browser_t* browser, int show) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -239,6 +274,7 @@ CefRenderHandlerCppToC::CefRenderHandlerCppToC(CefRenderHandler* cls) struct_.struct_.get_root_screen_rect = render_handler_get_root_screen_rect; struct_.struct_.get_view_rect = render_handler_get_view_rect; struct_.struct_.get_screen_point = render_handler_get_screen_point; + struct_.struct_.get_screen_info = render_handler_get_screen_info; struct_.struct_.on_popup_show = render_handler_on_popup_show; struct_.struct_.on_popup_size = render_handler_on_popup_size; struct_.struct_.on_paint = render_handler_on_paint; diff --git a/cef3/libcef_dll/ctocpp/browser_host_ctocpp.cc b/cef3/libcef_dll/ctocpp/browser_host_ctocpp.cc index 021d3c8de..b5a0fabbc 100644 --- a/cef3/libcef_dll/ctocpp/browser_host_ctocpp.cc +++ b/cef3/libcef_dll/ctocpp/browser_host_ctocpp.cc @@ -290,6 +290,16 @@ void CefBrowserHostCToCpp::WasHidden(bool hidden) { hidden); } +void CefBrowserHostCToCpp::NotifyScreenInfoChanged() { + if (CEF_MEMBER_MISSING(struct_, notify_screen_info_changed)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + struct_->notify_screen_info_changed(struct_); +} + void CefBrowserHostCToCpp::Invalidate(const CefRect& dirtyRect, PaintElementType type) { if (CEF_MEMBER_MISSING(struct_, invalidate)) diff --git a/cef3/libcef_dll/ctocpp/browser_host_ctocpp.h b/cef3/libcef_dll/ctocpp/browser_host_ctocpp.h index 3cd89bdf1..94f9b3341 100644 --- a/cef3/libcef_dll/ctocpp/browser_host_ctocpp.h +++ b/cef3/libcef_dll/ctocpp/browser_host_ctocpp.h @@ -57,6 +57,7 @@ class CefBrowserHostCToCpp virtual bool IsWindowRenderingDisabled() OVERRIDE; virtual void WasResized() OVERRIDE; virtual void WasHidden(bool hidden) OVERRIDE; + virtual void NotifyScreenInfoChanged() OVERRIDE; virtual void Invalidate(const CefRect& dirtyRect, PaintElementType type) OVERRIDE; virtual void SendKeyEvent(const CefKeyEvent& event) OVERRIDE; diff --git a/cef3/libcef_dll/ctocpp/render_handler_ctocpp.cc b/cef3/libcef_dll/ctocpp/render_handler_ctocpp.cc index 9b268a101..7299cc312 100644 --- a/cef3/libcef_dll/ctocpp/render_handler_ctocpp.cc +++ b/cef3/libcef_dll/ctocpp/render_handler_ctocpp.cc @@ -82,6 +82,27 @@ bool CefRenderHandlerCToCpp::GetScreenPoint(CefRefPtr browser, return _retval?true:false; } +bool CefRenderHandlerCToCpp::GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { + if (CEF_MEMBER_MISSING(struct_, get_screen_info)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return false; + + // Execute + int _retval = struct_->get_screen_info(struct_, + CefBrowserCppToC::Wrap(browser), + &screen_info); + + // Return type: bool + return _retval?true:false; +} + void CefRenderHandlerCToCpp::OnPopupShow(CefRefPtr browser, bool show) { if (CEF_MEMBER_MISSING(struct_, on_popup_show)) diff --git a/cef3/libcef_dll/ctocpp/render_handler_ctocpp.h b/cef3/libcef_dll/ctocpp/render_handler_ctocpp.h index 0c00c187e..1052f2700 100644 --- a/cef3/libcef_dll/ctocpp/render_handler_ctocpp.h +++ b/cef3/libcef_dll/ctocpp/render_handler_ctocpp.h @@ -40,6 +40,8 @@ class CefRenderHandlerCToCpp CefRect& rect) OVERRIDE; virtual bool GetScreenPoint(CefRefPtr browser, int viewX, int viewY, int& screenX, int& screenY) OVERRIDE; + virtual bool GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) OVERRIDE; virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE; virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; diff --git a/cef3/patch/patch.cfg b/cef3/patch/patch.cfg index f8c3a1144..b0eb9ffd1 100644 --- a/cef3/patch/patch.cfg +++ b/cef3/patch/patch.cfg @@ -41,6 +41,11 @@ patches = [ 'name': 'gyp_331', 'path': '../tools/gyp/pylib/', }, + { + # Enable popups in offscreen rendering on Mac + 'name': 'webkit_popups', + 'path': '../third_party/WebKit/Source/WebKit/chromium/', + }, { # http://code.google.com/p/chromiumembedded/issues/detail?id=364 'name': 'spi_webcore_364', diff --git a/cef3/patch/patches/webkit_popups.patch b/cef3/patch/patches/webkit_popups.patch new file mode 100644 index 000000000..ef5978bc1 --- /dev/null +++ b/cef3/patch/patches/webkit_popups.patch @@ -0,0 +1,76 @@ +Index: src/WebViewImpl.cpp +=================================================================== +--- src/WebViewImpl.cpp (revision 147958) ++++ src/WebViewImpl.cpp (working copy) +@@ -407,6 +407,7 @@ + , m_fakeDoubleTapPageScaleFactor(0) + , m_fakeDoubleTapUseAnchor(false) + , m_contextMenuAllowed(false) ++ , m_shouldUseExternalPopupMenus(shouldUseExternalPopupMenus) + , m_doingDragAndDrop(false) + , m_ignoreInputEvents(false) + , m_suppressNextKeypressEvent(false) +@@ -3823,9 +3824,14 @@ + { + } + ++void WebViewImpl::setUseExternalPopupMenusThisInstance(bool useExternalPopupMenus) ++{ ++ m_shouldUseExternalPopupMenus = useExternalPopupMenus; ++} ++ + bool WebViewImpl::useExternalPopupMenus() + { +- return shouldUseExternalPopupMenus; ++ return m_shouldUseExternalPopupMenus; + } + + void WebViewImpl::setEmulatedTextZoomFactor(float textZoomFactor) +Index: src/WebViewImpl.h +=================================================================== +--- src/WebViewImpl.h (revision 147958) ++++ src/WebViewImpl.h (working copy) +@@ -422,7 +422,8 @@ + + // Returns true if popup menus should be rendered by the browser, false if + // they should be rendered by WebKit (which is the default). +- static bool useExternalPopupMenus(); ++ void setUseExternalPopupMenusThisInstance(bool); ++ bool useExternalPopupMenus(); + + bool contextMenuAllowed() const + { +@@ -768,6 +769,8 @@ + + bool m_contextMenuAllowed; + ++ bool m_shouldUseExternalPopupMenus; ++ + bool m_doingDragAndDrop; + + bool m_ignoreInputEvents; +Index: src/ChromeClientImpl.cpp +=================================================================== +--- src/ChromeClientImpl.cpp (revision 147958) ++++ src/ChromeClientImpl.cpp (working copy) +@@ -1008,7 +1008,7 @@ + + PassRefPtr ChromeClientImpl::createPopupMenu(PopupMenuClient* client) const + { +- if (WebViewImpl::useExternalPopupMenus()) ++ if (m_webView->useExternalPopupMenus()) + return adoptRef(new ExternalPopupMenu(client, m_webView->client())); + + return adoptRef(new PopupMenuChromium(client)); +Index: public/WebView.h +=================================================================== +--- public/WebView.h (revision 147958) ++++ public/WebView.h (working copy) +@@ -419,6 +419,7 @@ + + // Sets whether select popup menus should be rendered by the browser. + WEBKIT_EXPORT static void setUseExternalPopupMenus(bool); ++ virtual void setUseExternalPopupMenusThisInstance(bool) = 0; + + + // Visited link state -------------------------------------------------- diff --git a/cef3/tests/cefclient/cefclient.rc b/cef3/tests/cefclient/cefclient.rc index 61e0d43da..1a9b9dae2 100644 --- a/cef3/tests/cefclient/cefclient.rc +++ b/cef3/tests/cefclient/cefclient.rc @@ -33,6 +33,7 @@ IDS_DIALOGS BINARY "res\\dialogs.html" IDS_DOMACCESS BINARY "res\\domaccess.html" IDS_LOCALSTORAGE BINARY "res\\localstorage.html" IDS_LOGO BINARY "res\\logo.png" +IDS_OSRTEST BINARY "res\\osr_test.html" IDS_OTHER_TESTS BINARY "res\\other_tests.html" IDS_PERFORMANCE BINARY "res\\performance.html" IDS_TRANSPARENCY BINARY "res\\transparency.html" @@ -54,7 +55,7 @@ IDI_SMALL ICON "res\small.ico" // Menu // -IDC_CEFCLIENT MENU +IDC_CEFCLIENT MENU BEGIN POPUP "&File" BEGIN @@ -86,7 +87,7 @@ END // Accelerator // -IDC_CEFCLIENT ACCELERATORS +IDC_CEFCLIENT ACCELERATORS BEGIN "?", IDM_ABOUT, ASCII, ALT "/", IDM_ABOUT, ASCII, ALT @@ -116,12 +117,12 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" "#include ""windows.h""\r\n" @@ -129,7 +130,7 @@ BEGIN "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -143,7 +144,7 @@ END // String Table // -STRINGTABLE +STRINGTABLE BEGIN IDS_APP_TITLE "cefclient" IDC_CEFCLIENT "CEFCLIENT" diff --git a/cef3/tests/cefclient/cefclient_mac.mm b/cef3/tests/cefclient/cefclient_mac.mm index 09d8b0cb0..bd28f5b25 100644 --- a/cef3/tests/cefclient/cefclient_mac.mm +++ b/cef3/tests/cefclient/cefclient_mac.mm @@ -11,7 +11,9 @@ #include "include/cef_browser.h" #include "include/cef_frame.h" #include "include/cef_runnable.h" +#include "cefclient/cefclient_osr_widget_mac.h" #include "cefclient/client_handler.h" +#include "cefclient/client_switches.h" #include "cefclient/resource_util.h" #include "cefclient/scheme_test.h" #include "cefclient/string_util.h" @@ -19,6 +21,15 @@ // The global ClientHandler reference. extern CefRefPtr g_handler; +class MainBrowserProvider : public OSRBrowserProvider { + virtual CefRefPtr GetBrowser() { + if (g_handler.get()) + return g_handler->GetBrowser(); + + return NULL; + } +} g_main_browser_provider; + char szWorkingDir[512]; // The current working directory // Sizes for URL bar layout @@ -93,15 +104,15 @@ const int kWindowHeight = 600; - (IBAction)takeURLStringValueFrom:(NSTextField *)sender { if (!g_handler.get() || !g_handler->GetBrowserId()) return; - + NSString *url = [sender stringValue]; - + // if it doesn't already have a prefix, add http. If we can't parse it, // just don't bother rather than making things worse. NSURL* tempUrl = [NSURL URLWithString:url]; if (tempUrl && ![tempUrl scheme]) url = [@"http://" stringByAppendingString:url]; - + std::string urlStr = [url UTF8String]; g_handler->GetBrowser()->GetMainFrame()->LoadURL(urlStr); } @@ -176,7 +187,7 @@ const int kWindowHeight = 600; } // Deletes itself. -- (void)cleanup:(id)window { +- (void)cleanup:(id)window { [self release]; } @@ -215,10 +226,10 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { - (void)createApp:(id)object { [NSApplication sharedApplication]; [NSBundle loadNibNamed:@"MainMenu" owner:NSApp]; - + // Set the delegate for application events. [NSApp setDelegate:self]; - + // Add the Tests menu. NSMenu* menubar = [NSApp mainMenu]; NSMenuItem *testItem = [[[NSMenuItem alloc] initWithTitle:@"Tests" @@ -260,10 +271,10 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { keyEquivalent:@""]; [testItem setSubmenu:testMenu]; [menubar addItem:testItem]; - + // Create the delegate for control and browser window events. ClientWindowDelegate* delegate = [[ClientWindowDelegate alloc] init]; - + // Create the main application window. NSRect screen_rect = [[NSScreen mainScreen] visibleFrame]; NSRect window_rect = { {0, screen_rect.size.height - kWindowHeight}, @@ -332,7 +343,22 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { CefWindowInfo window_info; CefBrowserSettings settings; - window_info.SetAsChild(contentView, 0, 0, kWindowWidth, kWindowHeight); + if (AppIsOffScreenRenderingEnabled()) { + CefRefPtr cmd_line = AppGetCommandLine(); + bool transparent = + cmd_line->HasSwitch(cefclient::kTransparentPaintingEnabled); + + CefRefPtr osr_window = + OSRWindow::Create(&g_main_browser_provider, transparent, contentView, + CefRect(0, 0, kWindowWidth, kWindowHeight)); + window_info.SetAsOffScreen(osr_window->GetWindowHandle()); + window_info.SetTransparentPainting(transparent); + g_handler->SetOSRHandler(osr_window->GetRenderHandler().get()); + } else { + // Initialize window info to the defaults for a child window. + window_info.SetAsChild(contentView, 0, 0, kWindowWidth, kWindowHeight); + } + CefBrowserHost::CreateBrowser(window_info, g_handler.get(), g_handler->GetStartupURL(), settings); @@ -407,7 +433,7 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { RunOtherTests(g_handler->GetBrowser()); } -// Called when the application’s Quit menu item is selected. +// Called when the application's Quit menu item is selected. - (NSApplicationTerminateReply)applicationShouldTerminate: (NSApplication *)sender { // Request that all browser windows close. @@ -445,7 +471,7 @@ int main(int argc, char* argv[]) { // Initialize the ClientApplication instance. [ClientApplication sharedApplication]; - + // Parse command line arguments. AppInitCommandLine(argc, argv); diff --git a/cef3/tests/cefclient/cefclient_osr_widget_mac.h b/cef3/tests/cefclient/cefclient_osr_widget_mac.h new file mode 100644 index 000000000..afaea346e --- /dev/null +++ b/cef3/tests/cefclient/cefclient_osr_widget_mac.h @@ -0,0 +1,125 @@ +// Copyright (c) 2013 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. + +#ifndef CEF_TESTS_CEFCLIENT_CEFCLIENT_OSR_WIDGET_MAC_H_ +#define CEF_TESTS_CEFCLIENT_CEFCLIENT_OSR_WIDGET_MAC_H_ + +#include "include/cef_client.h" +#include "cefclient/client_handler.h" + +class ClientOSRenderer; + +class OSRBrowserProvider { + public: + virtual CefRefPtr GetBrowser() =0; + + protected: + virtual ~OSRBrowserProvider() {} +}; + +// The client OpenGL view. +@interface ClientOpenGLView : NSOpenGLView { +@public + NSTrackingArea* tracking_area_; + OSRBrowserProvider* browser_provider_; + ClientOSRenderer* renderer_; + NSPoint last_mouse_pos_; + NSPoint cur_mouse_pos_; + bool rotating_; + + bool was_last_mouse_down_on_view_; +} + +- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency; +- (NSPoint)getClickPointForEvent:(NSEvent*)event; +- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event; +- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event; +- (int)getModifiersForEvent:(NSEvent*)event; +- (BOOL)isKeyUpEvent:(NSEvent*)event; +- (BOOL)isKeyPadEvent:(NSEvent*)event; +- (CefRefPtr)getBrowser; +@end + +// Handler for off-screen rendering windows. +class ClientOSRHandler : public ClientHandler::RenderHandler { + public: + explicit ClientOSRHandler(ClientOpenGLView* view, + OSRBrowserProvider* browser_provider); + virtual ~ClientOSRHandler(); + + void Disconnect(); + + // ClientHandler::RenderHandler + virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE; + + // CefRenderHandler methods + + virtual bool GetViewRect(CefRefPtr browser, + CefRect& rect) OVERRIDE; + + virtual bool GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY) OVERRIDE; + virtual bool GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) OVERRIDE; + + virtual void OnPopupShow(CefRefPtr browser, + bool show) OVERRIDE; + + virtual void OnPopupSize(CefRefPtr browser, + const CefRect& rect) OVERRIDE; + + virtual void OnPaint(CefRefPtr browser, + PaintElementType type, + const RectList& dirtyRects, + const void* buffer, + int width, int height) OVERRIDE; + + virtual void OnCursorChange(CefRefPtr browser, + CefCursorHandle cursor) OVERRIDE; + + CefWindowHandle view() { return view_; } + + private: + void SetLoading(bool isLoading); + + ClientOpenGLView* view_; + + bool painting_popup_; + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(ClientOSRHandler); +}; + +class OSRWindow { + public: + static CefRefPtr Create(OSRBrowserProvider* browser_provider, + bool transparent, + CefWindowHandle parentView, + const CefRect& frame); + + CefRefPtr GetRenderHandler() { + return render_client.get(); + } + + CefWindowHandle GetWindowHandle() { return view_; } + + private: + OSRWindow(OSRBrowserProvider* browser_provider, + bool transparent, + CefWindowHandle parentView, + const CefRect& frame); + + ~OSRWindow(); + + CefRefPtr render_client; + CefWindowHandle view_; + + IMPLEMENT_REFCOUNTING(OSRWindow); +}; + +#endif // CEF_TESTS_CEFCLIENT_CEFCLIENT_OSR_WIDGET_MAC_H_ + diff --git a/cef3/tests/cefclient/cefclient_osr_widget_mac.mm b/cef3/tests/cefclient/cefclient_osr_widget_mac.mm new file mode 100644 index 000000000..0f601e036 --- /dev/null +++ b/cef3/tests/cefclient/cefclient_osr_widget_mac.mm @@ -0,0 +1,905 @@ +// Copyright (c) 2013 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. + +#import +#include + +#include "cefclient/cefclient_osr_widget_mac.h" + +#include "include/cef_browser.h" +#include "include/cef_client.h" +#include "cefclient/cefclient.h" +#include "cefclient/osrenderer.h" +#include "cefclient/resource_util.h" +#include "cefclient/util.h" + +@interface ClientOpenGLView () +- (float)getDeviceScaleFactor; +- (void)windowDidChangeBackingProperties:(NSNotification*)notification; + +- (bool) isOverPopupWidgetX: (int) x andY: (int) y; +- (void) applyPopupOffsetToX: (int&) x andY: (int&) y; +- (int) getPopupXOffset; +- (int) getPopupYOffset; + +- (void) sendMouseClick: (NSEvent *)event + button: (CefBrowserHost::MouseButtonType)type + isUp: (bool)isUp; + +- (void) convertRects: (const CefRenderHandler::RectList&) rects + toBackingRects: (CefRenderHandler::RectList*) scaled_rects + andSize: (const NSSize) size + toBackingSize: (NSSize*) scaled_size; + +- (CefRect) convertRectToBackingInternal: (const CefRect&) rect; +- (CefRect) convertRectFromBackingInternal: (const CefRect&) rect; + +@property (readwrite, atomic) bool was_last_mouse_down_on_view; +@end + +namespace { + +static CefRect convertRect(const NSRect& target, const NSRect& frame) { + NSRect rect = target; + rect.origin.y = NSMaxY(frame) - NSMaxY(target); + return CefRect(rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height); +} + +} // namespace + +ClientOSRHandler::ClientOSRHandler(ClientOpenGLView* view, + OSRBrowserProvider* browser_provider) + : view_(view), + painting_popup_(false) { + [view_ retain]; + view_->browser_provider_ = browser_provider; + +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + // Do not override the delegate; the mac client already does that + if ([view_ respondsToSelector:@selector(backingScaleFactor:)]) { + [[NSNotificationCenter defaultCenter] + addObserver:view_ + selector:@selector(windowDidChangeBackingProperties:) + name:NSWindowDidChangeBackingPropertiesNotification + object:[view_ window]]; + } +#endif +} + +ClientOSRHandler:: ~ClientOSRHandler() { +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([view_ respondsToSelector:@selector(backingScaleFactor:)]) { + [[NSNotificationCenter defaultCenter] + removeObserver:view_ + name:NSWindowDidChangeBackingPropertiesNotification + object:[view_ window]]; + } +#endif +} + +void ClientOSRHandler::Disconnect() { + [view_ release]; + view_ = nil; +} + +// CefRenderHandler methods +void ClientOSRHandler::OnBeforeClose(CefRefPtr browser) { + if (view_) + view_->browser_provider_ = NULL; +} + +bool ClientOSRHandler::GetViewRect(CefRefPtr browser, + CefRect& rect) { + REQUIRE_UI_THREAD(); + + if (!view_) + return false; + + // The simulated screen and view rectangle are the same. This is necessary + // for popup menus to be located and sized inside the view. + const NSRect bounds = [view_ bounds]; + rect.x = rect.y = 0; + rect.width = bounds.size.width; + rect.height = bounds.size.height; + return true; +} + +bool ClientOSRHandler::GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY) { + REQUIRE_UI_THREAD(); + + if (!view_) + return false; + + // Convert the point from view coordinates to actual screen coordinates. + NSRect bounds = [view_ bounds]; + NSPoint view_pt = NSMakePoint(viewX, bounds.size.height - viewY); + NSPoint window_pt = [view_ convertPoint:view_pt toView:nil]; + NSPoint screen_pt = [[view_ window] convertBaseToScreen:window_pt]; + screenX = screen_pt.x; + screenY = screen_pt.y; + return true; +} + +bool ClientOSRHandler::GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { + REQUIRE_UI_THREAD(); + + if (!view_) + return false; + + NSWindow* window = [view_ window]; + if (!window) + return false; + + screen_info.device_scale_factor = [view_ getDeviceScaleFactor]; + + NSScreen* screen = [window screen]; + if (!screen) + screen = [NSScreen deepestScreen]; + + screen_info.depth = NSBitsPerPixelFromDepth([screen depth]); + screen_info.depth_per_component = NSBitsPerSampleFromDepth([screen depth]); + screen_info.is_monochrome = + [[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel; + // screen_info.is_monochrome = true; + screen_info.rect = convertRect([screen frame], [screen frame]); + screen_info.available_rect = + convertRect([screen visibleFrame], [screen frame]); + + return true; +} + +void ClientOSRHandler::OnPopupShow(CefRefPtr browser, + bool show) { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + if (!show) { + CefRect client_popup_rect = view_->renderer_->popup_rect(); + + // Clear the popup rectangles, so that the paint triggered by Invalidate + // will not repaint the popup content over the OpenGL view. + view_->renderer_->ClearPopupRects(); + CefRect scaled_rect = + [view_ convertRectFromBackingInternal:client_popup_rect]; + browser->GetHost()->Invalidate(scaled_rect, PET_VIEW); + } + + view_->renderer_->OnPopupShow(browser, show); +} + +void ClientOSRHandler::OnPopupSize(CefRefPtr browser, + const CefRect& rect) { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + CefRect scaled_rect = [view_ convertRectToBackingInternal:rect]; + view_->renderer_->OnPopupSize(browser, scaled_rect); +} + +void ClientOSRHandler::OnPaint(CefRefPtr browser, + PaintElementType type, + const RectList& dirtyRects, + const void* buffer, + int width, int height) { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + if (painting_popup_) { + RectList scaled_dirty_rects; + NSSize scaled_size = NSMakeSize(0, 0); + [view_ convertRects:dirtyRects + toBackingRects:&scaled_dirty_rects + andSize:NSMakeSize(width, height) + toBackingSize:&scaled_size]; + view_->renderer_->OnPaint(browser, type, scaled_dirty_rects, buffer, + scaled_size.width, scaled_size.height); + return; + } + + NSOpenGLContext* context = [view_ openGLContext]; + [context makeCurrentContext]; + + RectList scaled_dirty_rects; + NSSize scaled_size = NSMakeSize(0, 0); + [view_ convertRects:dirtyRects + toBackingRects:&scaled_dirty_rects + andSize:NSMakeSize(width, height) + toBackingSize:&scaled_size]; + + view_->renderer_->OnPaint(browser, type, scaled_dirty_rects, buffer, + scaled_size.width, scaled_size.height); + + if (type == PET_VIEW && !view_->renderer_->popup_rect().IsEmpty()) { + painting_popup_ = true; + CefRect client_popup_rect(0, 0, + view_->renderer_->popup_rect().width, + view_->renderer_->popup_rect().height); + + CefRect scaled_popup_rect = + [view_ convertRectFromBackingInternal:client_popup_rect]; + + browser->GetHost()->Invalidate(scaled_popup_rect, PET_POPUP); + painting_popup_ = false; + } + + view_->renderer_->Render(); + [context flushBuffer]; +} + +void ClientOSRHandler::OnCursorChange(CefRefPtr browser, + CefCursorHandle cursor) { + REQUIRE_UI_THREAD(); + [cursor set]; +} + +void ClientOSRHandler::SetLoading(bool isLoading) { +} + +@implementation ClientOpenGLView + +@synthesize was_last_mouse_down_on_view = was_last_mouse_down_on_view_; + +- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency { + NSOpenGLPixelFormat * pixelFormat = + [[NSOpenGLPixelFormat alloc] + initWithAttributes:(NSOpenGLPixelFormatAttribute[]) { + NSOpenGLPFAWindow, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, + 32, + 0}]; + [pixelFormat autorelease]; + + self = [super initWithFrame:frame pixelFormat:pixelFormat]; + if (self) { + renderer_ = new ClientOSRenderer(transparency); + rotating_ = false; + + tracking_area_ = + [[NSTrackingArea alloc] initWithRect:frame + options:NSTrackingMouseMoved | + NSTrackingActiveInActiveApp | + NSTrackingInVisibleRect + owner:self + userInfo:nil]; + [self addTrackingArea:tracking_area_]; + } + +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([self respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { + // enable HiDPI buffer + [self setWantsBestResolutionOpenGLSurface:YES]; + } +#endif + + return self; +} + +- (void)dealloc { + CefRefPtr browser = [self getBrowser]; + if (browser) { + static_cast( + browser->GetHost()->GetClient()->GetRenderHandler().get())->Disconnect(); + browser->GetHost()->CloseBrowser(true); + browser = NULL; + } + if (renderer_) + delete renderer_; + + [super dealloc]; +} + +- (CefRefPtr)getBrowser { + return browser_provider_->GetBrowser(); +} + +- (void)setFrame:(NSRect)frameRect { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + [super setFrame:frameRect]; + browser->GetHost()->WasResized(); +} + +- (void) sendMouseClick:(NSEvent *)event + button: (CefBrowserHost::MouseButtonType)type + isUp: (bool)isUp { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + CefMouseEvent mouseEvent; + [self getMouseEvent: mouseEvent forEvent: event]; + NSPoint point = [self getClickPointForEvent:event]; + if (!isUp) + self.was_last_mouse_down_on_view = ![self isOverPopupWidgetX: point.x + andY: point.y]; + else if (self.was_last_mouse_down_on_view && + [self isOverPopupWidgetX:point.x andY: point.y] && + ([self getPopupXOffset] || [self getPopupYOffset])) { + return; + } + + browser->GetHost()->SendMouseClickEvent(mouseEvent, + type, + isUp, + [event clickCount]); +} + +- (void)mouseDown:(NSEvent *)event { + [self sendMouseClick: event button:MBT_LEFT isUp:false]; +} + +- (void)rightMouseDown:(NSEvent *)event { + if ([event modifierFlags] & NSShiftKeyMask) { + // Start rotation effect. + last_mouse_pos_ = cur_mouse_pos_ = [self getClickPointForEvent:event]; + rotating_ = true; + return; + } + + [self sendMouseClick: event button:MBT_RIGHT isUp:false]; +} + +- (void)otherMouseDown:(NSEvent *)event { + [self sendMouseClick: event button:MBT_MIDDLE isUp:false]; +} + +- (void)mouseUp:(NSEvent *)event { + [self sendMouseClick: event button: MBT_LEFT isUp: true]; +} + +- (void)rightMouseUp:(NSEvent *)event { + if (rotating_) { + // End rotation effect. + renderer_->SetSpin(0, 0); + rotating_ = false; + [self setNeedsDisplay:YES]; + return; + } + [self sendMouseClick: event button: MBT_RIGHT isUp: true]; +} + +- (void)otherMouseUp:(NSEvent *)event { + [self sendMouseClick: event button: MBT_MIDDLE isUp: true]; +} + +- (void)mouseMoved:(NSEvent *)event { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + if (rotating_) { + // Apply rotation effect. + cur_mouse_pos_ = [self getClickPointForEvent:event];; + renderer_->IncrementSpin((cur_mouse_pos_.x - last_mouse_pos_.x), + (cur_mouse_pos_.y - last_mouse_pos_.y)); + last_mouse_pos_ = cur_mouse_pos_; + [self setNeedsDisplay:YES]; + return; + } + + CefMouseEvent mouseEvent; + [self getMouseEvent: mouseEvent forEvent: event]; + browser->GetHost()->SendMouseMoveEvent(mouseEvent, false); +} + +- (void)mouseDragged:(NSEvent *)event { + [self mouseMoved:event]; +} + +- (void)rightMouseDragged:(NSEvent *)event { + [self mouseMoved:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event { + [self mouseMoved:event]; +} + +- (void)mouseEntered:(NSEvent *)event { + [self mouseMoved:event]; +} + +- (void)mouseExited:(NSEvent *)event { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + CefMouseEvent mouseEvent; + [self getMouseEvent: mouseEvent forEvent: event]; + browser->GetHost()->SendMouseMoveEvent(mouseEvent, true); +} + +- (void)keyDown:(NSEvent *)event { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + CefKeyEvent keyEvent; + [self getKeyEvent:keyEvent forEvent:event]; + + keyEvent.type = KEYEVENT_KEYDOWN; + browser->GetHost()->SendKeyEvent(keyEvent); + + if ([event modifierFlags] & (NSNumericPadKeyMask | NSFunctionKeyMask)) { + // Don't send a Char event for non-char keys like arrows, function keys and + // clear. + switch (keyEvent.native_key_code) { + case 81: // = + case 75: // / + case 67: // * + case 78: // - + case 69: // + + case 76: // Enter + case 65: // . + case 82: // 0 + case 83: // 1 + case 84: // 2 + case 85: // 3 + case 86: // 4 + case 87: // 5 + case 88: // 6 + case 89: // 7 + case 91: // 8 + case 92: // 9 + break; + default: + return; + } + } + + keyEvent.type = KEYEVENT_CHAR; + browser->GetHost()->SendKeyEvent(keyEvent); +} + +- (void)keyUp:(NSEvent *)event { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + CefKeyEvent keyEvent; + [self getKeyEvent:keyEvent forEvent:event]; + + keyEvent.type = KEYEVENT_KEYUP; + browser->GetHost()->SendKeyEvent(keyEvent); +} + +- (void)flagsChanged:(NSEvent *)event { + if ([self isKeyUpEvent:event]) + [self keyUp:event]; + else + [self keyDown:event]; +} + +- (void)scrollWheel:(NSEvent *)event { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + CGEventRef cgEvent = [event CGEvent]; + ASSERT(cgEvent); + + int deltaX = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2); + int deltaY = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); + + CefMouseEvent mouseEvent; + [self getMouseEvent: mouseEvent forEvent: event]; + browser->GetHost()->SendMouseWheelEvent(mouseEvent, deltaX, deltaY); +} + +- (BOOL)canBecomeKeyView { + CefRefPtr browser = [self getBrowser]; + return (browser != NULL); +} + +- (BOOL)acceptsFirstResponder { + CefRefPtr browser = [self getBrowser]; + return (browser != NULL); +} + +- (BOOL)becomeFirstResponder { + CefRefPtr browser = [self getBrowser]; + if (browser) { + browser->GetHost()->SendFocusEvent(true); + return [super becomeFirstResponder]; + } + + return NO; +} + +- (BOOL)resignFirstResponder { + CefRefPtr browser = [self getBrowser]; + if (browser) { + browser->GetHost()->SendFocusEvent(false); + return [super resignFirstResponder]; + } + + return NO; +} + +- (void)undo:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Undo(); +} + +- (void)redo:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Redo(); +} + +- (void)cut:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Cut(); +} + +- (void)copy:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Copy(); +} + +- (void)paste:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Paste(); +} + +- (void)delete:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->Delete(); +} + +- (void)selectAll:(id)sender { + CefRefPtr browser = [self getBrowser]; + if (browser) + browser->GetFocusedFrame()->SelectAll(); +} + +- (NSPoint)getClickPointForEvent:(NSEvent*)event { + NSPoint windowLocal = [event locationInWindow]; + NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil]; + + NSPoint point; + point.x = contentLocal.x; + point.y = [self frame].size.height - contentLocal.y; // Flip y. + return point; +} + +- (void)getKeyEvent:(CefKeyEvent &)keyEvent forEvent:(NSEvent *)event { + if ([event type] == NSKeyDown || [event type] == NSKeyUp) { + NSString* s = [event characters]; + if ([s length] > 0) + keyEvent.character = [s characterAtIndex:0]; + + s = [event charactersIgnoringModifiers]; + if ([s length] > 0) + keyEvent.unmodified_character = [s characterAtIndex:0]; + } + + if ([event type] == NSFlagsChanged) { + keyEvent.character = 0; + keyEvent.unmodified_character = 0; + } + + keyEvent.native_key_code = [event keyCode]; + + keyEvent.modifiers = [self getModifiersForEvent:event]; +} + +- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event { + NSPoint point = [self getClickPointForEvent:event]; + mouseEvent.x = point.x; + mouseEvent.y = point.y; + + if ([self isOverPopupWidgetX:mouseEvent.x andY: mouseEvent.y]) { + [self applyPopupOffsetToX:mouseEvent.x andY: mouseEvent.y]; + } + + mouseEvent.modifiers = [self getModifiersForEvent:event]; +} + +- (int)getModifiersForEvent:(NSEvent*)event { + int modifiers = 0; + + if ([event modifierFlags] & NSControlKeyMask) + modifiers |= EVENTFLAG_CONTROL_DOWN; + if ([event modifierFlags] & NSShiftKeyMask) + modifiers |= EVENTFLAG_SHIFT_DOWN; + if ([event modifierFlags] & NSAlternateKeyMask) + modifiers |= EVENTFLAG_ALT_DOWN; + if ([event modifierFlags] & NSCommandKeyMask) + modifiers |= EVENTFLAG_COMMAND_DOWN; + if ([event modifierFlags] & NSAlphaShiftKeyMask) + modifiers |= EVENTFLAG_CAPS_LOCK_ON; + + if ([event type] == NSKeyUp || + [event type] == NSKeyDown || + [event type] == NSFlagsChanged) { + // Only perform this check for key events + if ([self isKeyPadEvent:event]) + modifiers |= EVENTFLAG_IS_KEY_PAD; + } + + // OS X does not have a modifier for NumLock, so I'm not entirely sure how to + // set EVENTFLAG_NUM_LOCK_ON; + // + // There is no EVENTFLAG for the function key either. + + // Mouse buttons + switch ([event type]) { + case NSLeftMouseDragged: + case NSLeftMouseDown: + case NSLeftMouseUp: + modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON; + break; + case NSRightMouseDragged: + case NSRightMouseDown: + case NSRightMouseUp: + modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON; + break; + case NSOtherMouseDragged: + case NSOtherMouseDown: + case NSOtherMouseUp: + modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON; + break; + } + + return modifiers; +} + +- (BOOL)isKeyUpEvent:(NSEvent*)event { + if ([event type] != NSFlagsChanged) + return [event type] == NSKeyUp; + + // FIXME: This logic fails if the user presses both Shift keys at once, for + // example: we treat releasing one of them as keyDown. + switch ([event keyCode]) { + case 54: // Right Command + case 55: // Left Command + return ([event modifierFlags] & NSCommandKeyMask) == 0; + + case 57: // Capslock + return ([event modifierFlags] & NSAlphaShiftKeyMask) == 0; + + case 56: // Left Shift + case 60: // Right Shift + return ([event modifierFlags] & NSShiftKeyMask) == 0; + + case 58: // Left Alt + case 61: // Right Alt + return ([event modifierFlags] & NSAlternateKeyMask) == 0; + + case 59: // Left Ctrl + case 62: // Right Ctrl + return ([event modifierFlags] & NSControlKeyMask) == 0; + + case 63: // Function + return ([event modifierFlags] & NSFunctionKeyMask) == 0; + } + return false; +} + +- (BOOL)isKeyPadEvent:(NSEvent*)event { + if ([event modifierFlags] & NSNumericPadKeyMask) + return true; + + switch ([event keyCode]) { + case 71: // Clear + case 81: // = + case 75: // / + case 67: // * + case 78: // - + case 69: // + + case 76: // Enter + case 65: // . + case 82: // 0 + case 83: // 1 + case 84: // 2 + case 85: // 3 + case 86: // 4 + case 87: // 5 + case 88: // 6 + case 89: // 7 + case 91: // 8 + case 92: // 9 + return true; + } + + return false; +} + +- (void)windowDidChangeBackingProperties:(NSNotification*)notification { +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + // This delegate method is only called on 10.7 and later, so don't worry about + // other backing changes calling it on 10.6 or earlier + CGFloat newBackingScaleFactor = [self getDeviceScaleFactor]; + NSNumber* oldBackingScaleFactor = + [[notification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey]; + if (newBackingScaleFactor != [oldBackingScaleFactor doubleValue]) { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + browser->GetHost()->NotifyScreenInfoChanged(); + } +#endif +} + +- (void)drawRect: (NSRect) dirtyRect { + // The Invalidate below fixes flicker when resizing + if ([self inLiveResize]) { + CefRefPtr browser = [self getBrowser]; + if (!browser) + return; + + NSRect b = [self bounds]; + CefRect boundsRect = CefRect((int)b.origin.x, + (int)b.origin.y, + (int)b.size.width, + (int)b.size.height); + browser->GetHost()->Invalidate(boundsRect, PET_VIEW); + } +} + +// Utility - private +- (float)getDeviceScaleFactor { + float deviceScaleFactor = 1; + NSWindow* window = [self window]; + if (!window) + return deviceScaleFactor; + +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([window respondsToSelector:@selector(backingScaleFactor)]) + deviceScaleFactor = [window backingScaleFactor]; + else +#endif + deviceScaleFactor = [window userSpaceScaleFactor]; + + return deviceScaleFactor; +} + +- (bool) isOverPopupWidgetX: (int) x andY: (int) y { + CefRect rc = [self convertRectFromBackingInternal:renderer_->popup_rect()]; + int popup_right = rc.x + rc.width; + int popup_bottom = rc.y + rc.height; + return (x >= rc.x) && (x < popup_right) && + (y >= rc.y) && (y < popup_bottom); +} + +- (int) getPopupXOffset { + int original_x = + [self convertRectFromBackingInternal:renderer_->original_popup_rect()].x; + int popup_x = + [self convertRectFromBackingInternal:renderer_->popup_rect()].x; + + return original_x - popup_x; +} + +- (int) getPopupYOffset { + int original_y = + [self convertRectFromBackingInternal:renderer_->original_popup_rect()].y; + int popup_y = + [self convertRectFromBackingInternal:renderer_->popup_rect()].y; + + return original_y - popup_y; +} + +- (void) applyPopupOffsetToX: (int&) x andY: (int&) y { + if ([self isOverPopupWidgetX:x andY:y]) { + x += [self getPopupXOffset]; + y += [self getPopupYOffset]; + } +} + +- (void) convertRects: (const CefRenderHandler::RectList&) rects + toBackingRects: (CefRenderHandler::RectList*) scaled_rects + andSize: (const NSSize) size + toBackingSize: (NSSize*) scaled_size { + *scaled_rects = rects; + *scaled_size = size; + +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([self getDeviceScaleFactor] != 1 && + [self respondsToSelector:@selector(convertSizeToBacking:)] && + [self respondsToSelector:@selector(convertRectToBacking:)]) { + CefRenderHandler::RectList scaled_dirty_rects; + for (size_t i = 0; i < rects.size(); ++i) { + scaled_dirty_rects.push_back([self convertRectToBackingInternal:rects[i]]); + } + + *scaled_rects = scaled_dirty_rects; + *scaled_size = [self convertSizeToBacking:size]; + } +#endif +} + +- (CefRect) convertRectToBackingInternal: (const CefRect&) rect { + if ([self getDeviceScaleFactor] == 1) + return rect; + +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([self respondsToSelector:@selector(convertRectToBacking:)]) { + NSRect old_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + NSRect scaled_rect = [self convertRectToBacking:old_rect]; + return CefRect((int)scaled_rect.origin.x, + (int)scaled_rect.origin.y, + (int)scaled_rect.size.width, + (int)scaled_rect.size.height); + } +#endif + + return rect; +} + +- (CefRect) convertRectFromBackingInternal: (const CefRect&) rect { +#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ + __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if ([self respondsToSelector:@selector(convertRectFromBacking:)]) { + NSRect old_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + NSRect scaled_rect = [self convertRectFromBacking:old_rect]; + return CefRect((int)scaled_rect.origin.x, + (int)scaled_rect.origin.y, + (int)scaled_rect.size.width, + (int)scaled_rect.size.height); + } +#endif + + return rect; +} + +@end + + +CefRefPtr OSRWindow::Create(OSRBrowserProvider* browser_provider, + bool transparent, + CefWindowHandle parentView, + const CefRect& frame) { + return new OSRWindow(browser_provider, transparent, parentView, frame); +} + +OSRWindow::OSRWindow(OSRBrowserProvider* browser_provider, + bool transparent, + CefWindowHandle parentView, + const CefRect& frame) { + NSRect window_rect = NSMakeRect(frame.x, frame.y, frame.width, frame.height); + ClientOpenGLView* view = [[ClientOpenGLView alloc] initWithFrame:window_rect + andTransparency:transparent]; + this->view_ = view; + [parentView addSubview:view]; + [view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [view setAutoresizesSubviews: true]; + + this->render_client = new ClientOSRHandler(view, browser_provider); +} + +OSRWindow::~OSRWindow() { +} diff --git a/cef3/tests/cefclient/cefclient_osr_widget_win.cpp b/cef3/tests/cefclient/cefclient_osr_widget_win.cpp index 14709c59f..2c950746a 100644 --- a/cef3/tests/cefclient/cefclient_osr_widget_win.cpp +++ b/cef3/tests/cefclient/cefclient_osr_widget_win.cpp @@ -99,6 +99,11 @@ bool OSRWindow::GetScreenPoint(CefRefPtr browser, void OSRWindow::OnPopupShow(CefRefPtr browser, bool show) { + if (!show) { + CefRect dirty_rect = renderer_.popup_rect(); + renderer_.ClearPopupRects(); + browser->GetHost()->Invalidate(dirty_rect, PET_VIEW); + } renderer_.OnPopupShow(browser, show); } diff --git a/cef3/tests/cefclient/client_handler.cpp b/cef3/tests/cefclient/client_handler.cpp index 3b5630eb8..28a7f40dc 100644 --- a/cef3/tests/cefclient/client_handler.cpp +++ b/cef3/tests/cefclient/client_handler.cpp @@ -3,10 +3,13 @@ // can be found in the LICENSE file. #include "cefclient/client_handler.h" -#include #include +#include +#include #include #include +#include + #include "include/cef_browser.h" #include "include/cef_frame.h" #include "include/cef_path_util.h" @@ -517,6 +520,13 @@ bool ClientHandler::GetScreenPoint(CefRefPtr browser, return m_OSRHandler->GetScreenPoint(browser, viewX, viewY, screenX, screenY); } +bool ClientHandler::GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { + if (!m_OSRHandler.get()) + return false; + return m_OSRHandler->GetScreenInfo(browser, screen_info); +} + void ClientHandler::OnPopupShow(CefRefPtr browser, bool show) { if (!m_OSRHandler.get()) diff --git a/cef3/tests/cefclient/client_handler.h b/cef3/tests/cefclient/client_handler.h index ef8afe962..40c643956 100644 --- a/cef3/tests/cefclient/client_handler.h +++ b/cef3/tests/cefclient/client_handler.h @@ -194,6 +194,8 @@ class ClientHandler : public CefClient, int viewY, int& screenX, int& screenY) OVERRIDE; + virtual bool GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) OVERRIDE; virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE; virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE; diff --git a/cef3/tests/cefclient/osrenderer.cpp b/cef3/tests/cefclient/osrenderer.cpp index a6f3409d3..dabfe3b4e 100644 --- a/cef3/tests/cefclient/osrenderer.cpp +++ b/cef3/tests/cefclient/osrenderer.cpp @@ -143,11 +143,7 @@ void ClientOSRenderer::OnPopupShow(CefRefPtr browser, bool show) { if (!show) { // Clear the popup rectangle. - CefRect popup_rect = popup_rect_; - popup_rect_.Set(0, 0, 0, 0); - original_popup_rect_.Set(0, 0, 0, 0); - // Invalidate the previous popup rectangle so that it will be repainted. - browser->GetHost()->Invalidate(popup_rect, PET_VIEW); + ClearPopupRects(); } } @@ -179,6 +175,11 @@ CefRect ClientOSRenderer::GetPopupRectInWebView(const CefRect& original_rect) { return rc; } +void ClientOSRenderer::ClearPopupRects() { + popup_rect_.Set(0, 0, 0, 0); + original_popup_rect_.Set(0, 0, 0, 0); +} + void ClientOSRenderer::OnPaint(CefRefPtr browser, CefRenderHandler::PaintElementType type, const CefRenderHandler::RectList& dirtyRects, diff --git a/cef3/tests/cefclient/osrenderer.h b/cef3/tests/cefclient/osrenderer.h index f549cf183..7bf585a3f 100644 --- a/cef3/tests/cefclient/osrenderer.h +++ b/cef3/tests/cefclient/osrenderer.h @@ -47,6 +47,7 @@ class ClientOSRenderer { const CefRect& original_popup_rect() const { return original_popup_rect_; } CefRect GetPopupRectInWebView(const CefRect& original_rect); + void ClearPopupRects(); private: bool transparent_; diff --git a/cef3/tests/cefclient/res/osr_test.html b/cef3/tests/cefclient/res/osr_test.html new file mode 100644 index 000000000..9d78e9529 --- /dev/null +++ b/cef3/tests/cefclient/res/osr_test.html @@ -0,0 +1,69 @@ + + OSR Test + + + +

+ OSR Testing h1 - Focus and blur + + this page and will get this red black +

+
    +
  1. OnPaint should be called each time a page loads
  2. +
  3. Move mouse + to require an OnCursorChange call
  4. +
  5. Hover will color this with + red. Will trigger OnPaint once on enter and once on leave
  6. +
  7. Right clicking will show contextual menu and will request + GetScreenPoint
  8. +
  9. IsWindowRenderingDisabled should be true
  10. +
  11. WasResized should trigger full repaint if size changes. +
  12. +
  13. Invalidate should trigger OnPaint once
  14. +
  15. Click and write here with SendKeyEvent to trigger repaints: +
  16. +
  17. Click here with SendMouseClickEvent to navigate: +
  18. +
  19. Mouse over this element will + trigger show a tooltip
  20. +
+
+
+
+
+
+
+ + diff --git a/cef3/tests/cefclient/resource.h b/cef3/tests/cefclient/resource.h index e565d057e..1baea58a7 100644 --- a/cef3/tests/cefclient/resource.h +++ b/cef3/tests/cefclient/resource.h @@ -44,11 +44,12 @@ #define IDS_LOCALSTORAGE 1003 #define IDS_LOGO 1004 #define IDS_LOGOBALL 1005 -#define IDS_OTHER_TESTS 1006 -#define IDS_PERFORMANCE 1007 -#define IDS_TRANSPARENCY 1008 -#define IDS_WINDOW 1009 -#define IDS_XMLHTTPREQUEST 1010 +#define IDS_OSRTEST 1006 +#define IDS_OTHER_TESTS 1007 +#define IDS_PERFORMANCE 1008 +#define IDS_TRANSPARENCY 1009 +#define IDS_WINDOW 1010 +#define IDS_XMLHTTPREQUEST 1011 // Avoid files associated with MacOS #define _X86_ diff --git a/cef3/tests/cefclient/resource_util_mac.mm b/cef3/tests/cefclient/resource_util_mac.mm index c4652a0cf..1d53639cd 100644 --- a/cef3/tests/cefclient/resource_util_mac.mm +++ b/cef3/tests/cefclient/resource_util_mac.mm @@ -29,7 +29,7 @@ bool AmIBundled() { ASSERT(false); return false; } - + return (info.nodeFlags & kFSNodeIsDirectoryMask); } @@ -41,7 +41,7 @@ bool GetResourceDir(std::string& dir) { // Retrieve the executable directory. uint32_t pathSize = 0; _NSGetExecutablePath(NULL, &pathSize); - if (pathSize > 0) { + if (pathSize > 0) { dir.resize(pathSize); _NSGetExecutablePath(const_cast(dir.c_str()), &pathSize); } diff --git a/cef3/tests/cefclient/resource_util_win.cpp b/cef3/tests/cefclient/resource_util_win.cpp index b8f7a5ff9..5cc129428 100644 --- a/cef3/tests/cefclient/resource_util_win.cpp +++ b/cef3/tests/cefclient/resource_util_win.cpp @@ -38,6 +38,7 @@ int GetResourceId(const char* resource_name) { {"domaccess.html", IDS_DOMACCESS}, {"localstorage.html", IDS_LOCALSTORAGE}, {"logo.png", IDS_LOGO}, + {"osr_test.html", IDS_OSRTEST}, {"other_tests.html", IDS_OTHER_TESTS}, {"performance.html", IDS_PERFORMANCE}, {"transparency.html", IDS_TRANSPARENCY}, diff --git a/cef3/tests/unittests/os_rendering_unittest.cc b/cef3/tests/unittests/os_rendering_unittest.cc index 7523eab19..ebbd56851 100644 --- a/cef3/tests/unittests/os_rendering_unittest.cc +++ b/cef3/tests/unittests/os_rendering_unittest.cc @@ -3,63 +3,31 @@ // can be found in the LICENSE file. #include "include/cef_runnable.h" +#include "include/cef_v8.h" +#include "include/wrapper/cef_stream_resource_handler.h" + +#include "tests/cefclient/client_app.h" +#include "tests/cefclient/resource_util.h" #include "tests/unittests/test_handler.h" +#include "base/logging.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/base/keycodes/keyboard_code_conversion.h" + +#if defined(OS_MACOSX) +#include "tests/unittests/os_rendering_unittest_mac.h" +#elif defined(OS_WIN) +// Required for resource_util_win, which uses this as an extern +HINSTANCE hInst = ::GetModuleHandle(NULL); +#endif + namespace { -const char kTestUrl[] = "http://tests/OSRTest"; +const char kTestUrl[] = "http://tests/osrtest"; // this html should render on a 600 x 400 window with a little vertical // offset with scrollbar. -const char kOsrHtml[] = -" " -" OSR Test " -" " -" " -" " -"

OSR Testing h1 - Focus and blur " -" " -" this page and will get this red black

" -"
    " -"
  1. OnPaint should be called each time a page loads
  2. " -"
  3. Move mouse " -" to require an OnCursorChange call
  4. " -"
  5. Hover will color this with " -" red. Will trigger OnPaint once on enter and once on leave
  6. " -"
  7. Right clicking will show contextual menu and will request " -" GetScreenPoint
  8. " -"
  9. IsWindowRenderingDisabled should be true
  10. " -"
  11. WasResized should trigger full repaint if size changes. " -"
  12. " -"
  13. Invalidate should trigger OnPaint once
  14. " -"
  15. Click and write here with SendKeyEvent to trigger repaints: " -"
  16. " -"
  17. Click here with SendMouseClickEvent to navigate: " -"
  18. " -"
  19. Mouse over this element will " -" trigger show a tooltip
  20. " -"
" -"
" -"
" -"
" -"
" -" " -" "; - // #define DEBUGGER_ATTACHED // default osr widget size @@ -67,6 +35,7 @@ const int kOsrWidth = 600; const int kOsrHeight = 400; // precomputed bounding client rects for html elements (h1 and li). +#if defined(OS_WIN) const CefRect kExpectedRectLI[] = { CefRect(8, 8, 567, 74), // LI00 CefRect(27, 103, 548, 20), // LI01 @@ -80,17 +49,44 @@ const CefRect kExpectedRectLI[] = { CefRect(27, 269, 548, 26), // LI09 CefRect(27, 295, 548, 20), // LI10 }; +#elif defined(OS_MACOSX) +const CefRect kExpectedRectLI[] = { + CefRect(8, 8, 584, 74), // LI00 + CefRect(28, 103, 564, 18), // LI01 + CefRect(28, 121, 564, 18), // LI02 + CefRect(28, 139, 564, 18), // LI03 + CefRect(28, 157, 564, 18), // LI04 + CefRect(28, 175, 564, 18), // LI05 + CefRect(28, 193, 564, 18), // LI06 + CefRect(28, 211, 564, 18), // LI07 + CefRect(28, 229, 564, 23), // LI08 + CefRect(28, 252, 564, 26), // LI09 + CefRect(20, 278, 572, 18), // LI10 +}; +#else +#error "Unsupported platform" +#endif // defined(OS_WIN) // bounding client rects for edit box and navigate button +#if defined(OS_WIN) const CefRect kEditBoxRect(412, 245, 153, 22); const CefRect kNavigateButtonRect(360, 271, 140, 22); const CefRect kSelectRect(467, 22, 75, 20); const CefRect kExpandedSelectRect(467, 42, 81, 322); const int kVerticalScrollbarWidth = GetSystemMetrics(SM_CXVSCROLL); const int kHorizontalScrollbarWidth = GetSystemMetrics(SM_CXHSCROLL); +#elif defined(OS_MACOSX) +const CefRect kEditBoxRect(429, 228, 129, 25); +const CefRect kNavigateButtonRect(375, 251, 138, 28); +const CefRect kSelectRect(461, 21, 87, 26); +const CefRect kExpandedSelectRect(467, 42, 80, 262); +#else +#error "Unsupported platform" +#endif // defined(OS_WIN) // adjusted expected rect regarding system vertical scrollbar width CefRect ExpectedRect(int index) { +#if defined(OS_WIN) // this is the scrollbar width for all the kExpectedRectLI const int kDefaultVerticalScrollbarWidth = 17; if (kDefaultVerticalScrollbarWidth == kVerticalScrollbarWidth) @@ -100,11 +96,23 @@ CefRect ExpectedRect(int index) { adjustedRect.width += kDefaultVerticalScrollbarWidth - kVerticalScrollbarWidth; return adjustedRect; +#elif defined(OS_MACOSX) + return kExpectedRectLI[index]; +#else +#error "Unsupported platform" +#endif } // word to be written into edit box const char kKeyTestWord[] = "done"; +const ui::KeyboardCode kKeyTestCodes[] = { + ui::VKEY_D, + ui::VKEY_O, + ui::VKEY_N, + ui::VKEY_E +}; + // width for the icon that appear on the screen when pressing // middle mouse button const int kMiddleButtonIconWidth = 16; @@ -141,6 +149,9 @@ enum OSRTestType { OSR_TEST_TOOLTIP, // mouse wheel will trigger a scroll event OSR_TEST_SCROLLING, + // Right click will trigger a context menu, and on destroying the test, it + // should not crash + OSR_TEST_CONTEXT_MENU, // clicking on dropdown box, PET_POPUP OnPaint is triggered OSR_TEST_POPUP_PAINT, // clicking on dropdown box, a popup will show up @@ -174,7 +185,6 @@ class OSRTestHandler : public TestHandler, // TestHandler methods virtual void RunTest() OVERRIDE { - AddResource(kTestUrl, kOsrHtml, "text/html"); CreateOSRBrowser(kTestUrl); #if !defined(DEBUGGER_ATTACHED) // Each test has a 5 second timeout. After this timeout it will be destroyed @@ -196,13 +206,61 @@ class OSRTestHandler : public TestHandler, virtual void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) OVERRIDE { - if (test_type_ == OSR_TEST_KEY_EVENTS && started()) { - std::string expectedUrl = std::string(kTestUrl) + "?k=" + kKeyTestWord; - EXPECT_EQ(frame->GetURL(), expectedUrl); - DestroySucceededTestSoon(); + if (!started()) + return; + + switch(test_type_) { + case OSR_TEST_KEY_EVENTS: + EXPECT_EQ(frame->GetURL(), std::string(kTestUrl) + "?k=" + kKeyTestWord); + DestroySucceededTestSoon(); + break; + default: + // Intentionally left blank + break; } } + virtual bool OnProcessMessageReceived( + CefRefPtr browser, + CefProcessId source_process, + CefRefPtr message) OVERRIDE { + EXPECT_TRUE(browser.get()); + EXPECT_EQ(PID_RENDERER, source_process); + EXPECT_TRUE(message.get()); + EXPECT_TRUE(message->IsReadOnly()); + + if (!started()) + return false; + + const CefString kMessageName = "osrtest"; + EXPECT_EQ(kMessageName, message->GetName()); + + CefString stringParam = message->GetArgumentList()->GetString(0); + switch(test_type_) { + case OSR_TEST_FOCUS: + EXPECT_EQ(stringParam, std::string("focus")); + DestroySucceededTestSoon(); + break; + case OSR_TEST_CLICK_LEFT: + EXPECT_EQ(stringParam, std::string("click0")); + DestroySucceededTestSoon(); + break; + case OSR_TEST_CLICK_MIDDLE: + EXPECT_EQ(stringParam, std::string("click1")); + DestroySucceededTestSoon(); + break; + case OSR_TEST_MOUSE_MOVE: + EXPECT_EQ(stringParam, std::string("mousemove")); + DestroySucceededTestSoon(); + break; + default: + // Intentionally left blank + break; + } + + return true; + } + // CefClient methods, providing handlers virtual CefRefPtr GetRenderHandler() OVERRIDE { return this; @@ -212,6 +270,26 @@ class OSRTestHandler : public TestHandler, return this; } + virtual CefRefPtr GetRequestHandler() OVERRIDE { + return this; + } + + virtual CefRefPtr GetResourceHandler( + CefRefPtr browser, + CefRefPtr frame, + CefRefPtr request) OVERRIDE { + std::string url = request->GetURL(); + + if (url.find("http://tests/osrtest") == 0) { + // Show the osr test contents + CefRefPtr stream = + GetBinaryResourceReader("osr_test.html"); + return new CefStreamResourceHandler("text/html", stream); + } + + return NULL; + } + // CefRenderHandler methods virtual bool GetViewRect(CefRefPtr browser, CefRect& rect) OVERRIDE { @@ -232,11 +310,29 @@ class OSRTestHandler : public TestHandler, EXPECT_EQ(viewX, MiddleX(ExpectedRect(4))); EXPECT_EQ(viewY, MiddleY(ExpectedRect(4))); DestroySucceededTestSoon(); + } else if (test_type_ == OSR_TEST_CONTEXT_MENU && started()){ + screenX = 0; + screenY = 0; + return true; } // we don't want to see a contextual menu. stop here. return false; } + virtual bool GetScreenInfo(CefRefPtr browser, + CefScreenInfo& screen_info) { + screen_info.device_scale_factor = 1; + + // The screen info rectangles are used by the renderer to create and + // position popups. If not overwritten in this function, the rectangle from + // returned GetViewRect will be used to popuplate them. + // The popup in the test fits without modifications in the test window, so + // setting the screen to the test window size does not affect its rectangle. + screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight); + screen_info.available_rect = CefRect(0, 0, kOsrWidth, kOsrHeight); + return true; + } + virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE { if (show && started()) { @@ -247,6 +343,8 @@ class OSRTestHandler : public TestHandler, DestroySucceededTestSoon(); } break; + default: + break; } } if (!show && started()) { @@ -257,6 +355,8 @@ class OSRTestHandler : public TestHandler, case OSR_TEST_POPUP_HIDE_ON_SCROLL: DestroySucceededTestSoon(); break; + default: + break; } } } @@ -269,6 +369,8 @@ class OSRTestHandler : public TestHandler, EXPECT_EQ(kExpandedSelectRect, rect); DestroySucceededTestSoon(); break; + default: + break; } } } @@ -301,26 +403,22 @@ class OSRTestHandler : public TestHandler, switch (test_type_) { case OSR_TEST_PAINT: // test that we have a full repaint - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0])); EXPECT_EQ(*(reinterpret_cast(buffer)), 0xffff8080); DestroySucceededTestSoon(); break; case OSR_TEST_TRANSPARENCY: // test that we have a full repaint - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0])); - EXPECT_EQ(*(reinterpret_cast(buffer)), 0x7f7f0000); + EXPECT_EQ(*(reinterpret_cast(buffer)), 0x7f7f0000U); DestroySucceededTestSoon(); break; case OSR_TEST_FOCUS: if (StartTest()) { // body.onfocus will make LI00 red browser->GetHost()->SendFocusEvent(true); - } else { - EXPECT_EQ(dirtyRects.size(), 1); - EXPECT_EQ(dirtyRects[0], ExpectedRect(0)); - DestroySucceededTestSoon(); } break; case OSR_TEST_CURSOR: @@ -344,14 +442,11 @@ class OSRTestHandler : public TestHandler, mouse_event.y = MiddleY(ExpectedRect(3)); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); - } else { - EXPECT_EQ(dirtyRects.size(), 1); - EXPECT_EQ(dirtyRects[0], ExpectedRect(3)); - DestroySucceededTestSoon(); } break; case OSR_TEST_CLICK_RIGHT: case OSR_TEST_SCREEN_POINT: + case OSR_TEST_CONTEXT_MENU: if (StartTest()) { CefMouseEvent mouse_event; mouse_event.x = MiddleX(ExpectedRect(4)); @@ -366,17 +461,14 @@ class OSRTestHandler : public TestHandler, case OSR_TEST_CLICK_LEFT: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(kEditBoxRect); - mouse_event.y = MiddleY(kEditBoxRect); + mouse_event.x = MiddleX(ExpectedRect(0)); + mouse_event.y = MiddleY(ExpectedRect(0)); + mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, false, 1); browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_LEFT, true, 1); - } else { - EXPECT_EQ(dirtyRects.size(), 1); - EXPECT_EQ(dirtyRects[0], kEditBoxRect); - DestroySucceededTestSoon(); } break; case OSR_TEST_CLICK_MIDDLE: @@ -390,7 +482,7 @@ class OSRTestHandler : public TestHandler, browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_MIDDLE, true, 1); } else { - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); CefRect expectedRect( MiddleX(ExpectedRect(0)) - kMiddleButtonIconWidth / 2, MiddleY(ExpectedRect(0)) - kMiddleButtonIconWidth / 2, @@ -405,7 +497,7 @@ class OSRTestHandler : public TestHandler, } else { EXPECT_EQ(kOsrWidth * 2, width); EXPECT_EQ(kOsrHeight * 2, height); - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0], width, height)); DestroySucceededTestSoon(); } @@ -419,7 +511,7 @@ class OSRTestHandler : public TestHandler, invalidating = false; } else { EXPECT_TRUE(invalidating); - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_EQ(dirtyRects[0], invalidate_rect); DestroySucceededTestSoon(); } @@ -442,21 +534,35 @@ class OSRTestHandler : public TestHandler, event.is_system_key = false; event.modifiers = 0; - size_t word_lenght = strlen(kKeyTestWord); - for (size_t i = 0; i < word_lenght; ++i) { + size_t word_length = strlen(kKeyTestWord); + for (size_t i = 0; i < word_length; ++i) { +#if defined(OS_WIN) BYTE VkCode = LOBYTE(VkKeyScanA(kKeyTestWord[i])); UINT scanCode = MapVirtualKey(VkCode, MAPVK_VK_TO_VSC); event.native_key_code = (scanCode << 16) | // key scan code - 1; // key repeat count + 1; // key repeat count event.windows_key_code = VkCode; +#elif defined(OS_MACOSX) + osr_unittests::GetKeyEvent(event, kKeyTestCodes[i], 0); +#else + NOTREACHED(); +#endif event.type = KEYEVENT_RAWKEYDOWN; browser->GetHost()->SendKeyEvent(event); +#if defined(OS_WIN) event.windows_key_code = kKeyTestWord[i]; +#elif defined(OS_MACOSX) + osr_unittests::GetKeyEvent(event, kKeyTestCodes[i], 0); +#endif event.type = KEYEVENT_CHAR; browser->GetHost()->SendKeyEvent(event); +#if defined(OS_WIN) event.windows_key_code = VkCode; // bits 30 and 31 should be always 1 for WM_KEYUP event.native_key_code |= 0xC0000000; +#elif defined(OS_MACOSX) + osr_unittests::GetKeyEvent(event, kKeyTestCodes[i], 0); +#endif event.type = KEYEVENT_KEYUP; browser->GetHost()->SendKeyEvent(event); } @@ -488,20 +594,38 @@ class OSRTestHandler : public TestHandler, mouse_event.modifiers = 0; browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, - deltaY); } else { +#if defined(OS_WIN) // there should be 3 update areas: // 1) vertical scrollbar // 2) discovered new area (bottom side) // 3) the whole visible rect. - EXPECT_EQ(dirtyRects.size(), 3); + EXPECT_EQ(dirtyRects.size(), 3U); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, kOsrWidth - kVerticalScrollbarWidth, kHorizontalScrollbarWidth)); + EXPECT_EQ(dirtyRects[1], CefRect(0, kHorizontalScrollbarWidth, kOsrWidth, kOsrHeight - 2 * kHorizontalScrollbarWidth)); + EXPECT_EQ(dirtyRects[2], CefRect(0, kOsrHeight - kHorizontalScrollbarWidth, kOsrWidth - kVerticalScrollbarWidth, kHorizontalScrollbarWidth)); DestroySucceededTestSoon(); +#elif defined(OS_MACOSX) + // On Mac, when scrollbars are Always on, there is a single update of + // the whole view + // When scrollbars are set to appear 'When scrolling', the first + // rectangle is of the whole view, and next comes a longer sequence of + // updates, each with one rectangle update for the whole scrollbar + // rectangle(584,0,16,400) + // Validating the first, full update passes in both cases + EXPECT_EQ(dirtyRects.size(), 1U); + EXPECT_EQ(dirtyRects[0], CefRect(0, 0, + kOsrWidth, kOsrHeight)); + DestroySucceededTestSoon(); +#else +#error "Unsupported platform" +#endif // defined(OS_WIN) } break; } @@ -544,11 +668,17 @@ class OSRTestHandler : public TestHandler, } else if (type == PET_POPUP) { CefKeyEvent event; event.is_system_key = false; +#if defined(OS_WIN) BYTE VkCode = LOBYTE(VK_ESCAPE); UINT scanCode = MapVirtualKey(VkCode, MAPVK_VK_TO_VSC); event.native_key_code = (scanCode << 16) | // key scan code 1; // key repeat count event.windows_key_code = VkCode; +#elif defined(OS_MACOSX) + osr_unittests::GetKeyEvent(event, ui::VKEY_ESCAPE, 0); +#else +#error "Unsupported platform" +#endif // defined(OS_WIN) event.type = KEYEVENT_CHAR; browser->GetHost()->SendKeyEvent(event); } @@ -563,7 +693,7 @@ class OSRTestHandler : public TestHandler, if (StartTest()) { ExpandDropDown(); } else if (type == PET_POPUP) { - EXPECT_EQ(dirtyRects.size(), 1); + EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, kExpandedSelectRect.width, @@ -576,28 +706,46 @@ class OSRTestHandler : public TestHandler, } break; case OSR_TEST_POPUP_SCROLL_INSIDE: - static enum {NotStarted, Started, Scrolled} - scroll_inside_state = NotStarted; - if (StartTest()) { - ExpandDropDown(); - scroll_inside_state = Started; - } else if (type == PET_POPUP) { - if (scroll_inside_state == Started) { - CefMouseEvent mouse_event; - mouse_event.x = MiddleX(kExpandedSelectRect); - mouse_event.y = MiddleY(kExpandedSelectRect); - mouse_event.modifiers = 0; - browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10); - scroll_inside_state = Scrolled; - } else if (scroll_inside_state == Scrolled) { - EXPECT_EQ(dirtyRects.size(), 1); - // border is not redrawn - EXPECT_EQ(dirtyRects[0], CefRect(1, 1, - kExpandedSelectRect.width - 3, - kExpandedSelectRect.height - 2)); - DestroySucceededTestSoon(); + { + static enum {NotStarted, Started, Scrolled} + scroll_inside_state = NotStarted; + if (StartTest()) { + ExpandDropDown(); + scroll_inside_state = Started; + } else if (type == PET_POPUP) { + if (scroll_inside_state == Started) { + CefMouseEvent mouse_event; + mouse_event.x = MiddleX(kExpandedSelectRect); + mouse_event.y = MiddleY(kExpandedSelectRect); + mouse_event.modifiers = 0; + browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10); + scroll_inside_state = Scrolled; + } else if (scroll_inside_state == Scrolled) { + EXPECT_EQ(dirtyRects.size(), 1U); +#if defined(OS_WIN) + // border is not redrawn + EXPECT_EQ(dirtyRects[0], + CefRect(1, + 1, + kExpandedSelectRect.width - 3, + kExpandedSelectRect.height - 2)); +#elif defined(OS_MACOSX) + EXPECT_EQ(dirtyRects[0], + CefRect(1, + 0, + kExpandedSelectRect.width - 3, + kExpandedSelectRect.height - 1)); +#else +#error "Unsupported platform" +#endif // defined(OS_WIN) + + DestroySucceededTestSoon(); + } } } + break; + default: + break; } } @@ -621,10 +769,15 @@ class OSRTestHandler : public TestHandler, CefRefPtr frame, CefRefPtr params, CefRefPtr model) OVERRIDE { - if (test_type_ == OSR_TEST_CLICK_RIGHT && started()) { + if (!started()) + return; + if (test_type_ == OSR_TEST_CLICK_RIGHT) { EXPECT_EQ(params->GetXCoord(), MiddleX(ExpectedRect(4))); EXPECT_EQ(params->GetYCoord(), MiddleY(ExpectedRect(4))); DestroySucceededTestSoon(); + } else if (test_type_ == OSR_TEST_CONTEXT_MENU) { + // This test will pass if it does not crash on destruction + DestroySucceededTestSoon(); } } @@ -632,7 +785,18 @@ class OSRTestHandler : public TestHandler, void CreateOSRBrowser(const CefString& url) { CefWindowInfo windowInfo; CefBrowserSettings settings; +#if defined(OS_WIN) windowInfo.SetAsOffScreen(GetDesktopWindow()); +#elif defined(OS_MACOSX) + // An actual vies is needed only for the ContextMenu test. The menu runner + // checks if the view is not nil before showing the context menu. + if (test_type_ == OSR_TEST_CONTEXT_MENU) + windowInfo.SetAsOffScreen(osr_unittests::GetFakeView()); + else + windowInfo.SetAsOffScreen(NULL); +#else +#error "Unsupported platform" +#endif if (test_type_ == OSR_TEST_TRANSPARENCY) windowInfo.SetTransparentPainting(TRUE); CefBrowserHost::CreateBrowser(windowInfo, this, url, settings); @@ -727,6 +891,7 @@ OSR_TEST(Invalidate, OSR_TEST_INVALIDATE); OSR_TEST(KeyEvents, OSR_TEST_KEY_EVENTS); OSR_TEST(Tooltip, OSR_TEST_TOOLTIP); OSR_TEST(Scrolling, OSR_TEST_SCROLLING); +OSR_TEST(ContextMenu, OSR_TEST_CONTEXT_MENU); OSR_TEST(PopupPaint, OSR_TEST_POPUP_PAINT); OSR_TEST(PopupShow, OSR_TEST_POPUP_SHOW); OSR_TEST(PopupSize, OSR_TEST_POPUP_SIZE); diff --git a/cef3/tests/unittests/os_rendering_unittest_mac.h b/cef3/tests/unittests/os_rendering_unittest_mac.h new file mode 100644 index 000000000..011580e56 --- /dev/null +++ b/cef3/tests/unittests/os_rendering_unittest_mac.h @@ -0,0 +1,18 @@ +// Copyright (c) 2013 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. + +#ifndef CEF_TESTS_UNITTESTS_OS_RENDERING_UNITTEST_MAC_H_ +#define CEF_TESTS_UNITTESTS_OS_RENDERING_UNITTEST_MAC_H_ + +#include "include/cef_base.h" +#include "ui/base/keycodes/keyboard_codes.h" + +namespace osr_unittests { + +CefWindowHandle GetFakeView(); +void GetKeyEvent(CefKeyEvent& event, ui::KeyboardCode keyCode, int modifiers); + +} // namespace osr_unittests + +#endif diff --git a/cef3/tests/unittests/os_rendering_unittest_mac.mm b/cef3/tests/unittests/os_rendering_unittest_mac.mm new file mode 100644 index 000000000..b76a22896 --- /dev/null +++ b/cef3/tests/unittests/os_rendering_unittest_mac.mm @@ -0,0 +1,39 @@ +// Copyright (c) 2013 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. + +#import +#import + +#include "os_rendering_unittest_mac.h" + +#include "ui/base/keycodes/keyboard_code_conversion_mac.h" + +#include "include/cef_base.h" + +namespace osr_unittests { + +CefWindowHandle GetFakeView() { + NSScreen *mainScreen = [NSScreen mainScreen]; + NSRect screenRect = [mainScreen visibleFrame]; + NSView* fakeView = [[NSView alloc] initWithFrame: screenRect]; + return fakeView; +} + +void GetKeyEvent(CefKeyEvent& event, ui::KeyboardCode keyCode, int modifiers) { + unichar character; + unichar unmodified_character; + + // TODO(port): translate modifiers from the input format to NSFlags + // MacKeyCodeForWindowsKeyCode takes a NSUinteger as flags. + int macKeyCode = ui::MacKeyCodeForWindowsKeyCode(keyCode, + modifiers, + &character, + &unmodified_character); + + event.native_key_code = macKeyCode; + event.character = character; + event.unmodified_character = unmodified_character; +} + +} // namespace osr_unittests diff --git a/cef3/tools/distrib/cefclient.gyp b/cef3/tools/distrib/cefclient.gyp index 581c4223b..de0c5e4e5 100644 --- a/cef3/tools/distrib/cefclient.gyp +++ b/cef3/tools/distrib/cefclient.gyp @@ -170,6 +170,7 @@ 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', '$(CONFIGURATION)/libcef.dylib', ], },