diff --git a/cef1/cef.gyp b/cef1/cef.gyp index ab0db5332..7af5ccffc 100644 --- a/cef1/cef.gyp +++ b/cef1/cef.gyp @@ -212,6 +212,7 @@ 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', ], }, 'sources': [ @@ -490,6 +491,7 @@ ], 'xcode_settings': { 'INSTALL_PATH': '@executable_path', + 'DYLIB_INSTALL_NAME_BASE': '@executable_path', # The libcef_static target contains ObjC categories. Passing the -ObjC flag # is necessary to properly load them and avoid a "selector not recognized" # runtime error. See http://developer.apple.com/library/mac/#qa/qa1490/_index.html diff --git a/cef1/cef_paths2.gypi b/cef1/cef_paths2.gypi index e8abb29c0..fcd5efc05 100644 --- a/cef1/cef_paths2.gypi +++ b/cef1/cef_paths2.gypi @@ -111,6 +111,8 @@ 'tests/cefclient/client_handler_win.cpp', 'tests/cefclient/clientplugin.cpp', 'tests/cefclient/clientplugin.h', + 'tests/cefclient/osrenderer.cpp', + 'tests/cefclient/osrenderer.h', 'tests/cefclient/osrplugin.cpp', 'tests/cefclient/osrplugin.h', 'tests/cefclient/osrplugin_test.cpp', @@ -135,6 +137,10 @@ 'cefclient_sources_mac': [ 'tests/cefclient/cefclient_mac.mm', 'tests/cefclient/client_handler_mac.mm', + 'tests/cefclient/osrenderer.cpp', + 'tests/cefclient/osrenderer.h', + 'tests/cefclient/osrtest_mac.h', + 'tests/cefclient/osrtest_mac.mm', 'tests/cefclient/resource_util_mac.mm', ], 'cefclient_bundle_resources_mac': [ @@ -146,6 +152,9 @@ 'tests/cefclient/res/extensionperf.html', 'tests/cefclient/res/localstorage.html', 'tests/cefclient/res/logo.png', + 'tests/cefclient/res/logoball.png', + 'tests/cefclient/res/osrtest.html', + 'tests/cefclient/res/transparency.html', 'tests/cefclient/res/xmlhttprequest.html', ], 'cefclient_sources_linux': [ diff --git a/cef1/include/capi/cef_browser_capi.h b/cef1/include/capi/cef_browser_capi.h index 60f8f4789..eccb3f3e9 100644 --- a/cef1/include/capi/cef_browser_capi.h +++ b/cef1/include/capi/cef_browser_capi.h @@ -259,8 +259,8 @@ typedef struct _cef_browser_t { // Send a key event to the browser. /// void (CEF_CALLBACK *send_key_event)(struct _cef_browser_t* self, - enum cef_key_type_t type, int key, int modifiers, int sysChar, - int imeChar); + enum cef_key_type_t type, const struct _cef_key_info_t* keyInfo, + int modifiers); /// // Send a mouse click event to the browser. The |x| and |y| coordinates are @@ -279,10 +279,11 @@ typedef struct _cef_browser_t { /// // Send a mouse wheel event to the browser. The |x| and |y| coordinates are - // relative to the upper-left corner of the view. + // relative to the upper-left corner of the view. The |deltaX| and |deltaY| + // values represent the movement delta in the X and Y directions respectively. /// void (CEF_CALLBACK *send_mouse_wheel_event)(struct _cef_browser_t* self, - int x, int y, int delta); + int x, int y, int deltaX, int deltaY); /// // Send a focus event to the browser. diff --git a/cef1/include/cef_browser.h b/cef1/include/cef_browser.h index 540e4b9c6..b319bc1dd 100644 --- a/cef1/include/cef_browser.h +++ b/cef1/include/cef_browser.h @@ -294,8 +294,8 @@ class CefBrowser : public virtual CefBase { // Send a key event to the browser. /// /*--cef()--*/ - virtual void SendKeyEvent(KeyType type, int key, int modifiers, bool sysChar, - bool imeChar) =0; + virtual void SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) =0; /// // Send a mouse click event to the browser. The |x| and |y| coordinates are @@ -314,10 +314,11 @@ class CefBrowser : public virtual CefBase { /// // Send a mouse wheel event to the browser. The |x| and |y| coordinates are - // relative to the upper-left corner of the view. + // relative to the upper-left corner of the view. The |deltaX| and |deltaY| + // values represent the movement delta in the X and Y directions respectively. /// /*--cef()--*/ - virtual void SendMouseWheelEvent(int x, int y, int delta) =0; + virtual void SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) =0; /// // Send a focus event to the browser. diff --git a/cef1/include/internal/cef_linux.h b/cef1/include/internal/cef_linux.h index b53c2341c..0c9003b06 100644 --- a/cef1/include/internal/cef_linux.h +++ b/cef1/include/internal/cef_linux.h @@ -37,7 +37,9 @@ #include "include/internal/cef_types_linux.h" #include "include/internal/cef_types_wrappers.h" +/// // Atomic increment and decrement. +/// inline long CefAtomicIncrement(long volatile *pDest) { // NOLINT(runtime/int) return __sync_add_and_fetch(pDest, 1); } @@ -45,7 +47,9 @@ inline long CefAtomicDecrement(long volatile *pDest) { // NOLINT(runtime/int) return __sync_sub_and_fetch(pDest, 1); } +/// // Critical section wrapper. +/// class CefCriticalSection { public: CefCriticalSection() { @@ -68,10 +72,13 @@ class CefCriticalSection { pthread_mutexattr_t attr_; }; +/// // Handle types. +/// #define CefWindowHandle cef_window_handle_t #define CefCursorHandle cef_cursor_handle_t + struct CefWindowInfoTraits { typedef cef_window_info_t struct_type; @@ -85,7 +92,9 @@ struct CefWindowInfoTraits { } }; +/// // Class representing window information. +/// class CefWindowInfo : public CefStructBase { public: typedef CefStructBase parent; @@ -99,6 +108,7 @@ class CefWindowInfo : public CefStructBase { } }; + struct CefPrintInfoTraits { typedef cef_print_info_t struct_type; @@ -111,9 +121,29 @@ struct CefPrintInfoTraits { } }; +/// // Class representing print context information. +/// typedef CefStructBase CefPrintInfo; + +struct CefKeyInfoTraits { + typedef cef_key_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->key = src->key; + } +}; + +/// +// Class representing key information. +/// +typedef CefStructBase CefKeyInfo; + #endif // OS_LINUX #endif // CEF_INCLUDE_INTERNAL_CEF_LINUX_H_ diff --git a/cef1/include/internal/cef_mac.h b/cef1/include/internal/cef_mac.h index 532ade4f9..d979e80fe 100644 --- a/cef1/include/internal/cef_mac.h +++ b/cef1/include/internal/cef_mac.h @@ -37,7 +37,9 @@ #include "include/internal/cef_types_mac.h" #include "include/internal/cef_types_wrappers.h" +/// // Atomic increment and decrement. +/// inline long CefAtomicIncrement(long volatile *pDest) { // NOLINT(runtime/int) return __sync_add_and_fetch(pDest, 1); } @@ -45,11 +47,15 @@ inline long CefAtomicDecrement(long volatile *pDest) { // NOLINT(runtime/int) return __sync_sub_and_fetch(pDest, 1); } +/// // Handle types. +/// #define CefWindowHandle cef_window_handle_t #define CefCursorHandle cef_cursor_handle_t +/// // Critical section wrapper. +/// class CefCriticalSection { public: CefCriticalSection() { @@ -72,6 +78,7 @@ class CefCriticalSection { pthread_mutexattr_t attr_; }; + struct CefWindowInfoTraits { typedef cef_window_info_t struct_type; @@ -92,10 +99,14 @@ struct CefWindowInfoTraits { target->m_nWidth = src->m_nWidth; target->m_nHeight = src->m_nHeight; target->m_bHidden = src->m_bHidden; + target->m_bWindowRenderingDisabled = src->m_bWindowRenderingDisabled; + target->m_bTransparentPainting = src->m_bTransparentPainting; } }; +/// // Class representing window information. +/// class CefWindowInfo : public CefStructBase { public: typedef CefStructBase parent; @@ -113,8 +124,18 @@ class CefWindowInfo : public CefStructBase { m_nHeight = height; m_bHidden = false; } + + void SetAsOffScreen(NSView* parent) { + m_bWindowRenderingDisabled = true; + m_ParentView = parent; + } + + void SetTransparentPainting(int transparentPainting) { + m_bTransparentPainting = transparentPainting; + } }; + struct CefPrintInfoTraits { typedef cef_print_info_t struct_type; @@ -127,9 +148,31 @@ struct CefPrintInfoTraits { } }; +/// // Class representing print context information. +/// typedef CefStructBase CefPrintInfo; +struct CefKeyInfoTraits { + typedef cef_key_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->keyCode = src->keyCode; + target->character = src->character; + target->characterNoModifiers = src->characterNoModifiers; + } +}; + +/// +// Class representing key information. +/// +typedef CefStructBase CefKeyInfo; + + #endif // OS_MACOSX #endif // CEF_INCLUDE_INTERNAL_CEF_MAC_H_ diff --git a/cef1/include/internal/cef_types.h b/cef1/include/internal/cef_types.h index 1cecb5e63..2e11a20ae 100644 --- a/cef1/include/internal/cef_types.h +++ b/cef1/include/internal/cef_types.h @@ -856,10 +856,11 @@ enum cef_handler_keyevent_type_t { // Key event modifiers. /// enum cef_handler_keyevent_modifiers_t { - KEY_SHIFT = 1 << 0, - KEY_CTRL = 1 << 1, - KEY_ALT = 1 << 2, - KEY_META = 1 << 3 + KEY_SHIFT = 1 << 0, + KEY_CTRL = 1 << 1, + KEY_ALT = 1 << 2, + KEY_META = 1 << 3, + KEY_KEYPAD = 1 << 4, // Only used on Mac OS-X }; /// diff --git a/cef1/include/internal/cef_types_linux.h b/cef1/include/internal/cef_types_linux.h index 81e997a62..15966907e 100644 --- a/cef1/include/internal/cef_types_linux.h +++ b/cef1/include/internal/cef_types_linux.h @@ -72,6 +72,13 @@ typedef struct _cef_print_info_t { double m_Scale; } cef_print_info_t; +/// +// Class representing key information. +/// +typedef struct _cef_key_info_t { + int key; +} cef_key_info_t; + #ifdef __cplusplus } #endif diff --git a/cef1/include/internal/cef_types_mac.h b/cef1/include/internal/cef_types_mac.h index 8ead5ae9d..a10c54016 100644 --- a/cef1/include/internal/cef_types_mac.h +++ b/cef1/include/internal/cef_types_mac.h @@ -40,15 +40,18 @@ // Window handle. #ifdef __cplusplus #ifdef __OBJC__ +@class NSCursor; @class NSView; #else +class NSCursor; struct NSView; #endif #define cef_window_handle_t NSView* +#define cef_cursor_handle_t NSCursor* #else #define cef_window_handle_t void* -#endif #define cef_cursor_handle_t void* +#endif #ifdef __cplusplus extern "C" { @@ -76,6 +79,14 @@ typedef struct _cef_window_info_t { // NSView pointer for the parent view. cef_window_handle_t m_ParentView; + // If window rendering is disabled no browser window will be created. Set + // |m_ParentView| to the window that will act as the parent for popup menus, + // dialog boxes, etc. + int m_bWindowRenderingDisabled; + + // Set to true to enable transparent painting. + int m_bTransparentPainting; + // NSView pointer for the new browser view. cef_window_handle_t m_View; } cef_window_info_t; @@ -87,6 +98,15 @@ typedef struct _cef_print_info_t { double m_Scale; } cef_print_info_t; +/// +// Class representing key information. +/// +typedef struct _cef_key_info_t { + int keyCode; + int character; + int characterNoModifiers; +} cef_key_info_t; + #ifdef __cplusplus } #endif diff --git a/cef1/include/internal/cef_types_win.h b/cef1/include/internal/cef_types_win.h index c275f69d8..9b5e48e59 100644 --- a/cef1/include/internal/cef_types_win.h +++ b/cef1/include/internal/cef_types_win.h @@ -92,6 +92,15 @@ typedef struct _cef_print_info_t { double m_Scale; } cef_print_info_t; +/// +// Class representing key information. +/// +typedef struct _cef_key_info_t { + int key; + BOOL sysChar; + BOOL imeChar; +} cef_key_info_t; + #ifdef __cplusplus } #endif diff --git a/cef1/include/internal/cef_win.h b/cef1/include/internal/cef_win.h index 9a89f4a7c..68d91dced 100644 --- a/cef1/include/internal/cef_win.h +++ b/cef1/include/internal/cef_win.h @@ -71,7 +71,6 @@ class CefCriticalSection { #define CefWindowHandle cef_window_handle_t #define CefCursorHandle cef_cursor_handle_t - struct CefWindowInfoTraits { typedef cef_window_info_t struct_type; @@ -162,6 +161,26 @@ struct CefPrintInfoTraits { /// typedef CefStructBase CefPrintInfo; + +struct CefKeyInfoTraits { + typedef cef_key_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->key = src->key; + target->sysChar = src->sysChar; + target->imeChar = src->imeChar; + } +}; + +/// +// Class representing key information. +/// +typedef CefStructBase CefKeyInfo; + #endif // OS_WIN #endif // CEF_INCLUDE_INTERNAL_CEF_WIN_H_ diff --git a/cef1/libcef/browser_impl.cc b/cef1/libcef/browser_impl.cc index 90f5878a3..14a44bc9f 100644 --- a/cef1/libcef/browser_impl.cc +++ b/cef1/libcef/browser_impl.cc @@ -440,13 +440,13 @@ bool CefBrowserImpl::GetImage(PaintElementType type, int width, int height, return false; } -void CefBrowserImpl::SendKeyEvent(KeyType type, int key, int modifiers, - bool sysChar, bool imeChar) { +void CefBrowserImpl::SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) { // Intentially post event tasks in all cases so that painting tasks can be // handled at sane times. CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&CefBrowserImpl::UIT_SendKeyEvent, this, type, key, modifiers, - sysChar, imeChar)); + base::Bind(&CefBrowserImpl::UIT_SendKeyEvent, this, type, keyInfo, + modifiers)); } void CefBrowserImpl::SendMouseClickEvent(int x, int y, MouseButtonType type, @@ -466,11 +466,12 @@ void CefBrowserImpl::SendMouseMoveEvent(int x, int y, bool mouseLeave) { mouseLeave)); } -void CefBrowserImpl::SendMouseWheelEvent(int x, int y, int delta) { +void CefBrowserImpl::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) { // Intentially post event tasks in all cases so that painting tasks can be // handled at sane times. CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&CefBrowserImpl::UIT_SendMouseWheelEvent, this, x, y, delta)); + base::Bind(&CefBrowserImpl::UIT_SendMouseWheelEvent, this, x, y, deltaX, + deltaY)); } void CefBrowserImpl::SendFocusEvent(bool setFocus) { @@ -1051,16 +1052,16 @@ void CefBrowserImpl::UIT_Invalidate(const CefRect& dirtyRect) { } } -void CefBrowserImpl::UIT_SendKeyEvent(KeyType type, int key, int modifiers, - bool sysChar, bool imeChar) { +void CefBrowserImpl::UIT_SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) { REQUIRE_UIT(); if (popuphost_) { // Send the event to the popup. - popuphost_->SendKeyEvent(type, key, modifiers, sysChar, imeChar); + popuphost_->SendKeyEvent(type, keyInfo, modifiers); } else { WebViewHost* host = UIT_GetWebViewHost(); if (host) - host->SendKeyEvent(type, key, modifiers, sysChar, imeChar); + host->SendKeyEvent(type, keyInfo, modifiers); } } @@ -1091,16 +1092,17 @@ void CefBrowserImpl::UIT_SendMouseMoveEvent(int x, int y, bool mouseLeave) { } } -void CefBrowserImpl::UIT_SendMouseWheelEvent(int x, int y, int delta) { +void CefBrowserImpl::UIT_SendMouseWheelEvent(int x, int y, int deltaX, + int deltaY) { REQUIRE_UIT(); if (popuphost_ && popup_rect_.Contains(x, y)) { // Send the event to the popup. popuphost_->SendMouseWheelEvent(x - popup_rect_.x(), y - popup_rect_.y(), - delta); + deltaX, deltaY); } else { WebViewHost* host = UIT_GetWebViewHost(); if (host) - host->SendMouseWheelEvent(x, y, delta); + host->SendMouseWheelEvent(x, y, deltaX, deltaY); } } diff --git a/cef1/libcef/browser_impl.h b/cef1/libcef/browser_impl.h index 3004c3562..2fcb0b3e0 100644 --- a/cef1/libcef/browser_impl.h +++ b/cef1/libcef/browser_impl.h @@ -106,12 +106,13 @@ class CefBrowserImpl : public CefBrowser { virtual void Invalidate(const CefRect& dirtyRect) OVERRIDE; virtual bool GetImage(PaintElementType type, int width, int height, void* buffer) OVERRIDE; - virtual void SendKeyEvent(KeyType type, int key, int modifiers, bool sysChar, - bool imeChar) OVERRIDE; + virtual void SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) OVERRIDE; virtual void SendMouseClickEvent(int x, int y, MouseButtonType type, bool mouseUp, int clickCount) OVERRIDE; virtual void SendMouseMoveEvent(int x, int y, bool mouseLeave) OVERRIDE; - virtual void SendMouseWheelEvent(int x, int y, int delta) OVERRIDE; + virtual void SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) + OVERRIDE; virtual void SendFocusEvent(bool setFocus) OVERRIDE; virtual void SendCaptureLostEvent() OVERRIDE; @@ -269,12 +270,11 @@ class CefBrowserImpl : public CefBrowser { void UIT_SetFocus(WebWidgetHost* host, bool enable); void UIT_SetSize(PaintElementType type, int width, int height); void UIT_Invalidate(const CefRect& dirtyRect); - void UIT_SendKeyEvent(KeyType type, int key, int modifiers, bool sysChar, - bool imeChar); + void UIT_SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, int modifiers); void UIT_SendMouseClickEvent(int x, int y, MouseButtonType type, bool mouseUp, int clickCount); void UIT_SendMouseMoveEvent(int x, int y, bool mouseLeave); - void UIT_SendMouseWheelEvent(int x, int y, int delta); + void UIT_SendMouseWheelEvent(int x, int y, int deltaX, int deltaY); void UIT_SendFocusEvent(bool setFocus); void UIT_SendCaptureLostEvent(); diff --git a/cef1/libcef/browser_impl_mac.mm b/cef1/libcef/browser_impl_mac.mm index e18587638..f8054ff91 100644 --- a/cef1/libcef/browser_impl_mac.mm +++ b/cef1/libcef/browser_impl_mac.mm @@ -29,18 +29,19 @@ CefWindowHandle CefBrowserImpl::GetWindowHandle() { } bool CefBrowserImpl::IsWindowRenderingDisabled() { - // TODO(port): Add support for off-screen rendering. - return false; + return (window_info_.m_bWindowRenderingDisabled ? true : false); } gfx::NativeView CefBrowserImpl::UIT_GetMainWndHandle() { REQUIRE_UIT(); - return window_info_.m_View; + return window_info_.m_bWindowRenderingDisabled ? + window_info_.m_ParentView : window_info_.m_View; } void CefBrowserImpl::UIT_ClearMainWndHandle() { REQUIRE_UIT(); - window_info_.m_View = NULL; + if (!window_info_.m_bWindowRenderingDisabled) + window_info_.m_View = NULL; } bool CefBrowserImpl::UIT_CreateBrowser(const CefString& url) { @@ -61,29 +62,35 @@ bool CefBrowserImpl::UIT_CreateBrowser(const CefString& url) { NSView* parentView = window_info_.m_ParentView; gfx::Rect contentRect(window_info_.m_x, window_info_.m_y, window_info_.m_nWidth, window_info_.m_nHeight); - if (parentView == nil) { - // Create a new window. - NSRect screen_rect = [[NSScreen mainScreen] visibleFrame]; - NSRect window_rect = {{window_info_.m_x, - screen_rect.size.height - window_info_.m_y}, - {window_info_.m_nWidth, window_info_.m_nHeight}}; - if (window_rect.size.width == 0) - window_rect.size.width = 750; - if (window_rect.size.height == 0) - window_rect.size.height = 750; - contentRect.SetRect(0, 0, window_rect.size.width, window_rect.size.height); + if (!window_info_.m_bWindowRenderingDisabled) { + if (parentView == nil) { + // Create a new window. + NSRect screen_rect = [[NSScreen mainScreen] visibleFrame]; + NSRect window_rect = {{window_info_.m_x, + screen_rect.size.height - window_info_.m_y}, + {window_info_.m_nWidth, window_info_.m_nHeight}}; + if (window_rect.size.width == 0) + window_rect.size.width = 750; + if (window_rect.size.height == 0) + window_rect.size.height = 750; + contentRect.SetRect(0, 0, window_rect.size.width, + window_rect.size.height); - newWnd = [[NSWindow alloc] - initWithContentRect:window_rect - styleMask:(NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask | - NSUnifiedTitleAndToolbarWindowMask ) - backing:NSBackingStoreBuffered - defer:NO]; - parentView = [newWnd contentView]; - window_info_.m_ParentView = parentView; + newWnd = [[NSWindow alloc] + initWithContentRect:window_rect + styleMask:(NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask | + NSUnifiedTitleAndToolbarWindowMask ) + backing:NSBackingStoreBuffered + defer:NO]; + parentView = [newWnd contentView]; + window_info_.m_ParentView = parentView; + } + } else { + // Create a new paint delegate. + paint_delegate_.reset(new PaintDelegate(this)); } WebPreferences prefs; @@ -92,7 +99,11 @@ bool CefBrowserImpl::UIT_CreateBrowser(const CefString& url) { // Create the webview host object webviewhost_.reset( WebViewHost::Create(parentView, contentRect, delegate_.get(), - NULL, dev_tools_agent_.get(), prefs)); + paint_delegate_.get(), dev_tools_agent_.get(), + prefs)); + + if (window_info_.m_bTransparentPainting) + webviewhost_->webview()->setIsTransparent(true); if (!settings_.developer_tools_disabled) dev_tools_agent_->SetWebView(webviewhost_->webview()); @@ -101,8 +112,10 @@ bool CefBrowserImpl::UIT_CreateBrowser(const CefString& url) { browserView.browser = this; window_info_.m_View = browserView; - if (!settings_.drag_drop_disabled) - [browserView registerDragDrop]; + if (!window_info_.m_bWindowRenderingDisabled) { + if (!settings_.drag_drop_disabled) + [browserView registerDragDrop]; + } Unlock(); @@ -142,11 +155,24 @@ void CefBrowserImpl::UIT_SetFocus(WebWidgetHost* host, bool enable) { } } -bool CefBrowserImpl::UIT_ViewDocumentString(WebKit::WebFrame *frame) { +bool CefBrowserImpl::UIT_ViewDocumentString(WebKit::WebFrame* frame) { REQUIRE_UIT(); - // TODO(port): Add implementation. - NOTIMPLEMENTED(); + char sztmp[L_tmpnam+4]; + if (tmpnam(sztmp)) { + strcat(sztmp, ".txt"); + + FILE* fp = fopen(sztmp, "wb"); + if (fp) { + std::string markup = frame->contentAsMarkup().utf8(); + fwrite(markup.c_str(), 1, markup.size(), fp); + fclose(fp); + + char szopen[L_tmpnam + 14]; + snprintf(szopen, sizeof(szopen), "open -t \"%s\"", sztmp); + return (system(szopen) >= 0); + } + } return false; } diff --git a/cef1/libcef/browser_webview_delegate_mac.mm b/cef1/libcef/browser_webview_delegate_mac.mm index 3a431417c..2ad0dde34 100644 --- a/cef1/libcef/browser_webview_delegate_mac.mm +++ b/cef1/libcef/browser_webview_delegate_mac.mm @@ -48,6 +48,7 @@ namespace { void AddMenuItem(CefRefPtr browser, CefRefPtr handler, NSMenu* menu, + id target, cef_menu_id_t menuId, const std::string& label, bool enabled) { @@ -65,7 +66,8 @@ void AddMenuItem(CefRefPtr browser, NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:str action:enabled?@selector(menuItemSelected:):nil - keyEquivalent:@""] autorelease]; + keyEquivalent:@""] autorelease]; + [item setTarget:target]; [item setTag:menuId]; [menu addItem:item]; } @@ -77,6 +79,46 @@ void AddMenuSeparator(NSMenu* menu) { } // namespace +@interface BrowserMenuDelegate : NSObject { + @private + CefRefPtr browser_; +} + +- (id)initWithBrowser:(CefBrowserImpl*)browser; +- (void)menuItemSelected:(id)sender; +@end + +@implementation BrowserMenuDelegate + +- (id)initWithBrowser:(CefBrowserImpl*)browser { + self = [super init]; + if (self) + browser_ = browser; + return self; + +} + +// Called when a context menu item is selected by the user. +- (void)menuItemSelected:(id)sender { + cef_menu_id_t menuId = static_cast([sender tag]); + bool handled = false; + + CefRefPtr client = browser_->GetClient(); + if (client.get()) { + CefRefPtr handler = client->GetMenuHandler(); + if (handler.get()) { + // Ask the handler if it wants to handle the action. + handled = handler->OnMenuAction(browser_.get(), menuId); + } + } + + if(!handled) { + // Execute the action. + browser_->UIT_HandleAction(menuId, browser_->GetFocusedFrame()); + } +} + +@end // WebViewClient -------------------------------------------------------------- @@ -102,12 +144,21 @@ void BrowserWebViewDelegate::showContextMenu( if (!host) return; - BrowserWebView *view = static_cast(host->view_handle()); + NSView *view = browser_->UIT_GetMainWndHandle(); if (!view) return; NSWindow* window = [view window]; - NSPoint position = [window mouseLocationOutsideOfEventStream]; + + int screenX = -1; + int screenY = -1; + NSPoint mouse_pt = {data.mousePosition.x, data.mousePosition.y}; + if (!browser_->IsWindowRenderingDisabled()) { + mouse_pt = [window mouseLocationOutsideOfEventStream]; + NSPoint screen_pt = [window convertBaseToScreen:mouse_pt]; + screenX = screen_pt.x; + screenY = screen_pt.y; + } int edit_flags = 0; int type_flags = 0; @@ -117,7 +168,7 @@ void BrowserWebViewDelegate::showContextMenu( MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); // Give the client a chance to handle the menu. - if (OnBeforeMenu(data, position.x, position.y, edit_flags, type_flags)) + if (OnBeforeMenu(data, mouse_pt.x, mouse_pt.y, edit_flags, type_flags)) return; CefRefPtr client = browser_->GetClient(); @@ -125,43 +176,57 @@ void BrowserWebViewDelegate::showContextMenu( if (client.get()) handler = client->GetMenuHandler(); + if (client.get() && browser_->IsWindowRenderingDisabled()) { + // Retrieve the screen coordinates. + CefRefPtr render_handler = client->GetRenderHandler(); + if (!render_handler.get() || + !render_handler->GetScreenPoint(browser_, mouse_pt.x, mouse_pt.y, + screenX, screenY)) { + return; + } + } + + BrowserMenuDelegate* delegate = + [[[BrowserMenuDelegate alloc] initWithBrowser:browser_] autorelease]; + // Build the correct default context menu if (type_flags & MENUTYPE_EDITABLE) { menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; - AddMenuItem(browser_, handler, menu, MENU_ID_UNDO, "Undo", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_UNDO, "Undo", !!(edit_flags & MENU_CAN_UNDO)); - AddMenuItem(browser_, handler, menu, MENU_ID_REDO, "Redo", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_REDO, "Redo", !!(edit_flags & MENU_CAN_REDO)); AddMenuSeparator(menu); - AddMenuItem(browser_, handler, menu, MENU_ID_CUT, "Cut", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_CUT, "Cut", !!(edit_flags & MENU_CAN_CUT)); - AddMenuItem(browser_, handler, menu, MENU_ID_COPY, "Copy", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_COPY, "Copy", !!(edit_flags & MENU_CAN_COPY)); - AddMenuItem(browser_, handler, menu, MENU_ID_PASTE, "Paste", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_PASTE, "Paste", !!(edit_flags & MENU_CAN_PASTE)); - AddMenuItem(browser_, handler, menu, MENU_ID_DELETE, "Delete", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_DELETE, "Delete", !!(edit_flags & MENU_CAN_DELETE)); AddMenuSeparator(menu); - AddMenuItem(browser_, handler, menu, MENU_ID_SELECTALL, "Select All", - !!(edit_flags & MENU_CAN_SELECT_ALL)); + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_SELECTALL, + "Select All", !!(edit_flags & MENU_CAN_SELECT_ALL)); } else if(type_flags & MENUTYPE_SELECTION) { menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; - AddMenuItem(browser_, handler, menu, MENU_ID_COPY, "Copy", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_COPY, "Copy", !!(edit_flags & MENU_CAN_COPY)); } else if(type_flags & (MENUTYPE_PAGE | MENUTYPE_FRAME)) { menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; - AddMenuItem(browser_, handler, menu, MENU_ID_NAV_BACK, "Back", + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_NAV_BACK, "Back", !!(edit_flags & MENU_CAN_GO_BACK)); - AddMenuItem(browser_, handler, menu, MENU_ID_NAV_FORWARD, "Forward", - !!(edit_flags & MENU_CAN_GO_FORWARD)); + AddMenuItem(browser_, handler, menu, delegate, MENU_ID_NAV_FORWARD, + "Forward", !!(edit_flags & MENU_CAN_GO_FORWARD)); // TODO(port): Enable the below menu items when supported. //AddMenuSeparator(menu); - //AddMenuItem(browser_, handler, menu, MENU_ID_PRINT, "Print", true); - //AddMenuItem(browser_, handler, menu, MENU_ID_VIEWSOURCE, "View Source", - // true); + //AddMenuItem(browser_, handler, menu, delegate, MENU_ID_PRINT, "Print", + // true); + //AddMenuItem(browser_, handler, menu, delegate, MENU_ID_VIEWSOURCE, + // "View Source", true); } if (!menu) @@ -169,21 +234,24 @@ void BrowserWebViewDelegate::showContextMenu( // Synthesize an event for the click, as there is no certainty that // [NSApp currentEvent] will return a valid event. + NSPoint screen_pt = {screenX, screenY}; + NSPoint window_pt = [window convertScreenToBase:screen_pt]; + NSEvent* currentEvent = [NSApp currentEvent]; NSTimeInterval eventTime = [currentEvent timestamp]; NSEvent* clickEvent = [NSEvent mouseEventWithType:NSRightMouseDown - location:position - modifierFlags:NSRightMouseDownMask - timestamp:eventTime - windowNumber:[window windowNumber] - context:nil - eventNumber:0 - clickCount:1 - pressure:1.0]; + location:window_pt + modifierFlags:NSRightMouseDownMask + timestamp:eventTime + windowNumber:[window windowNumber] + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; - // Menu selection events go to the BrowserWebView. - [menu setDelegate:view]; + // Menu selection events go to the BrowserMenuDelegate. + [menu setDelegate:delegate]; // Show the menu. [NSMenu popUpContextMenu:menu @@ -194,18 +262,43 @@ void BrowserWebViewDelegate::showContextMenu( // WebWidgetClient ------------------------------------------------------------ void BrowserWebViewDelegate::show(WebNavigationPolicy policy) { + DCHECK(this != browser_->UIT_GetPopupDelegate()); } void BrowserWebViewDelegate::didChangeCursor(const WebCursorInfo& cursor_info) { NSCursor* ns_cursor = WebCursor(cursor_info).GetNativeCursor(); - [ns_cursor set]; + + if (!browser_->IsWindowRenderingDisabled()) { + [ns_cursor set]; + } else { + // Notify the handler of cursor change. + CefRefPtr client = browser_->GetClient(); + if (client.get()) { + CefRefPtr handler = client->GetRenderHandler(); + if (handler.get()) + handler->OnCursorChange(browser_, ns_cursor); + } + } } WebRect BrowserWebViewDelegate::windowRect() { if (WebWidgetHost* host = GetWidgetHost()) { - NSView *view = host->view_handle(); - NSRect rect = [view frame]; - return gfx::Rect(NSRectToCGRect(rect)); + if (!browser_->IsWindowRenderingDisabled()) { + NSView *view = host->view_handle(); + NSRect rect = [view frame]; + return gfx::Rect(NSRectToCGRect(rect)); + } else { + // Retrieve the view rectangle from the handler. + CefRefPtr client = browser_->GetClient(); + if (client.get()) { + CefRefPtr handler = client->GetRenderHandler(); + if (handler.get()) { + CefRect rect(0, 0, 0, 0); + if (handler->GetViewRect(browser_, rect)) + return WebRect(rect.x, rect.y, rect.width, rect.height); + } + } + } } return WebRect(); } @@ -213,6 +306,8 @@ WebRect BrowserWebViewDelegate::windowRect() { void BrowserWebViewDelegate::setWindowRect(const WebRect& rect) { if (this == browser_->UIT_GetWebViewDelegate()) { // TODO(port): Set the window rectangle. + } else if (this == browser_->UIT_GetPopupDelegate()) { + NOTREACHED(); } } @@ -257,7 +352,8 @@ void BrowserWebViewDelegate::startDragging(const WebDragData& data, WebDragOperationsMask mask, const WebImage& image, const WebPoint& image_offset) { - if (browser_->settings().drag_drop_disabled) { + if (browser_->settings().drag_drop_disabled || + browser_->IsWindowRenderingDisabled()) { browser_->UIT_GetWebView()->dragSourceSystemDragEnded(); return; } @@ -332,15 +428,30 @@ webkit::npapi::WebPluginDelegate* BrowserWebViewDelegate::CreatePluginDelegate( void BrowserWebViewDelegate::CreatedPluginWindow( gfx::PluginWindowHandle handle) { + if (browser_->IsWindowRenderingDisabled()) { + WebViewHost* host = browser_->UIT_GetWebViewHost(); + if (host) + host->AddWindowedPlugin(handle); + } } void BrowserWebViewDelegate::WillDestroyPluginWindow( gfx::PluginWindowHandle handle) { + if (browser_->IsWindowRenderingDisabled()) { + WebViewHost* host = browser_->UIT_GetWebViewHost(); + if (host) + host->RemoveWindowedPlugin(handle); + } } void BrowserWebViewDelegate::DidMovePlugin( const webkit::npapi::WebPluginGeometry& move) { - // TODO(port): add me once plugins work. + if (browser_->IsWindowRenderingDisabled()) { + WebViewHost* host = browser_->UIT_GetWebViewHost(); + if (host) { + host->MoveWindowedPlugin(move); + } + } } // Protected methods ---------------------------------------------------------- diff --git a/cef1/libcef/browser_webview_delegate_win.cc b/cef1/libcef/browser_webview_delegate_win.cc index c98ec8788..de0f779f6 100644 --- a/cef1/libcef/browser_webview_delegate_win.cc +++ b/cef1/libcef/browser_webview_delegate_win.cc @@ -448,7 +448,7 @@ void BrowserWebViewDelegate::showContextMenu( if (client.get() && browser_->IsWindowRenderingDisabled()) { // Retrieve the screen coordinates. CefRefPtr render_handler = client->GetRenderHandler(); - if (render_handler.get() && + if (!render_handler.get() || !render_handler->GetScreenPoint(browser_, mouse_pt.x, mouse_pt.y, screenX, screenY)) { return; diff --git a/cef1/libcef/browser_webview_mac.h b/cef1/libcef/browser_webview_mac.h index 3d7792ccf..790fc1ac9 100644 --- a/cef1/libcef/browser_webview_mac.h +++ b/cef1/libcef/browser_webview_mac.h @@ -18,7 +18,7 @@ struct WebDropData; // A view to wrap the WebCore view and help it live in a Cocoa world. The // (rough) equivalent of Apple's WebView. -@interface BrowserWebView : NSView { +@interface BrowserWebView : NSView { @private CefBrowserImpl* browser_; // weak NSTrackingArea* trackingArea_; @@ -46,9 +46,6 @@ struct WebDropData; - (BOOL)isOpaque; - (void)setFrame:(NSRect)frameRect; -// Called when a context menu item is selected by the user. -- (void)menuItemSelected:(id)sender; - // Register this WebView as a drag/drop target. - (void)registerDragDrop; diff --git a/cef1/libcef/browser_webview_mac.mm b/cef1/libcef/browser_webview_mac.mm index 3faf08977..ccfe1aeb6 100644 --- a/cef1/libcef/browser_webview_mac.mm +++ b/cef1/libcef/browser_webview_mac.mm @@ -239,25 +239,6 @@ browser_->GetFocusedFrame()->SelectAll(); } -- (void)menuItemSelected:(id)sender { - cef_menu_id_t menuId = static_cast([sender tag]); - bool handled = false; - - CefRefPtr client = browser_->GetClient(); - if (client.get()) { - CefRefPtr handler = client->GetMenuHandler(); - if (handler.get()) { - // Ask the handler if it wants to handle the action. - handled = handler->OnMenuAction(browser_, menuId); - } - } - - if(!handled) { - // Execute the action. - browser_->UIT_HandleAction(menuId, browser_->GetFocusedFrame()); - } -} - - (void)registerDragDrop { dropTarget_.reset([[WebDropTarget alloc] initWithWebView:self]); diff --git a/cef1/libcef/external_popup_menu_mac.mm b/cef1/libcef/external_popup_menu_mac.mm index d92e7d69e..b895598c9 100644 --- a/cef1/libcef/external_popup_menu_mac.mm +++ b/cef1/libcef/external_popup_menu_mac.mm @@ -33,9 +33,35 @@ void ExternalPopupMenu::show(const WebKit::WebRect& bounds) { CefBrowserImpl* browser = delegate_->GetBrowser(); + NSView* view = nil; + NSRect view_rect; + + if (!browser->IsWindowRenderingDisabled()) { + view = browser->UIT_GetWebViewWndHandle(); + view_rect = [view bounds]; + } else { + view = browser->UIT_GetMainWndHandle(); + if (view != nil) { + CefRefPtr client = browser->GetClient(); + if (client.get()) { + // Retrieve the view rect. + CefRect rect; + CefRefPtr render_handler = client->GetRenderHandler(); + if (render_handler.get() && + render_handler->GetViewRect(browser, rect)) { + view_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + } + } + } + } + + if (view == nil || view_rect.size.width == 0 || view_rect.size.height == 0) { + popup_menu_client_->didCancel(); + delegate_->ClosePopupMenu(); + return; + } + // Set up the menu position. - NSView* web_view = browser->UIT_GetWebViewWndHandle(); - NSRect view_rect = [web_view bounds]; int y_offset = bounds.y + bounds.height; NSRect position = NSMakeRect(bounds.x, view_rect.size.height - y_offset, bounds.width, bounds.height); @@ -46,7 +72,7 @@ void ExternalPopupMenu::show(const WebKit::WebRect& bounds) { fontSize:font_size rightAligned:right_aligned]); - [menu_runner runMenuInView:browser->UIT_GetWebViewWndHandle() + [menu_runner runMenuInView:view withBounds:position initialIndex:selected_index]; diff --git a/cef1/libcef/webview_host_mac.mm b/cef1/libcef/webview_host_mac.mm index b51dfff99..79924a35e 100644 --- a/cef1/libcef/webview_host_mac.mm +++ b/cef1/libcef/webview_host_mac.mm @@ -35,10 +35,15 @@ WebViewHost* WebViewHost::Create(NSView* parent_view, WebViewHost* host = new WebViewHost(delegate); NSRect content_rect = {{rect.x(), rect.y()}, {rect.width(), rect.height()}}; - host->view_ = [[BrowserWebView alloc] initWithFrame:content_rect]; - [host->view_ setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; - [parent_view addSubview:host->view_]; - [host->view_ release]; + if (!paint_delegate) { + host->view_ = [[BrowserWebView alloc] initWithFrame:content_rect]; + [host->view_ setAutoresizingMask:(NSViewWidthSizable | + NSViewHeightSizable)]; + [parent_view addSubview:host->view_]; + [host->view_ release]; + } else { + host->paint_delegate_ = paint_delegate; + } #if defined(WEBKIT_HAS_WEB_AUTO_FILL_CLIENT) host->webwidget_ = WebView::create(delegate, NULL); diff --git a/cef1/libcef/webwidget_host.cc b/cef1/libcef/webwidget_host.cc index 411561330..c1ae0be65 100644 --- a/cef1/libcef/webwidget_host.cc +++ b/cef1/libcef/webwidget_host.cc @@ -106,21 +106,18 @@ gfx::PluginWindowHandle WebWidgetHost::GetWindowedPluginAt(int x, int y) { } void WebWidgetHost::DoPaint() { - // TODO(cef): The below code is cross-platform but the IsIdle() method - // currently requires patches to Chromium. Since this code is only executed - // on Windows it's been stuck behind an #ifdef for now to avoid having to - // patch Chromium code on other platforms. -#if defined(OS_WIN) - if (MessageLoop::current()->IsIdle()) { + if (MessageLoop::current()->IsIdle()) { has_update_task_ = false; // Paint to the delegate. +#if defined(OS_MACOSX) + SkRegion region; + Paint(region); +#else Paint(); +#endif } else { // Try again later. CefThread::PostTask(CefThread::UI, FROM_HERE, base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); } -#else - NOTIMPLEMENTED(); -#endif } diff --git a/cef1/libcef/webwidget_host.h b/cef1/libcef/webwidget_host.h index c011ecdc2..b5dfec428 100644 --- a/cef1/libcef/webwidget_host.h +++ b/cef1/libcef/webwidget_host.h @@ -123,12 +123,12 @@ class WebWidgetHost { void SetTooltipText(const CefString& tooltip_text); - void SendKeyEvent(cef_key_type_t type, int key, int modifiers, bool sysChar, - bool imeChar); + void SendKeyEvent(cef_key_type_t type, const cef_key_info_t& keyInfo, + int modifiers); void SendMouseClickEvent(int x, int y, cef_mouse_button_type_t type, bool mouseUp, int clickCount); void SendMouseMoveEvent(int x, int y, bool mouseLeave); - void SendMouseWheelEvent(int x, int y, int delta); + void SendMouseWheelEvent(int x, int y, int deltaX, int deltaY); void SendFocusEvent(bool setFocus); void SendCaptureLostEvent(); @@ -284,6 +284,11 @@ class WebWidgetHost { WebKit::WebRect caret_bounds_; #endif +#if defined(OS_MACOSX) + int mouse_modifiers_; + WebKit::WebMouseEvent::Button mouse_button_down_; +#endif + #if defined(TOOLKIT_GTK) // Since GtkWindow resize is asynchronous, we have to stash the dimensions, // so that the backing store doesn't have to wait for sizing to take place. diff --git a/cef1/libcef/webwidget_host_gtk.cc b/cef1/libcef/webwidget_host_gtk.cc index da230be21..38b1658b9 100644 --- a/cef1/libcef/webwidget_host_gtk.cc +++ b/cef1/libcef/webwidget_host_gtk.cc @@ -440,8 +440,9 @@ void WebWidgetHost::PaintRect(const gfx::Rect& rect) { set_painting(false); } -void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers, - bool sysChar, bool imeChar) { +void WebWidgetHost::SendKeyEvent(cef_key_type_t type, + const cef_key_info_t& keyInfo, + int modifiers) { // TODO(port): Implement this method as part of off-screen rendering support. NOTIMPLEMENTED(); } @@ -458,7 +459,7 @@ void WebWidgetHost::SendMouseMoveEvent(int x, int y, bool mouseLeave) { NOTIMPLEMENTED(); } -void WebWidgetHost::SendMouseWheelEvent(int x, int y, int delta) { +void WebWidgetHost::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) { // TODO(port): Implement this method as part of off-screen rendering support. NOTIMPLEMENTED(); } diff --git a/cef1/libcef/webwidget_host_mac.mm b/cef1/libcef/webwidget_host_mac.mm index 9f9f605d0..4de7cf7dd 100644 --- a/cef1/libcef/webwidget_host_mac.mm +++ b/cef1/libcef/webwidget_host_mac.mm @@ -3,7 +3,18 @@ // found in the LICENSE file. #import + +#include "base/compiler_specific.h" +#include "third_party/WebKit/Source/WebCore/config.h" +MSVC_PUSH_WARNING_LEVEL(0); +#include "KeyEventCocoa.h" // NOLINT(build/include) +MSVC_POP_WARNING(); + +#undef LOG #import "libcef/webwidget_host.h" +#include "libcef/cef_thread.h" + +#include "base/bind.h" #import "base/logging.h" #import "skia/ext/platform_canvas.h" #import "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h" @@ -11,12 +22,14 @@ #import "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #import "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h" #import "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #import "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" #import "third_party/skia/include/core/SkRegion.h" #import "ui/gfx/rect.h" #import "ui/gfx/size.h" #import "webkit/glue/webkit_glue.h" +using webkit::npapi::WebPluginGeometry; using WebKit::WebInputEvent; using WebKit::WebInputEventFactory; using WebKit::WebKeyboardEvent; @@ -46,14 +59,19 @@ WebWidgetHost* WebWidgetHost::Create(NSView* parent_view, PaintDelegate* paint_delegate) { WebWidgetHost* host = new WebWidgetHost(); - const NSRect bounds = [parent_view bounds]; - host->view_ = [[NSView alloc] initWithFrame:bounds]; - [parent_view addSubview:host->view_]; + if (!paint_delegate) { + const NSRect bounds = [parent_view bounds]; + host->view_ = [[NSView alloc] initWithFrame:bounds]; + [parent_view addSubview:host->view_]; + + host->webwidget_ = WebPopupMenu::create(client); + host->webwidget_->resize(WebSize(NSWidth(bounds), NSHeight(bounds))); + } else { + host->paint_delegate_ = paint_delegate; + host->view_ = nil; + host->webwidget_ = WebPopupMenu::create(client); + } - host->painting_ = false; - host->layouting_ = false; - host->webwidget_ = WebPopupMenu::create(client); - host->webwidget_->resize(WebSize(NSWidth(bounds), NSHeight(bounds))); return host; } @@ -66,6 +84,9 @@ WebWidgetHost::WebWidgetHost() popup_(false), has_update_task_(false), has_invalidate_task_(false), + mouse_modifiers_(0), + painting_(false), + layouting_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { set_painting(false); } @@ -74,21 +95,38 @@ WebWidgetHost::~WebWidgetHost() { } void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { - const gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); + int width, height; + GetSize(width, height); + const gfx::Rect client_rect(width, height); + const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect); if (!damaged_rect_in_client.IsEmpty()) { UpdatePaintRect(damaged_rect_in_client); - NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); - cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect); - [view_ setNeedsDisplayInRect:cocoa_rect]; + + if (view_) { + NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); + cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect); + [view_ setNeedsDisplayInRect:cocoa_rect]; + } else { + // Don't post a paint task if this invalidation occurred during layout or + // if a paint task is already pending. Paint() will be called by + // DoPaint(). + if (!layouting_ && !has_update_task_) { + has_update_task_ = true; + CefThread::PostTask(CefThread::UI, FROM_HERE, + base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); + } + } } } void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { DCHECK(dx || dy); - const gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); + int width, height; + GetSize(width, height); + const gfx::Rect client_rect(width, height); gfx::Rect rect = clip_rect.Intersect(client_rect); const int x = rect.x(); @@ -106,7 +144,8 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { // needs to be laid out; calling scrollRect:by: in this situation leads to // unwanted behavior. Finally, scrolling the rectangle by more than the size // of the view means we can just invalidate the entire scroll rect. - if (![view_ canDraw] || painting_ || layouting_ || Dx >= w || Dy >= h) { + if (!view_ || [view_ canDraw] || painting_ || layouting_ || Dx >= w || + Dy >= h) { DidInvalidateRect(clip_rect); return; } @@ -144,36 +183,56 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { } void WebWidgetHost::Paint(SkRegion& update_rgn) { - gfx::Rect client_rect(NSRectToCGRect([view_ bounds])); + int width, height; + GetSize(width, height); + gfx::Rect client_rect(width, height); - // Union the rectangle that WebKit think needs repainting with the rectangle - // of the view that must be painted now. In most situations this will not - // affect the painted rectangle. In some situations we could end up re- - // painting areas that are already in the canvas and only dirty in the view - // itself. However, if we don't do this we can get artifacts when scrolling - // because contents of the canvas are no longer correct after scrolling only - // in the view. - paint_rgn_.op(update_rgn, SkRegion::kUnion_Op); + SkRegion damaged_rgn; + if (!view_ && !redraw_rect_.IsEmpty()) { + // At a minimum we need to send the delegate the rectangle that was + // requested by calling CefBrowser::InvalidateRect(). + damaged_rgn.setRect(convertToSkiaRect(redraw_rect_)); + redraw_rect_ = gfx::Rect(); + } - // When we are not using accelerated compositing the canvas area is allowed - // to differ in size from the client by a certain number of pixels (128 in - // this case). When accelerated compositing is in effect the size must match - // exactly. - const int extra_w = (webwidget_->isAcceleratedCompositingActive()? 0: 128); - const int extra_h = (webwidget_->isAcceleratedCompositingActive()? 0: 128); - const int min_w = client_rect.width(); - const int min_h = client_rect.height(); - const int max_w = client_rect.width() + extra_w * 2; - const int max_h = client_rect.height() + extra_h * 2; + if (view_) { + // Union the rectangle that WebKit think needs repainting with the rectangle + // of the view that must be painted now. In most situations this will not + // affect the painted rectangle. In some situations we could end up re- + // painting areas that are already in the canvas and only dirty in the view + // itself. However, if we don't do this we can get artifacts when scrolling + // because contents of the canvas are no longer correct after scrolling only + // in the view. + paint_rgn_.op(update_rgn, SkRegion::kUnion_Op); - const bool too_small = (canvas_w_ < min_w || canvas_h_ < min_h); - const bool too_large = (canvas_w_ > max_w || canvas_h_ > max_h); + // When we are not using accelerated compositing the canvas area is allowed + // to differ in size from the client by a certain number of pixels (128 in + // this case). When accelerated compositing is in effect the size must match + // exactly. + const int extra_w = (webwidget_->isAcceleratedCompositingActive()? 0: 128); + const int extra_h = (webwidget_->isAcceleratedCompositingActive()? 0: 128); + const int min_w = client_rect.width(); + const int min_h = client_rect.height(); + const int max_w = client_rect.width() + extra_w * 2; + const int max_h = client_rect.height() + extra_h * 2; - if (!canvas_.get() || too_small || too_large) { - canvas_w_ = client_rect.width() + extra_w; - canvas_h_ = client_rect.height() + extra_h; - canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); + const bool too_small = (canvas_w_ < min_w || canvas_h_ < min_h); + const bool too_large = (canvas_w_ > max_w || canvas_h_ > max_h); + + if (!canvas_.get() || too_small || too_large) { + canvas_w_ = client_rect.width() + extra_w; + canvas_h_ = client_rect.height() + extra_h; + canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); + paint_rgn_.setRect(convertToSkiaRect(client_rect)); + } + } else if (!canvas_.get() || canvas_w_ != client_rect.width() || + canvas_h_ != client_rect.height()) { paint_rgn_.setRect(convertToSkiaRect(client_rect)); + + // The canvas must be the exact size of the client area. + canvas_w_ = client_rect.width(); + canvas_h_ = client_rect.height(); + canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); } webwidget_->animate(0.0); @@ -202,28 +261,69 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { draw_rgn.swap(paint_rgn_); SkRegion::Cliperator iterator(draw_rgn, convertToSkiaRect(client_rect)); - for (; !iterator.done(); iterator.next()) - PaintRect(convertFromSkiaRect(iterator.rect())); + for (; !iterator.done(); iterator.next()) { + const SkIRect& r = iterator.rect(); + PaintRect(convertFromSkiaRect(r)); - // If any more rectangles were made dirty during the paint operation, make - // sure they are copied to the window buffer, by including the paint region. - // If nothing needs additional painting, this is a no-op. - update_rgn.op(paint_rgn_, SkRegion::kUnion_Op); + if (!view_) + damaged_rgn.op(r, SkRegion::kUnion_Op); + } + + if (view_) { + // If any more rectangles were made dirty during the paint operation, make + // sure they are copied to the window buffer, by including the paint + // region. If nothing needs additional painting, this is a no-op. + update_rgn.op(paint_rgn_, SkRegion::kUnion_Op); + } + } + + if (!view_ && plugin_map_.size() > 0) { + // Flash seems to stop calling NPN_InvalidateRect, which means we stop + // painting. If we've got a plugin make sure we paint its rect each time. + + PluginMap::const_iterator it = plugin_map_.begin(); + for (; it != plugin_map_.end(); ++it) { + if (it->second.visible && + client_rect.Intersects(it->second.window_rect)) { + const WebPluginGeometry* geom = &it->second; + damaged_rgn.op(convertToSkiaRect(geom->window_rect), + SkRegion::kUnion_Op); + } + } } - // Set the context back to our view and copy the bitmap that we just painted - // into to the view. Only the regions that were updated are copied. - [NSGraphicsContext restoreGraphicsState]; - NSGraphicsContext* view_context = [NSGraphicsContext currentContext]; - CGContextRef context = static_cast([view_context graphicsPort]); + if (view_) { + // Set the context back to our view and copy the bitmap that we just painted + // into to the view. Only the regions that were updated are copied. + [NSGraphicsContext restoreGraphicsState]; + NSGraphicsContext* view_context = [NSGraphicsContext currentContext]; + CGContextRef context = + static_cast([view_context graphicsPort]); - SkRegion::Cliperator iterator(update_rgn, convertToSkiaRect(client_rect)); - for (; !iterator.done(); iterator.next()) { - const SkIRect& r = iterator.rect(); - CGRect copy_rect = { { r.x(), r.y() }, { r.width(), r.height() } }; - const float x = r.x(); - const float y = client_rect.height() - r.bottom(); - skia::DrawToNativeContext(canvas_.get(), context, x, y, ©_rect); + SkRegion::Cliperator iterator(update_rgn, convertToSkiaRect(client_rect)); + for (; !iterator.done(); iterator.next()) { + const SkIRect& r = iterator.rect(); + CGRect copy_rect = { { r.x(), r.y() }, { r.width(), r.height() } }; + const float x = r.x(); + const float y = client_rect.height() - r.bottom(); + skia::DrawToNativeContext(canvas_.get(), context, x, y, ©_rect); + } + } else { + // Paint to the delegate. + DCHECK(paint_delegate_); + const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); + DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + const void* pixels = bitmap.getPixels(); + + std::vector damaged_rects; + SkRegion::Cliperator iterator(damaged_rgn, convertToSkiaRect(client_rect)); + for (; !iterator.done(); iterator.next()) { + const SkIRect& r = iterator.rect(); + damaged_rects.push_back( + CefRect(r.left(), r.top(), r.width(), r.height())); + } + + paint_delegate_->Paint(popup_, damaged_rects, pixels); } // Used with scheduled invalidation to maintain a consistent frame rate. @@ -233,7 +333,12 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { } void WebWidgetHost::Invalidate() { - [view_ setNeedsDisplay:YES]; + if (view_) { + [view_ setNeedsDisplay:YES]; + } else if (webwidget_) { + WebSize size = webwidget_->size(); + InvalidateRect(gfx::Rect(0, 0, size.width, size.height)); + } } void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) { @@ -241,17 +346,50 @@ void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) { } void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + if (rect.IsEmpty()) + return; + + if (!view_) { + // Don't post a paint task if this invalidation occurred during layout or if + // a paint task is already pending. Paint() will be called by DoPaint(). + if (!layouting_ && !has_update_task_) { + has_update_task_ = true; + CefThread::PostTask(CefThread::UI, FROM_HERE, + base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); + } + } else { + NOTIMPLEMENTED(); + } } bool WebWidgetHost::GetImage(int width, int height, void* rgba_buffer) { if (!canvas_.get()) return false; - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); - return false; + const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); + DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config); + + if (width == canvas_->getDevice()->width() && + height == canvas_->getDevice()->height()) { + // The specified width and height values are the same as the canvas size. + // Return the existing canvas contents. + const void* pixels = bitmap.getPixels(); + memcpy(rgba_buffer, pixels, width * height * 4); + return true; + } + + // Create a new canvas of the requested size. + scoped_ptr new_canvas( + new skia::PlatformCanvas(width, height, true)); + + new_canvas->writePixels(bitmap, 0, 0); + const SkBitmap& new_bitmap = new_canvas->getDevice()->accessBitmap(false); + DCHECK(new_bitmap.config() == SkBitmap::kARGB_8888_Config); + + // Return the new canvas contents. + const void* pixels = new_bitmap.getPixels(); + memcpy(rgba_buffer, pixels, width * height * 4); + return true; } WebScreenInfo WebWidgetHost::GetScreenInfo() { @@ -326,44 +464,213 @@ void WebWidgetHost::PaintRect(const gfx::Rect& rect) { #endif DCHECK(canvas_.get()); - if (!rect.IsEmpty()) { - set_painting(true); - webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect); - set_painting(false); + if (rect.IsEmpty()) + return; + + if (!popup() && ((WebKit::WebView*)webwidget_)->isTransparent()) { + // When using transparency mode clear the rectangle before painting. + SkPaint clearpaint; + clearpaint.setARGB(0, 0, 0, 0); + clearpaint.setXfermodeMode(SkXfermode::kClear_Mode); + + SkRect skrc; + skrc.set(rect.x(), rect.y(), rect.right(), rect.bottom()); + canvas_->drawRect(skrc, clearpaint); } + + set_painting(true); + webwidget_->paint(webkit_glue::ToWebCanvas(canvas_.get()), rect); + set_painting(false); } -void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers, - bool sysChar, bool imeChar) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); +void WebWidgetHost::SendKeyEvent(cef_key_type_t type, + const cef_key_info_t& keyInfo, + int modifiers) { + WebKeyboardEvent event; + + switch (type) { + case KT_KEYUP: + default: + event.type = WebInputEvent::KeyUp; + break; + case KT_KEYDOWN: + event.type = WebInputEvent::RawKeyDown; + break; + case KT_CHAR: + event.type = WebInputEvent::Char; + break; + } + event.timeStampSeconds = TickCount(); + + if (modifiers & KEY_SHIFT) + event.modifiers |= WebInputEvent::ShiftKey; + if (modifiers & KEY_ALT) + event.modifiers |= WebInputEvent::AltKey; + if (modifiers & KEY_CTRL) + event.modifiers |= WebInputEvent::ControlKey; + if (modifiers & KEY_META) + event.modifiers |= WebInputEvent::MetaKey; + if (modifiers & KEY_KEYPAD) + event.modifiers |= WebInputEvent::IsKeyPad; + + // There are several kinds of characters for which we produce key code from + // char code: + // 1. Roman letters. Windows keyboard layouts affect both virtual key codes + // and character codes for these, so e.g. 'A' gets the same keyCode on + // QWERTY, AZERTY or Dvorak layouts. + // 2. Keys for which there is no known Mac virtual key codes, like + // PrintScreen. + // 3. Certain punctuation keys. On Windows, these are also remapped depending + // on current keyboard layout, but see comment in + // windowsKeyCodeForCharCode(). + + if (type == KT_KEYUP || type == KT_KEYDOWN) { + if (keyInfo.character != 0) { + // Cmd switches Roman letters for Dvorak-QWERTY layout, so try modified + // characters first. + event.windowsKeyCode = + WebCore::windowsKeyCodeForCharCode(keyInfo.character); + } + if (event.windowsKeyCode == 0 && keyInfo.characterNoModifiers != 0) { + // Ctrl+A on an AZERTY keyboard would get VK_Q keyCode if we relied on + // keyInfo.keyCode below. + event.windowsKeyCode = + WebCore::windowsKeyCodeForCharCode(keyInfo.characterNoModifiers); + } + } + + if (event.windowsKeyCode == 0) { + // Map Mac virtual key code directly to Windows one for any keys not handled + // above. E.g. the key next to Caps Lock has the same Event.keyCode on U.S. + // keyboard ('A') and on Russian keyboard (CYRILLIC LETTER EF). + event.windowsKeyCode = WebCore::windowsKeyCodeForKeyCode(keyInfo.keyCode); + } + + event.nativeKeyCode = keyInfo.keyCode; + + int textChar = keyInfo.character; + int unmodifiedChar = keyInfo.characterNoModifiers; + + // Always use 13 for Enter/Return -- we don't want to use AppKit's + // different character for Enter. + if (event.windowsKeyCode == '\r') { + textChar = '\r'; + unmodifiedChar = '\r'; + } + + // The adjustments below are only needed in backward compatibility mode, + // but we cannot tell what mode we are in from here. + + // Turn 0x7F into 8, because backspace needs to always be 8. + if (textChar == '\x7F') + textChar = '\x8'; + if (unmodifiedChar == '\x7F') + unmodifiedChar = '\x8'; + // Always use 9 for tab -- we don't want to use AppKit's different character + // for shift-tab. + if (event.windowsKeyCode == 9) { + textChar = '\x9'; + unmodifiedChar = '\x9'; + } + + event.text[0] = textChar; + event.unmodifiedText[0] = unmodifiedChar; + + event.setKeyIdentifierFromWindowsKeyCode(); + + event.isSystemKey = !!(modifiers & KEY_META); + + last_key_event_ = event; + + webwidget_->handleInputEvent(event); } void WebWidgetHost::SendMouseClickEvent(int x, int y, cef_mouse_button_type_t type, bool mouseUp, int clickCount) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + WebMouseEvent event; + + switch(type) { + case MBT_LEFT: + event.button = WebMouseEvent::ButtonLeft; + event.modifiers |= WebInputEvent::LeftButtonDown; + break; + case MBT_MIDDLE: + event.button = WebMouseEvent::ButtonMiddle; + event.modifiers |= WebInputEvent::MiddleButtonDown; + break; + case MBT_RIGHT: + event.button = WebMouseEvent::ButtonRight; + event.modifiers |= WebInputEvent::RightButtonDown; + break; + } + + if (mouseUp) + event.type = WebInputEvent::MouseUp; + else + event.type = WebInputEvent::MouseDown; + + event.clickCount = clickCount; + event.timeStampSeconds = TickCount(); + event.x = x; + event.y = y; + event.windowX = event.x; + event.windowY = event.y; + + if (mouseUp) { + mouse_button_down_ = WebMouseEvent::ButtonNone; + mouse_modifiers_ &= ~event.modifiers; + } else { + mouse_modifiers_ |= event.modifiers; + mouse_button_down_ = event.button; + } + event.modifiers = mouse_modifiers_; + + webwidget_->handleInputEvent(event); } void WebWidgetHost::SendMouseMoveEvent(int x, int y, bool mouseLeave) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + WebMouseEvent event; + + event.type = WebInputEvent::MouseMove; + event.timeStampSeconds = TickCount(); + event.button = mouse_button_down_; + event.x = x; + event.y = y; + event.windowX = event.x; + event.windowY = event.y; + event.modifiers = mouse_modifiers_; + + webwidget_->handleInputEvent(event); } -void WebWidgetHost::SendMouseWheelEvent(int x, int y, int delta) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); +void WebWidgetHost::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) { + WebMouseWheelEvent event; + + // Conversion between wheel delta amounts and number of pixels to scroll. + static const double scrollbarPixelsPerCocoaTick = 40.0; + + event.type = WebInputEvent::MouseWheel; + event.timeStampSeconds = TickCount(); + event.button = WebMouseEvent::ButtonNone; + event.deltaX = static_cast(deltaX); + event.deltaY = static_cast(deltaY); + event.wheelTicksX = static_cast(deltaX/scrollbarPixelsPerCocoaTick); + event.wheelTicksY = static_cast(deltaY/scrollbarPixelsPerCocoaTick); + event.hasPreciseScrollingDeltas = true; + event.x = x; + event.y = y; + event.windowX = event.x; + event.windowY = event.y; + + webwidget_->handleInputEvent(event); } void WebWidgetHost::SendFocusEvent(bool setFocus) { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); + SetFocus(setFocus); } void WebWidgetHost::SendCaptureLostEvent() { - // TODO(port): Implement this method as part of off-screen rendering support. - NOTIMPLEMENTED(); } void WebWidgetHost::EnsureTooltip() { diff --git a/cef1/libcef/webwidget_host_win.cc b/cef1/libcef/webwidget_host_win.cc index 825c6a910..37658f5d0 100644 --- a/cef1/libcef/webwidget_host_win.cc +++ b/cef1/libcef/webwidget_host_win.cc @@ -777,30 +777,31 @@ void WebWidgetHost::PaintRect(const gfx::Rect& rect) { set_painting(false); } -void WebWidgetHost::SendKeyEvent(cef_key_type_t type, int key, int modifiers, - bool sysChar, bool imeChar) { +void WebWidgetHost::SendKeyEvent(cef_key_type_t type, + const cef_key_info_t& keyInfo, + int modifiers) { UINT message = 0; - WPARAM wparam = key; + WPARAM wparam = keyInfo.key; LPARAM lparam = modifiers; if (type == KT_KEYUP) { - if (sysChar) + if (keyInfo.sysChar) message = WM_SYSKEYUP; - else if (imeChar) + else if (keyInfo.imeChar) message = WM_IME_KEYUP; else message = WM_KEYUP; } else if (type == KT_KEYDOWN) { - if (sysChar) + if (keyInfo.sysChar) message = WM_SYSKEYDOWN; - else if (imeChar) + else if (keyInfo.imeChar) message = WM_IME_KEYDOWN; else message = WM_KEYDOWN; } else if (type == KT_CHAR) { - if (sysChar) + if (keyInfo.sysChar) message = WM_SYSCHAR; - else if (imeChar) + else if (keyInfo.imeChar) message = WM_IME_CHAR; else message = WM_CHAR; @@ -903,8 +904,8 @@ void WebWidgetHost::SendMouseMoveEvent(int x, int y, bool mouseLeave) { } } -void WebWidgetHost::SendMouseWheelEvent(int x, int y, int delta) { - WPARAM wparam = MAKEWPARAM(0, delta); +void WebWidgetHost::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY) { + WPARAM wparam = MAKEWPARAM(0, deltaY); LPARAM lparam = MAKELPARAM(x, y); if (GetKeyState(VK_CONTROL) & 0x8000) diff --git a/cef1/libcef_dll/cpptoc/browser_cpptoc.cc b/cef1/libcef_dll/cpptoc/browser_cpptoc.cc index 431b27a7d..3830709af 100644 --- a/cef1/libcef_dll/cpptoc/browser_cpptoc.cc +++ b/cef1/libcef_dll/cpptoc/browser_cpptoc.cc @@ -606,21 +606,28 @@ int CEF_CALLBACK browser_get_image(struct _cef_browser_t* self, } void CEF_CALLBACK browser_send_key_event(struct _cef_browser_t* self, - enum cef_key_type_t type, int key, int modifiers, int sysChar, - int imeChar) { + enum cef_key_type_t type, const struct _cef_key_info_t* keyInfo, + int modifiers) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); if (!self) return; + // Verify param: keyInfo; type: struct_byref_const + DCHECK(keyInfo); + if (!keyInfo) + return; + + // Translate param: keyInfo; type: struct_byref_const + CefKeyInfo keyInfoObj; + if (keyInfo) + keyInfoObj.Set(*keyInfo, false); // Execute CefBrowserCppToC::Get(self)->SendKeyEvent( type, - key, - modifiers, - sysChar?true:false, - imeChar?true:false); + keyInfoObj, + modifiers); } void CEF_CALLBACK browser_send_mouse_click_event(struct _cef_browser_t* self, @@ -657,7 +664,7 @@ void CEF_CALLBACK browser_send_mouse_move_event(struct _cef_browser_t* self, } void CEF_CALLBACK browser_send_mouse_wheel_event(struct _cef_browser_t* self, - int x, int y, int delta) { + int x, int y, int deltaX, int deltaY) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); @@ -668,7 +675,8 @@ void CEF_CALLBACK browser_send_mouse_wheel_event(struct _cef_browser_t* self, CefBrowserCppToC::Get(self)->SendMouseWheelEvent( x, y, - delta); + deltaX, + deltaY); } void CEF_CALLBACK browser_send_focus_event(struct _cef_browser_t* self, diff --git a/cef1/libcef_dll/ctocpp/browser_ctocpp.cc b/cef1/libcef_dll/ctocpp/browser_ctocpp.cc index c37bf1798..cbce950c9 100644 --- a/cef1/libcef_dll/ctocpp/browser_ctocpp.cc +++ b/cef1/libcef_dll/ctocpp/browser_ctocpp.cc @@ -491,8 +491,8 @@ bool CefBrowserCToCpp::GetImage(PaintElementType type, int width, int height, return _retval?true:false; } -void CefBrowserCToCpp::SendKeyEvent(KeyType type, int key, int modifiers, - bool sysChar, bool imeChar) { +void CefBrowserCToCpp::SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) { if (CEF_MEMBER_MISSING(struct_, send_key_event)) return; @@ -501,10 +501,8 @@ void CefBrowserCToCpp::SendKeyEvent(KeyType type, int key, int modifiers, // Execute struct_->send_key_event(struct_, type, - key, - modifiers, - sysChar, - imeChar); + &keyInfo, + modifiers); } void CefBrowserCToCpp::SendMouseClickEvent(int x, int y, MouseButtonType type, @@ -536,7 +534,8 @@ void CefBrowserCToCpp::SendMouseMoveEvent(int x, int y, bool mouseLeave) { mouseLeave); } -void CefBrowserCToCpp::SendMouseWheelEvent(int x, int y, int delta) { +void CefBrowserCToCpp::SendMouseWheelEvent(int x, int y, int deltaX, + int deltaY) { if (CEF_MEMBER_MISSING(struct_, send_mouse_wheel_event)) return; @@ -546,7 +545,8 @@ void CefBrowserCToCpp::SendMouseWheelEvent(int x, int y, int delta) { struct_->send_mouse_wheel_event(struct_, x, y, - delta); + deltaX, + deltaY); } void CefBrowserCToCpp::SendFocusEvent(bool setFocus) { diff --git a/cef1/libcef_dll/ctocpp/browser_ctocpp.h b/cef1/libcef_dll/ctocpp/browser_ctocpp.h index 8c917e1c8..d22900c55 100644 --- a/cef1/libcef_dll/ctocpp/browser_ctocpp.h +++ b/cef1/libcef_dll/ctocpp/browser_ctocpp.h @@ -70,12 +70,13 @@ class CefBrowserCToCpp virtual void Invalidate(const CefRect& dirtyRect) OVERRIDE; virtual bool GetImage(PaintElementType type, int width, int height, void* buffer) OVERRIDE; - virtual void SendKeyEvent(KeyType type, int key, int modifiers, bool sysChar, - bool imeChar) OVERRIDE; + virtual void SendKeyEvent(KeyType type, const CefKeyInfo& keyInfo, + int modifiers) OVERRIDE; virtual void SendMouseClickEvent(int x, int y, MouseButtonType type, bool mouseUp, int clickCount) OVERRIDE; virtual void SendMouseMoveEvent(int x, int y, bool mouseLeave) OVERRIDE; - virtual void SendMouseWheelEvent(int x, int y, int delta) OVERRIDE; + virtual void SendMouseWheelEvent(int x, int y, int deltaX, + int deltaY) OVERRIDE; virtual void SendFocusEvent(bool setFocus) OVERRIDE; virtual void SendCaptureLostEvent() OVERRIDE; }; diff --git a/cef1/patch/patch.cfg b/cef1/patch/patch.cfg index f4122f7ec..0a1e6a961 100644 --- a/cef1/patch/patch.cfg +++ b/cef1/patch/patch.cfg @@ -18,6 +18,7 @@ patches = [ }, { # http://code.google.com/p/gyp/issues/detail?id=223 + # http://codereview.chromium.org/10383117/ 'name': 'tools_gyp', 'path': '../tools/gyp/', }, diff --git a/cef1/patch/patches/tools_gyp.patch b/cef1/patch/patches/tools_gyp.patch index 2a8fdfa22..ba7b91051 100644 --- a/cef1/patch/patches/tools_gyp.patch +++ b/cef1/patch/patches/tools_gyp.patch @@ -1,6 +1,6 @@ Index: pylib/gyp/input.py =================================================================== ---- pylib/gyp/input.py (revision 1107) +--- pylib/gyp/input.py (revision 1323) +++ pylib/gyp/input.py (working copy) @@ -666,7 +666,8 @@ # that don't load quickly, this can be faster than @@ -12,3 +12,25 @@ Index: pylib/gyp/input.py parsed_contents = shlex.split(contents) py_module = __import__(parsed_contents[0]) +Index: pylib/gyp/mac_tool.py +=================================================================== +--- pylib/gyp/mac_tool.py (revision 1323) ++++ pylib/gyp/mac_tool.py (working copy) +@@ -54,16 +54,8 @@ + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest) +- # TODO: Given that files with arbitrary extensions can be copied to the +- # bundle, we will want to get rid of this whitelist eventually. +- elif extension in [ +- '.icns', '.manifest', '.pak', '.pdf', '.png', '.sb', '.sh', +- '.ttf', '.sdef']: +- shutil.copyfile(source, dest) + else: +- raise NotImplementedError( +- "Don't know how to copy bundle resources of type %s while copying " +- "%s to %s)" % (extension, source, dest)) ++ shutil.copyfile(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" diff --git a/cef1/tests/cefclient/cefclient_mac.mm b/cef1/tests/cefclient/cefclient_mac.mm index 3b29bb1a8..c4ddf38f9 100644 --- a/cef1/tests/cefclient/cefclient_mac.mm +++ b/cef1/tests/cefclient/cefclient_mac.mm @@ -14,6 +14,7 @@ #include "cefclient/binding_test.h" #include "cefclient/client_handler.h" #include "cefclient/extension_test.h" +#include "cefclient/osrtest_mac.h" #include "cefclient/resource_util.h" #include "cefclient/scheme_test.h" #include "cefclient/string_util.h" @@ -212,6 +213,8 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { - (IBAction)testDevToolsShow:(id)sender; - (IBAction)testDevToolsClose:(id)sender; - (IBAction)testPluginInfo:(id)sender; +- (IBAction)testOffscreenRendering:(id)sender; +- (IBAction)testTransparentOffscreenRendering:(id)sender; @end @implementation ClientAppDelegate @@ -305,6 +308,12 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { [testMenu addItemWithTitle:@"Plugin Info" action:@selector(testPluginInfo:) keyEquivalent:@""]; + [testMenu addItemWithTitle:@"Offscreen Rendering" + action:@selector(testOffscreenRendering:) + keyEquivalent:@""]; + [testMenu addItemWithTitle:@"Transparent Offscreen Rendering" + action:@selector(testTransparentOffscreenRendering:) + keyEquivalent:@""]; [testItem setSubmenu:testMenu]; [menubar addItem:testItem]; @@ -531,6 +540,14 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) { RunPluginInfoTest(g_handler->GetBrowser()); } +- (IBAction)testOffscreenRendering:(id)sender { + osrtest::RunTest(false); +} + +- (IBAction)testTransparentOffscreenRendering:(id)sender { + osrtest::RunTest(true); +} + // Sent by the default notification center immediately before the application // terminates. - (void)applicationWillTerminate:(NSNotification *)aNotification { diff --git a/cef1/tests/cefclient/osrenderer.cpp b/cef1/tests/cefclient/osrenderer.cpp new file mode 100644 index 000000000..cbf0a1070 --- /dev/null +++ b/cef1/tests/cefclient/osrenderer.cpp @@ -0,0 +1,410 @@ +// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +#include "cefclient/osrenderer.h" +#include "cefclient/util.h" + +#if defined(OS_WIN) +#include +#include +#elif defined(OS_MACOSX) +#include +#else +#error Platform is not supported. +#endif + +namespace { + +// Convert from BGRA to RGBA format and from upper-left to lower-left origin. +void ConvertToRGBA(const unsigned char* src, unsigned char* dst, + int width, int height) { + int sp = 0, dp = (height-1) * width * 4; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++, dp += 4, sp += 4) { + dst[dp] = src[sp+2]; // R + dst[dp+1] = src[sp+1]; // G + dst[dp+2] = src[sp]; // B + dst[dp+3] = src[sp+3]; // A + } + dp -= width * 8; + } +} + +void ConvertToRGBARect(const CefRect& clipRect, + const unsigned char* src, unsigned char* dst, + int width, int height) { + int sp = 0, dp = (height-1) * width * 4; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++, dp += 4, sp += 4) { + if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) && + (clipRect.y <= i) && (clipRect.y + clipRect.height > i)) { + dst[dp] = src[sp+2]; // R + dst[dp+1] = src[sp+1]; // G + dst[dp+2] = src[sp]; // B + dst[dp+3] = src[sp+3]; // A + } + } + dp -= width * 8; + } +} + +// Convert from BGRA to RGB format and from upper-left to lower-left origin. +void ConvertToRGB(const unsigned char* src, unsigned char* dst, + int width, int height) { + int sp = 0, dp = (height-1) * width * 3; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++, dp += 3, sp += 4) { + dst[dp] = src[sp+2]; // R + dst[dp+1] = src[sp+1]; // G + dst[dp+2] = src[sp]; // B + } + dp -= width * 6; + } +} + +void ConvertToRGBRect(const CefRect& clipRect, + const unsigned char* src, unsigned char* dst, + int width, int height) { + int sp = 0, dp = (height-1) * width * 3; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++, dp += 3, sp += 4) { + if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) && + (clipRect.y <= i) && (clipRect.y + clipRect.height > i)) { + dst[dp] = src[sp+2]; // R + dst[dp+1] = src[sp+1]; // G + dst[dp+2] = src[sp]; // B + } + } + dp -= width * 6; + } +} + +} // namespace + +ClientOSRenderer::ClientOSRenderer(bool transparent) + : transparent_(transparent), + initialized_(false), + texture_id_(0), + view_buffer_(NULL), + view_buffer_size_(0), + popup_buffer_(NULL), + popup_buffer_size_(0), + view_width_(0), + view_height_(0), + spin_x_(0), + spin_y_(0) { +} + +ClientOSRenderer::~ClientOSRenderer() { + Cleanup(); +} + +void ClientOSRenderer::Initialize() { + if (initialized_) + return; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + // Necessary for non-power-of-2 textures to render correctly. + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + if (transparent_) { + // Alpha blending style. + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + initialized_ = true; +} + +void ClientOSRenderer::Cleanup() { + if (view_buffer_) { + delete [] view_buffer_; + view_buffer_ = NULL; + view_buffer_size_ = 0; + } + if (popup_buffer_) { + delete [] popup_buffer_; + popup_buffer_ = NULL; + popup_buffer_size_ = 0; + } + + if (texture_id_ != 0) + glDeleteTextures(1, &texture_id_); +} + +void ClientOSRenderer::SetSize(int width, int height) { + if (!initialized_) + Initialize(); + + // Match GL units to screen coordinates. + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 0, width, height, 0.1, 100.0); + + if (transparent_) { + // Enable alpha blending. + glEnable(GL_BLEND); + } + + // Enable 2D textures. + glEnable(GL_TEXTURE_2D); + + GLuint new_texture_id = 0; + + // Create a new texture. + glGenTextures(1, &new_texture_id); + ASSERT(new_texture_id != 0); + glBindTexture(GL_TEXTURE_2D, new_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // Start with all white contents. + { + int size = width * height * (transparent_?4:3); + unsigned char* buffer = new unsigned char[size]; + memset(buffer, 255, size); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, buffer); + + delete [] buffer; + } + + if (texture_id_ != 0) { + // Draw the existing view buffer to the new texture. + DrawViewBuffer(width, height); + + // Delete the old texture. + glDeleteTextures(1, &texture_id_); + } + + texture_id_ = new_texture_id; + + // Disable 2D textures. + glDisable(GL_TEXTURE_2D); + + if (transparent_) { + // Disable alpha blending. + glDisable(GL_BLEND); + } +} + +void ClientOSRenderer::Render() { + ASSERT(initialized_); + + struct { + float tu, tv; + float x, y, z; + } static vertices[] = { + {0.0f, 0.0f, -1.0f, -1.0f, 0.0f}, + {1.0f, 0.0f, 1.0f, -1.0f, 0.0f}, + {1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, -1.0f, 1.0f, 0.0f} + }; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Draw the background gradient. + glPushAttrib(GL_ALL_ATTRIB_BITS); + glBegin(GL_QUADS); + glColor4f(1.0, 0.0, 0.0, 1.0); // red + glVertex2f(-1.0, -1.0); + glVertex2f(1.0, -1.0); + glColor4f(0.0, 0.0, 1.0, 1.0); // blue + glVertex2f(1.0, 1.0); + glVertex2f(-1.0, 1.0); + glEnd(); + glPopAttrib(); + + // Rotate the view based on the mouse spin. + glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f); + glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f); + + if (transparent_) { + // Enable alpha blending. + glEnable(GL_BLEND); + } + + // Enable 2D textures. + glEnable(GL_TEXTURE_2D); + + // Draw the facets with the texture. + ASSERT(texture_id_ != 0); + glBindTexture(GL_TEXTURE_2D, texture_id_); + glInterleavedArrays(GL_T2F_V3F, 0, vertices); + glDrawArrays(GL_QUADS, 0, 4); + + // Disable 2D textures. + glDisable(GL_TEXTURE_2D); + + if (transparent_) { + // Disable alpha blending. + glDisable(GL_BLEND); + } + + glFlush(); +} + +void ClientOSRenderer::OnPopupShow(CefRefPtr browser, + bool show) { + if (!show) { + // Clear the popup buffer. + popup_rect_.Set(0, 0, 0, 0); + if (popup_buffer_) { + delete [] popup_buffer_; + popup_buffer_ = NULL; + popup_buffer_size_ = 0; + } + } +} + +void ClientOSRenderer::OnPopupSize(CefRefPtr browser, + const CefRect& rect) { + if (rect.width > 0) { + // Update the popup rectange. It should always be inside the view. + ASSERT(rect.x + rect.width < view_width_ && + rect.y + rect.height < view_height_); + popup_rect_ = rect; + } +} + +void ClientOSRenderer::OnPaint(CefRefPtr browser, + CefRenderHandler::PaintElementType type, + const CefRenderHandler::RectList& dirtyRects, + const void* buffer) { + ASSERT(initialized_); + + // Retrieve the current size of the browser view. + browser->GetSize(type, view_width_, view_height_); + + if (transparent_) { + // Enable alpha blending. + glEnable(GL_BLEND); + } + + // Enable 2D textures. + glEnable(GL_TEXTURE_2D); + + ASSERT(texture_id_ != 0); + glBindTexture(GL_TEXTURE_2D, texture_id_); + + if (type == PET_VIEW) { + SetBufferSize(view_width_, view_height_, true); + + // Paint the view. + if (transparent_) { + CefRenderHandler::RectList::const_iterator i = dirtyRects.begin(); + for (; i != dirtyRects.end(); ++i) { + ConvertToRGBARect(*i, (unsigned char*)buffer, view_buffer_, + view_width_, view_height_); + } + } else { + CefRenderHandler::RectList::const_iterator i = dirtyRects.begin(); + for (; i != dirtyRects.end(); ++i) { + ConvertToRGBRect(*i, (unsigned char*)buffer, view_buffer_, + view_width_, view_height_); + } + } + + // Update the whole texture. This is done for simplicity instead of + // updating just the dirty region. + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_width_, view_height_, + transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, view_buffer_); + } + + if (popup_rect_.width > 0) { + if (type == PET_POPUP) { + // Paint the popup. + if (transparent_) + SetRGBA(buffer, popup_rect_.width, popup_rect_.height, false); + else + SetRGB(buffer, popup_rect_.width, popup_rect_.height, false); + } + + if (popup_buffer_) { + // Update the popup region. + glTexSubImage2D(GL_TEXTURE_2D, 0, popup_rect_.x, + view_height_ - popup_rect_.y - popup_rect_.height, + popup_rect_.width, popup_rect_.height, + transparent_?GL_RGBA:GL_RGB, + GL_UNSIGNED_BYTE, popup_buffer_); + } + } + + // Disable 2D textures. + glDisable(GL_TEXTURE_2D); + + if (transparent_) { + // Disable alpha blending. + glDisable(GL_BLEND); + } +} + +void ClientOSRenderer::SetSpin(float spinX, float spinY) { + spin_x_ = spinX; + spin_y_ = spinY; +} + +void ClientOSRenderer::IncrementSpin(float spinDX, float spinDY) { + spin_x_ -= spinDX; + spin_y_ -= spinDY; +} + +void ClientOSRenderer::SetBufferSize(int width, int height, bool view) { + int dst_size = width * height * (transparent_?4:3); + + // Allocate a new buffer if necesary. + if (view) { + if (dst_size > view_buffer_size_) { + if (view_buffer_) + delete [] view_buffer_; + view_buffer_ = new unsigned char[dst_size]; + view_buffer_size_ = dst_size; + } + } else { + if (dst_size > popup_buffer_size_) { + if (popup_buffer_) + delete [] popup_buffer_; + popup_buffer_ = new unsigned char[dst_size]; + popup_buffer_size_ = dst_size; + } + } +} + +// Set the contents of the RGBA buffer. +void ClientOSRenderer::SetRGBA(const void* src, int width, int height, + bool view) { + SetBufferSize(width, height, view); + ConvertToRGBA((unsigned char*)src, view?view_buffer_:popup_buffer_, width, + height); +} + +// Set the contents of the RGB buffer. +void ClientOSRenderer::SetRGB(const void* src, int width, int height, + bool view) { + SetBufferSize(width, height, view); + ConvertToRGB((unsigned char*)src, view?view_buffer_:popup_buffer_, width, + height); +} + +void ClientOSRenderer::DrawViewBuffer(int max_width, int max_height) { + if (max_width < view_width_ || max_height < view_height_) { + // The requested max size is smaller than the current view buffer. + int width = std::min(max_width, view_width_); + int height = std::min(max_height, view_height_); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, + transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, + view_buffer_); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, view_width_, view_height_, + transparent_?GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, view_buffer_); + } +} diff --git a/cef1/tests/cefclient/osrenderer.h b/cef1/tests/cefclient/osrenderer.h new file mode 100644 index 000000000..bc5e39915 --- /dev/null +++ b/cef1/tests/cefclient/osrenderer.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012 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_OSRENDERER_H_ +#define CEF_TESTS_CEFCLIENT_OSRENDERER_H_ + +#include "include/cef_browser.h" +#include "include/cef_render_handler.h" + +class ClientOSRenderer { + public: + // The context must outlive this object. + explicit ClientOSRenderer(bool transparent); + virtual ~ClientOSRenderer(); + + // Initialize the OpenGL environment. + void Initialize(); + + // Clean up the OpenGL environment. + void Cleanup(); + + // Set the size of the viewport. + void SetSize(int width, int height); + + // Render to the screen. + void Render(); + + // Forwarded from CefRenderHandler callbacks. + void OnPopupShow(CefRefPtr browser, + bool show); + void OnPopupSize(CefRefPtr browser, + const CefRect& rect); + void OnPaint(CefRefPtr browser, + CefRenderHandler::PaintElementType type, + const CefRenderHandler::RectList& dirtyRects, + const void* buffer); + + // Apply spin. + void SetSpin(float spinX, float spinY); + void IncrementSpin(float spinDX, float spinDY); + + private: + void SetBufferSize(int width, int height, bool view); + void SetRGBA(const void* src, int width, int height, bool view); + void SetRGB(const void* src, int width, int height, bool view); + void DrawViewBuffer(int max_width, int max_height); + + bool transparent_; + bool initialized_; + unsigned int texture_id_; + unsigned char* view_buffer_; + int view_buffer_size_; + unsigned char* popup_buffer_; + int popup_buffer_size_; + CefRect popup_rect_; + int view_width_; + int view_height_; + float spin_x_; + float spin_y_; +}; + +#endif // CEF_TESTS_CEFCLIENT_OSR_RENDERER_H_ + diff --git a/cef1/tests/cefclient/osrplugin.cpp b/cef1/tests/cefclient/osrplugin.cpp index 6444d5630..99e38f78a 100644 --- a/cef1/tests/cefclient/osrplugin.cpp +++ b/cef1/tests/cefclient/osrplugin.cpp @@ -4,6 +4,7 @@ // found in the LICENSE file. #include "cefclient/osrplugin.h" +#include "cefclient/osrenderer.h" #if defined(OS_WIN) @@ -28,27 +29,22 @@ NPNetscapeFuncs* g_osrbrowser = NULL; namespace { -GLuint g_textureID = -1; -float g_spinX = 0.0f; -float g_spinY = 0.0f; -int g_width = -1, g_height = -1; CefRefPtr g_offscreenBrowser; // If set to true alpha transparency will be used. bool g_offscreenTransparent = false; -#define GL_IMAGE_FORMAT (g_offscreenTransparent?GL_RGBA:GL_RGB) -#define GL_BYTE_COUNT (g_offscreenTransparent?4:3) - // Class holding pointers for the client plugin window. class ClientPlugin { public: - ClientPlugin() { - hWnd = NULL; - hDC = NULL; - hRC = NULL; + ClientPlugin(bool transparent) + : renderer(transparent), + hWnd(NULL), + hDC(NULL), + hRC(NULL) { } + ClientOSRenderer renderer; HWND hWnd; HDC hDC; HGLRC hRC; @@ -63,17 +59,9 @@ class ClientOSRHandler : public CefClient, public CefRenderHandler { public: explicit ClientOSRHandler(ClientPlugin* plugin) - : plugin_(plugin), - view_buffer_(NULL), - view_buffer_size_(0), - popup_buffer_(NULL), - popup_buffer_size_(0) { + : plugin_(plugin) { } ~ClientOSRHandler() { - if (view_buffer_) - delete [] view_buffer_; - if (popup_buffer_) - delete [] popup_buffer_; } // CefClient methods @@ -111,10 +99,12 @@ class ClientOSRHandler : public CefClient, virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE { REQUIRE_UI_THREAD(); - // Set the view size to match the plugin window size. - browser->SetSize(PET_VIEW, g_width, g_height); - g_offscreenBrowser = browser; + + // Set the off-screen browser size to match the plugin window size. + RECT clientRect; + ::GetClientRect(plugin_->hWnd, &clientRect); + g_offscreenBrowser->SetSize(PET_VIEW, clientRect.right, clientRect.bottom); } virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE { @@ -214,9 +204,12 @@ class ClientOSRHandler : public CefClient, // The simulated screen and view rectangle are the same. This is necessary // for popup menus to be located and sized inside the view. + RECT clientRect; + ::GetClientRect(plugin_->hWnd, &clientRect); + rect.x = rect.y = 0; - rect.width = g_width; - rect.height = g_height; + rect.width = clientRect.right; + rect.height = clientRect.bottom; return true; } @@ -243,29 +236,13 @@ class ClientOSRHandler : public CefClient, virtual void OnPopupShow(CefRefPtr browser, bool show) OVERRIDE { REQUIRE_UI_THREAD(); - - if (!show) { - // Clear the popup buffer. - popup_rect_.Set(0, 0, 0, 0); - if (popup_buffer_) { - delete [] popup_buffer_; - popup_buffer_ = NULL; - popup_buffer_size_ = 0; - } - } + plugin_->renderer.OnPopupShow(browser, show); } virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) OVERRIDE { REQUIRE_UI_THREAD(); - - if (rect.width > 0) { - // Update the popup rectange. It will always be inside the view due to - // HandleGetRect(). - ASSERT(rect.x + rect.width < g_width && - rect.y + rect.height < g_height); - popup_rect_ = rect; - } + plugin_->renderer.OnPopupSize(browser, rect); } virtual void OnPaint(CefRefPtr browser, @@ -275,65 +252,7 @@ class ClientOSRHandler : public CefClient, REQUIRE_UI_THREAD(); wglMakeCurrent(plugin_->hDC, plugin_->hRC); - - if (g_offscreenTransparent) { - // Enable alpha blending. - glEnable(GL_BLEND); - } - - // Enable 2D textures. - glEnable(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, g_textureID); - - if (type == PET_VIEW) { - SetBufferSize(g_width, g_height, true); - // Paint the view. - if (g_offscreenTransparent) { - RectList::const_iterator i = dirtyRects.begin(); - for (; i != dirtyRects.end(); ++i) { - ConvertToRGBARect(*i, (unsigned char*)buffer, view_buffer_, g_width, - g_height); - } - } else { - RectList::const_iterator i = dirtyRects.begin(); - for (i; i != dirtyRects.end(); ++i) { - ConvertToRGBRect(*i, (unsigned char*)buffer, view_buffer_, g_width, - g_height); - } - } - - // Update the whole texture. This is done for simplicity instead of - // updating just the dirty region. - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_width, g_height, - GL_IMAGE_FORMAT, GL_UNSIGNED_BYTE, view_buffer_); - } - - if (popup_rect_.width > 0) { - if (type == PET_POPUP) { - // Paint the popup. - if (g_offscreenTransparent) - SetRGBA(buffer, popup_rect_.width, popup_rect_.height, false); - else - SetRGB(buffer, popup_rect_.width, popup_rect_.height, false); - } - - if (popup_buffer_) { - // Update the popup region. - glTexSubImage2D(GL_TEXTURE_2D, 0, popup_rect_.x, - g_height-popup_rect_.y-popup_rect_.height, popup_rect_.width, - popup_rect_.height, GL_IMAGE_FORMAT, GL_UNSIGNED_BYTE, - popup_buffer_); - } - } - - // Disable 2D textures. - glDisable(GL_TEXTURE_2D); - - if (g_offscreenTransparent) { - // Disable alpha blending. - glDisable(GL_BLEND); - } + plugin_->renderer.OnPaint(browser, type, dirtyRects, buffer); } virtual void OnCursorChange(CefRefPtr browser, @@ -357,112 +276,7 @@ class ClientOSRHandler : public CefClient, AppGetBrowser()->GetMainFrame()->ExecuteJavaScript(ss.str(), "", 0); } - // Size the RGB buffer. - void SetBufferSize(int width, int height, bool view) { - int dst_size = width * height * GL_BYTE_COUNT; - - // Allocate a new buffer if necesary. - if (view) { - if (dst_size > view_buffer_size_) { - if (view_buffer_) - delete [] view_buffer_; - view_buffer_ = new unsigned char[dst_size]; - view_buffer_size_ = dst_size; - } - } else { - if (dst_size > popup_buffer_size_) { - if (popup_buffer_) - delete [] popup_buffer_; - popup_buffer_ = new unsigned char[dst_size]; - popup_buffer_size_ = dst_size; - } - } - } - - // Set the contents of the RGBA buffer. - void SetRGBA(const void* src, int width, int height, bool view) { - SetBufferSize(width, height, view); - ConvertToRGBA((unsigned char*)src, view?view_buffer_:popup_buffer_, width, - height); - } - - // Convert from BGRA to RGBA format and from upper-left to lower-left origin. - static void ConvertToRGBA(const unsigned char* src, unsigned char* dst, - int width, int height) { - int sp = 0, dp = (height-1) * width * 4; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++, dp += 4, sp += 4) { - dst[dp] = src[sp+2]; // R - dst[dp+1] = src[sp+1]; // G - dst[dp+2] = src[sp]; // B - dst[dp+3] = src[sp+3]; // A - } - dp -= width * 8; - } - } - - static void ConvertToRGBARect(const CefRect& clipRect, - const unsigned char* src, unsigned char* dst, - int width, int height) { - int sp = 0, dp = (height-1) * width * 4; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++, dp += 4, sp += 4) { - if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) && - (clipRect.y <= i) && (clipRect.y + clipRect.height > i)) { - dst[dp] = src[sp+2]; // R - dst[dp+1] = src[sp+1]; // G - dst[dp+2] = src[sp]; // B - dst[dp+3] = src[sp+3]; // A - } - } - dp -= width * 8; - } - } - - // Set the contents of the RGB buffer. - void SetRGB(const void* src, int width, int height, bool view) { - SetBufferSize(width, height, view); - ConvertToRGB((unsigned char*)src, view?view_buffer_:popup_buffer_, width, - height); - } - - // Convert from BGRA to RGB format and from upper-left to lower-left origin. - static void ConvertToRGB(const unsigned char* src, unsigned char* dst, - int width, int height) { - int sp = 0, dp = (height-1) * width * 3; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++, dp += 3, sp += 4) { - dst[dp] = src[sp+2]; // R - dst[dp+1] = src[sp+1]; // G - dst[dp+2] = src[sp]; // B - } - dp -= width * 6; - } - } - - static void ConvertToRGBRect(const CefRect& clipRect, - const unsigned char* src, unsigned char* dst, - int width, int height) { - int sp = 0, dp = (height-1) * width * 3; - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++, dp += 3, sp += 4) { - if ((clipRect.x <= j) && (clipRect.x + clipRect.width > j) && - (clipRect.y <= i) && (clipRect.y + clipRect.height > i)) { - dst[dp] = src[sp+2]; // R - dst[dp+1] = src[sp+1]; // G - dst[dp+2] = src[sp]; // B - } - } - dp -= width * 6; - } - } - ClientPlugin* plugin_; - unsigned char* view_buffer_; - int view_buffer_size_; - unsigned char* popup_buffer_; - int popup_buffer_size_; - CefRect popup_rect_; // Include the default reference counting implementation. IMPLEMENT_REFCOUNTING(ClientOSRPlugin); @@ -473,12 +287,12 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); // Enable GL. -void EnableGL(HWND hWnd, HDC * hDC, HGLRC * hRC) { +void EnableGL(ClientPlugin* plugin) { PIXELFORMATDESCRIPTOR pfd; int format; // Get the device context. - *hDC = GetDC(hWnd); + plugin->hDC = GetDC(plugin->hWnd); // Set the pixel format for the DC. ZeroMemory(&pfd, sizeof(pfd)); @@ -489,83 +303,30 @@ void EnableGL(HWND hWnd, HDC * hDC, HGLRC * hRC) { pfd.cColorBits = 24; pfd.cDepthBits = 16; pfd.iLayerType = PFD_MAIN_PLANE; - format = ChoosePixelFormat(*hDC, &pfd); - SetPixelFormat(*hDC, format, &pfd); + format = ChoosePixelFormat(plugin->hDC, &pfd); + SetPixelFormat(plugin->hDC, format, &pfd); // Create and enable the render context. - *hRC = wglCreateContext(*hDC); - wglMakeCurrent(*hDC, *hRC); + plugin->hRC = wglCreateContext(plugin->hDC); + wglMakeCurrent(plugin->hDC, plugin->hRC); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - // Necessary for non-power-of-2 textures to render correctly. - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - if (g_offscreenTransparent) { - // Alpha blending style. - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } + plugin->renderer.Initialize(); } // Disable GL. -void DisableGL(HWND hWnd, HDC hDC, HGLRC hRC) { - // Delete the texture. - if (g_textureID != -1) - glDeleteTextures(1, &g_textureID); +void DisableGL(ClientPlugin* plugin) { + plugin->renderer.Cleanup(); wglMakeCurrent(NULL, NULL); - wglDeleteContext(hRC); - ReleaseDC(hWnd, hDC); + wglDeleteContext(plugin->hRC); + ReleaseDC(plugin->hWnd, plugin->hDC); } // Size the GL view. void SizeGL(ClientPlugin* plugin, int width, int height) { - g_width = width; - g_height = height; - wglMakeCurrent(plugin->hDC, plugin->hRC); - // Match GL units to screen coordinates. - glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, 0, width, height, 0.1, 100.0); - - if (g_offscreenTransparent) { - // Enable alpha blending. - glEnable(GL_BLEND); - } - - // Enable 2D textures. - glEnable(GL_TEXTURE_2D); - - // Delete the existing exture. - if (g_textureID != -1) - glDeleteTextures(1, &g_textureID); - - // Create a new texture. - glGenTextures(1, &g_textureID); - glBindTexture(GL_TEXTURE_2D, g_textureID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - // Start with all white contents. - int size = width * height * GL_BYTE_COUNT; - unsigned char* buffer = new unsigned char[size]; - memset(buffer, 255, size); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, - GL_IMAGE_FORMAT, GL_UNSIGNED_BYTE, buffer); - - // Disable 2D textures. - glDisable(GL_TEXTURE_2D); - - if (g_offscreenTransparent) { - // Disable alpha blending. - glDisable(GL_BLEND); - } - - delete [] buffer; + plugin->renderer.SetSize(width, height); if (g_offscreenBrowser.get()) g_offscreenBrowser->SetSize(PET_VIEW, width, height); @@ -575,58 +336,7 @@ void SizeGL(ClientPlugin* plugin, int width, int height) { void RenderGL(ClientPlugin* plugin) { wglMakeCurrent(plugin->hDC, plugin->hRC); - struct { - float tu, tv; - float x, y, z; - } static vertices[] = { - {0.0f, 0.0f, -1.0f, -1.0f, 0.0f}, - {1.0f, 0.0f, 1.0f, -1.0f, 0.0f}, - {1.0f, 1.0f, 1.0f, 1.0f, 0.0f}, - {0.0f, 1.0f, -1.0f, 1.0f, 0.0f} - }; - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - // glTranslatef(0.0f, 0.0f, -3.0f); - - // Draw the background gradient. - glPushAttrib(GL_ALL_ATTRIB_BITS); - glBegin(GL_QUADS); - glColor4f(1.0, 0.0, 0.0, 1.0); // red - glVertex2f(-1.0, -1.0); - glVertex2f(1.0, -1.0); - glColor4f(0.0, 0.0, 1.0, 1.0); // blue - glVertex2f(1.0, 1.0); - glVertex2f(-1.0, 1.0); - glEnd(); - glPopAttrib(); - - // Rotate the view based on the mouse spin. - glRotatef(-g_spinX, 1.0f, 0.0f, 0.0f); - glRotatef(-g_spinY, 0.0f, 1.0f, 0.0f); - - if (g_offscreenTransparent) { - // Enable alpha blending. - glEnable(GL_BLEND); - } - - // Enable 2D textures. - glEnable(GL_TEXTURE_2D); - - // Draw the facets with the texture. - glBindTexture(GL_TEXTURE_2D, g_textureID); - glInterleavedArrays(GL_T2F_V3F, 0, vertices); - glDrawArrays(GL_QUADS, 0, 4); - - // Disable 2D textures. - glDisable(GL_TEXTURE_2D); - - if (g_offscreenTransparent) { - // Disable alpha blending. - glDisable(GL_BLEND); - } + plugin->renderer.Render(); SwapBuffers(plugin->hDC); } @@ -637,7 +347,7 @@ NPError NPP_NewImpl(NPMIMEType plugin_type, NPP instance, uint16 mode, if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; - ClientPlugin* plugin = new ClientPlugin; + ClientPlugin* plugin = new ClientPlugin(g_offscreenTransparent); instance->pdata = reinterpret_cast(plugin); return NPERR_NO_ERROR; @@ -649,7 +359,7 @@ NPError NPP_DestroyImpl(NPP instance, NPSavedData** save) { if (plugin) { if (plugin->hWnd) { DestroyWindow(plugin->hWnd); - DisableGL(plugin->hWnd, plugin->hDC, plugin->hRC); + DisableGL(plugin); } delete plugin; } @@ -693,7 +403,7 @@ NPError NPP_SetWindowImpl(NPP instance, NPWindow* window_info) { reinterpret_cast(plugin)); // Enable GL drawing for the window. - EnableGL(plugin->hWnd, &(plugin->hDC), &(plugin->hRC)); + EnableGL(plugin); // Create the off-screen rendering window. CefWindowInfo windowInfo; @@ -771,8 +481,7 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, if (mouseRotation) { // End rotation effect. mouseRotation = false; - g_spinX = 0; - g_spinY = 0; + plugin->renderer.SetSpin(0, 0); } else { if (g_offscreenBrowser.get()) { g_offscreenBrowser->SendMouseClickEvent(LOWORD(lParam), HIWORD(lParam), @@ -786,8 +495,8 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, // Apply rotation effect. curMousePos.x = LOWORD(lParam); curMousePos.y = HIWORD(lParam); - g_spinX -= (curMousePos.x - lastMousePos.x); - g_spinY -= (curMousePos.y - lastMousePos.y); + plugin->renderer.IncrementSpin((curMousePos.x - lastMousePos.x), + (curMousePos.y - lastMousePos.y)); lastMousePos.x = curMousePos.x; lastMousePos.y = curMousePos.y; } else { @@ -825,7 +534,7 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, case WM_MOUSEWHEEL: if (g_offscreenBrowser.get()) { g_offscreenBrowser->SendMouseWheelEvent(LOWORD(lParam), HIWORD(lParam), - GET_WHEEL_DELTA_WPARAM(wParam)); + 0, GET_WHEEL_DELTA_WPARAM(wParam)); } break; @@ -860,7 +569,8 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, case WM_IME_CHAR: if (g_offscreenBrowser.get()) { CefBrowser::KeyType type = KT_CHAR; - bool sysChar = false, imeChar = false; + CefKeyInfo keyInfo; + keyInfo.key = wParam; if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) type = KT_KEYDOWN; @@ -869,12 +579,12 @@ LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT message, WPARAM wParam, if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP || message == WM_SYSCHAR) - sysChar = true; + keyInfo.sysChar = true; if (message == WM_IME_CHAR) - imeChar = true; + keyInfo.imeChar = true; - g_offscreenBrowser->SendKeyEvent(type, wParam, lParam, sysChar, imeChar); + g_offscreenBrowser->SendKeyEvent(type, keyInfo, lParam); } break; diff --git a/cef1/tests/cefclient/osrtest_mac.h b/cef1/tests/cefclient/osrtest_mac.h new file mode 100644 index 000000000..a6e1ee297 --- /dev/null +++ b/cef1/tests/cefclient/osrtest_mac.h @@ -0,0 +1,15 @@ +// Copyright (c) 2012 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_OSRTEST_MAC_H_ +#define CEF_TESTS_CEFCLIENT_OSRTEST_MAC_H_ + +namespace osrtest { + +void RunTest(bool transparent); + +} // namespace osrtest + +#endif // CEF_TESTS_CEFCLIENT_OSRTEST_MAC_H_ + diff --git a/cef1/tests/cefclient/osrtest_mac.mm b/cef1/tests/cefclient/osrtest_mac.mm new file mode 100644 index 000000000..8fa8243c4 --- /dev/null +++ b/cef1/tests/cefclient/osrtest_mac.mm @@ -0,0 +1,752 @@ + // Copyright (c) 2012 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/osrtest_mac.h" +#include "include/cef_browser.h" +#include "cefclient/osrenderer.h" +#include "cefclient/client_popup_handler.h" +#include "cefclient/resource_util.h" +#include "cefclient/util.h" + +// The client OpenGL view. +@interface ClientOpenGLView : NSOpenGLView { + @public + NSTrackingArea* tracking_area_; + CefRefPtr browser_; + ClientOSRenderer* renderer_; + NSPoint last_mouse_pos_; + NSPoint cur_mouse_pos_; + bool rotating_; +} + +- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency; +- (NSPoint)getClickPointForEvent:(NSEvent*)event; +- (void)getKeyInfo:(CefKeyInfo&)info forEvent:(NSEvent*)event; +- (int)getModifiersForEvent:(NSEvent*)event; +- (BOOL)isKeyUpEvent:(NSEvent*)event; +- (BOOL)isKeyPadEvent:(NSEvent*)event; +@end + + +namespace { + +// Handler for off-screen rendering windows. +class ClientOSRHandler : public CefClient, + public CefLifeSpanHandler, + public CefLoadHandler, + public CefRequestHandler, + public CefDisplayHandler, + public CefRenderHandler { + public: + explicit ClientOSRHandler(ClientOpenGLView* view) + : view_(view) { + } + ~ClientOSRHandler() { + } + + void Disconnect() { + view_ = nil; + } + + // CefClient methods + virtual CefRefPtr GetLifeSpanHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetLoadHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetRequestHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetDisplayHandler() OVERRIDE { + return this; + } + virtual CefRefPtr GetRenderHandler() OVERRIDE { + return this; + } + + // CefLifeSpanHandler methods + + virtual bool OnBeforePopup(CefRefPtr parentBrowser, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + const CefString& url, + CefRefPtr& client, + CefBrowserSettings& settings) OVERRIDE { + REQUIRE_UI_THREAD(); + + windowInfo.m_bWindowRenderingDisabled = TRUE; + client = new ClientPopupHandler(view_->browser_); + return false; + } + + virtual void OnAfterCreated(CefRefPtr browser) OVERRIDE { + REQUIRE_UI_THREAD(); + + // Set the view size to match the window size. + const NSRect bounds = [view_ bounds]; + browser->SetSize(PET_VIEW, bounds.size.width, bounds.size.height); + + view_->browser_ = browser; + } + + virtual void OnBeforeClose(CefRefPtr browser) OVERRIDE { + if (view_) + view_->browser_ = NULL; + } + + // CefLoadHandler methods + + virtual void OnLoadStart(CefRefPtr browser, + CefRefPtr frame) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!browser->IsPopup() && frame->IsMain()) { + // We've just started loading a page + SetLoading(true); + } + } + + virtual void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!browser->IsPopup() && frame->IsMain()) { + // We've just finished loading a page + SetLoading(false); + } + } + + // CefRequestHandler methods + + virtual bool OnBeforeResourceLoad(CefRefPtr browser, + CefRefPtr request, + CefString& redirectUrl, + CefRefPtr& resourceStream, + CefRefPtr response, + int loadFlags) OVERRIDE { + REQUIRE_IO_THREAD(); + + std::string url = request->GetURL(); + if (url == "http://tests/osrtest") { + // Show the osrtest HTML contents + resourceStream = GetBinaryResourceReader("osrtest.html"); + response->SetMimeType("text/html"); + response->SetStatus(200); + } else if (url == "http://tests/transparency") { + // Show the osrtest HTML contents + resourceStream = GetBinaryResourceReader("transparency.html"); + response->SetMimeType("text/html"); + response->SetStatus(200); + } else if (strstr(url.c_str(), "/logoball.png") != NULL) { + // Load the "logoball.png" image resource. + resourceStream = GetBinaryResourceReader("logoball.png"); + response->SetMimeType("image/png"); + response->SetStatus(200); + } + + return false; + } + + // CefDisplayHandler methods + + virtual void OnNavStateChange(CefRefPtr browser, + bool canGoBack, + bool canGoForward) OVERRIDE { + REQUIRE_UI_THREAD(); + } + + virtual void OnAddressChange(CefRefPtr browser, + CefRefPtr frame, + const CefString& url) OVERRIDE { + REQUIRE_UI_THREAD(); + } + + virtual void OnTitleChange(CefRefPtr browser, + const CefString& title) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + // Set the frame window title bar + NSWindow* window = [view_ window]; + std::string titleStr(title); + NSString* str = [NSString stringWithUTF8String:titleStr.c_str()]; + [window setTitle:str]; + } + + // CefRenderHandler methods + + virtual bool GetViewRect(CefRefPtr browser, + CefRect& rect) OVERRIDE { + 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; + } + + virtual bool GetScreenRect(CefRefPtr browser, + CefRect& rect) OVERRIDE { + return GetViewRect(browser, rect); + } + + virtual bool GetScreenPoint(CefRefPtr browser, + int viewX, + int viewY, + int& screenX, + int& screenY) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!view_) + return false; + + // Convert the point from view coordinates to actual screen coordinates. + NSRect bounds = [view_ bounds]; + NSPoint view_pt = {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; + } + + virtual void OnPopupShow(CefRefPtr browser, + bool show) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + view_->renderer_->OnPopupShow(browser, show); + } + + virtual void OnPopupSize(CefRefPtr browser, + const CefRect& rect) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + view_->renderer_->OnPopupSize(browser, rect); + } + + virtual void OnPaint(CefRefPtr browser, + PaintElementType type, + const RectList& dirtyRects, + const void* buffer) OVERRIDE { + REQUIRE_UI_THREAD(); + + if (!view_) + return; + + [[view_ openGLContext] makeCurrentContext]; + + view_->renderer_->OnPaint(browser, type, dirtyRects, buffer); + + // Notify the view to redraw the invalidated regions. + { + RectList::const_iterator i = dirtyRects.begin(); + for (; i != dirtyRects.end(); ++i) { + NSRect rect = {{i->x, i->y}, {i->width, i->height}}; + [view_ setNeedsDisplayInRect:rect]; + } + } + } + + virtual void OnCursorChange(CefRefPtr browser, + CefCursorHandle cursor) OVERRIDE { + REQUIRE_UI_THREAD(); + [cursor set]; + } + + private: + void SetLoading(bool isLoading) { + } + + ClientOpenGLView* view_; + + // Include the default reference counting implementation. + IMPLEMENT_REFCOUNTING(ClientOSRPlugin); +}; + +} // namespace + + +@implementation ClientOpenGLView + +- (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_]; + } + return self; +} + +- (void)dealloc { + if (browser_) { + static_cast(browser_->GetClient().get())->Disconnect(); + browser_->CloseBrowser(); + browser_ = NULL; + } + if (renderer_) + delete renderer_; + + [super dealloc]; +} + +- (void)drawRect: (NSRect)bounds { + NSOpenGLContext* context = [self openGLContext]; + [context makeCurrentContext]; + + renderer_->Render(); + + [context flushBuffer]; +} + +- (void)setFrame:(NSRect)frameRect { + [super setFrame:frameRect]; + + int width = frameRect.size.width; + int height = frameRect.size.height; + + [[self openGLContext] makeCurrentContext]; + + renderer_->SetSize(width, height); + + if (browser_) + browser_->SetSize(PET_VIEW, width, height); +} + +- (void)mouseDown:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_LEFT, false, clickCount); +} + +- (void)rightMouseDown:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + + if ([event modifierFlags] & NSShiftKeyMask) { + // Start rotation effect. + last_mouse_pos_ = cur_mouse_pos_ = point; + rotating_ = true; + return; + } + + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_RIGHT, false, clickCount); +} + +- (void)otherMouseDown:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_MIDDLE, false, + clickCount); +} + +- (void)mouseUp:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_LEFT, true, clickCount); +} + +- (void)rightMouseUp:(NSEvent *)event { + if (!browser_) + return; + + if (rotating_) { + // End rotation effect. + renderer_->SetSpin(0, 0); + rotating_ = false; + [self setNeedsDisplay:YES]; + return; + } + + NSPoint point = [self getClickPointForEvent:event]; + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_RIGHT, true, clickCount); +} + +- (void)otherMouseUp:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + int clickCount = [event clickCount]; + + browser_->SendMouseClickEvent(point.x, point.y, MBT_MIDDLE, true, clickCount); +} + +- (void)mouseMoved:(NSEvent *)event { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + + if (rotating_) { + // Apply rotation effect. + cur_mouse_pos_ = point; + 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; + } + + browser_->SendMouseMoveEvent(point.x, point.y, 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 { + if (!browser_) + return; + + NSPoint point = [self getClickPointForEvent:event]; + + browser_->SendMouseMoveEvent(point.x, point.y, true); +} + +- (void)keyDown:(NSEvent *)event { + if (!browser_) + return; + + CefKeyInfo keyInfo; + [self getKeyInfo:keyInfo forEvent:event]; + int modifiers = [self getModifiersForEvent:event]; + + browser_->SendKeyEvent(KT_KEYDOWN, keyInfo, modifiers); + + if ([event modifierFlags] & (NSNumericPadKeyMask | NSFunctionKeyMask)) { + // Don't send a Char event for non-char keys like arrows, function keys and + // clear. + switch (keyInfo.keyCode) { + 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; + } + } + + browser_->SendKeyEvent(KT_CHAR, keyInfo, modifiers); +} + +- (void)keyUp:(NSEvent *)event { + if (!browser_) + return; + + CefKeyInfo keyInfo; + [self getKeyInfo:keyInfo forEvent:event]; + int modifiers = [self getModifiersForEvent:event]; + + browser_->SendKeyEvent(KT_KEYUP, keyInfo, modifiers); +} + +- (void)flagsChanged:(NSEvent *)event { + if ([self isKeyUpEvent:event]) + [self keyUp:event]; + else + [self keyDown:event]; +} + +- (void)scrollWheel:(NSEvent *)event { + if (!browser_) + return; + + CGEventRef cgEvent = [event CGEvent]; + ASSERT(cgEvent); + + NSPoint point = [self getClickPointForEvent:event]; + int deltaX = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2); + int deltaY = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); + + browser_->SendMouseWheelEvent(point.x, point.y, deltaX, deltaY); +} + +- (BOOL)canBecomeKeyView { + return (browser_ != NULL); +} + +- (BOOL)acceptsFirstResponder { + return (browser_ != NULL); +} + +- (BOOL)becomeFirstResponder { + if (browser_) { + browser_->SendFocusEvent(true); + return [super becomeFirstResponder]; + } + + return NO; +} + +- (BOOL)resignFirstResponder { + if (browser_) { + browser_->SendFocusEvent(false); + return [super resignFirstResponder]; + } + + return NO; +} + +- (void)undo:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Undo(); +} + +- (void)redo:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Redo(); +} + +- (void)cut:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Cut(); +} + +- (void)copy:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Copy(); +} + +- (void)paste:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Paste(); +} + +- (void)delete:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->Delete(); +} + +- (void)selectAll:(id)sender { + if (browser_) + browser_->GetFocusedFrame()->SelectAll(); +} + +- (NSPoint)getClickPointForEvent:(NSEvent*)event { + NSPoint windowLocal = [event locationInWindow]; + NSPoint contentLocal = [self convertPoint:windowLocal fromView:nil]; + int x = contentLocal.x; + int y = [self frame].size.height - contentLocal.y; // Flip y. + + return {x,y}; +} + +- (void)getKeyInfo:(CefKeyInfo&)info forEvent:(NSEvent*)event { + if ([event type] == NSKeyDown || [event type] == NSKeyUp) { + NSString* s = [event characters]; + if ([s length] > 0) + info.character = [s characterAtIndex:0]; + + s = [event charactersIgnoringModifiers]; + if ([s length] > 0) + info.characterNoModifiers = [s characterAtIndex:0]; + } + + info.keyCode = [event keyCode]; +} + +- (int)getModifiersForEvent:(NSEvent*)event { + int modifiers = 0; + + if ([event modifierFlags] & NSControlKeyMask) + modifiers |= KEY_CTRL; + if ([event modifierFlags] & NSShiftKeyMask) + modifiers |= KEY_SHIFT; + if ([event modifierFlags] & NSAlternateKeyMask) + modifiers |= KEY_ALT; + if ([event modifierFlags] & NSCommandKeyMask) + modifiers |= KEY_META; + if ([self isKeyPadEvent:event]) + modifiers |= KEY_KEYPAD; + + 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; +} + +@end + + +namespace osrtest { + +void RunTest(bool transparent) { + NSRect screen_rect = [[NSScreen mainScreen] visibleFrame]; + NSRect window_rect = {{0, screen_rect.size.height}, {700, 700}}; + NSWindow* newWnd = [[NSWindow alloc] + initWithContentRect:window_rect + styleMask:(NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask | + NSUnifiedTitleAndToolbarWindowMask) + backing:NSBackingStoreBuffered + defer:NO]; + ASSERT(newWnd); + + ClientOpenGLView* view = [[ClientOpenGLView alloc] initWithFrame:window_rect + andTransparency:transparent]; + [view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + + [newWnd setContentView:view]; + [view release]; + + CefWindowInfo info; + CefBrowserSettings settings; + + // Initialize the window info as off-screen. + info.SetAsOffScreen(view); + info.SetTransparentPainting(transparent); + + // Creat the browser window. + CefBrowser::CreateBrowser(info, new ClientOSRHandler(view), + "http://tests/osrtest", settings); + + [newWnd makeKeyAndOrderFront: nil]; +} + +} // namespace osrtest + diff --git a/cef1/tests/cefclient/res/osrtest.html b/cef1/tests/cefclient/res/osrtest.html new file mode 100644 index 000000000..c3fdc2eee --- /dev/null +++ b/cef1/tests/cefclient/res/osrtest.html @@ -0,0 +1,40 @@ + + +Off-Screen Rendering Test + + +
+ + + + + + + + + + + + + + + + + +
Off-Screen Rendering App Example
An OpenGL view that renders content from an off-screen browser window.
You can rotate the view! +
    +
  • Click and drag the view with the left mouse button while holding the shift key.
  • +
  • Enter a URL and click the "Go!" button to browse to a new Website.
  • +
  • Click here to test transparency.
  • +
  • Click here to test a windowless plugin.
  • +
+
+
+ + +
+
+
+ + + diff --git a/cef1/tools/distrib/cefclient.gyp b/cef1/tools/distrib/cefclient.gyp index e478bf665..d948b3e83 100644 --- a/cef1/tools/distrib/cefclient.gyp +++ b/cef1/tools/distrib/cefclient.gyp @@ -96,6 +96,7 @@ 'link_settings': { 'libraries': [ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', '$(CONFIGURATION)/libcef.dylib' ], },