diff --git a/cef.gyp b/cef.gyp index 6c167d579..cbdfd62dd 100644 --- a/cef.gyp +++ b/cef.gyp @@ -415,6 +415,7 @@ 'tests/unittests/display_unittest.cc', 'tests/unittests/dom_unittest.cc', 'tests/unittests/download_unittest.cc', + 'tests/unittests/draggable_regions_unittest.cc', 'tests/unittests/frame_unittest.cc', 'tests/unittests/geolocation_unittest.cc', 'tests/unittests/jsdialog_unittest.cc', diff --git a/include/capi/cef_drag_handler_capi.h b/include/capi/cef_drag_handler_capi.h index 1e2368b25..c18440821 100644 --- a/include/capi/cef_drag_handler_capi.h +++ b/include/capi/cef_drag_handler_capi.h @@ -66,6 +66,17 @@ typedef struct _cef_drag_handler_t { int (CEF_CALLBACK *on_drag_enter)(struct _cef_drag_handler_t* self, struct _cef_browser_t* browser, struct _cef_drag_data_t* dragData, cef_drag_operations_mask_t mask); + + /// + // Called whenever draggable regions for the browser window change. These can + // be specified using the '-webkit-app-region: drag/no-drag' CSS-property. If + // draggable regions are never defined in a document this function will also + // never be called. If the last draggable region is removed from a document + // this function will be called with an NULL vector. + /// + void (CEF_CALLBACK *on_draggable_regions_changed)( + struct _cef_drag_handler_t* self, struct _cef_browser_t* browser, + size_t regionsCount, cef_draggable_region_t const* regions); } cef_drag_handler_t; diff --git a/include/cef_drag_handler.h b/include/cef_drag_handler.h index 7cfc40ba5..3e26f9755 100644 --- a/include/cef_drag_handler.h +++ b/include/cef_drag_handler.h @@ -61,6 +61,18 @@ class CefDragHandler : public virtual CefBase { virtual bool OnDragEnter(CefRefPtr browser, CefRefPtr dragData, DragOperationsMask mask) { return false; } + + /// + // Called whenever draggable regions for the browser window change. These can + // be specified using the '-webkit-app-region: drag/no-drag' CSS-property. If + // draggable regions are never defined in a document this method will also + // never be called. If the last draggable region is removed from a document + // this method will be called with an empty vector. + /// + /*--cef()--*/ + virtual void OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) {} }; #endif // CEF_INCLUDE_CEF_DRAG_HANDLER_H_ diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 6d34863a1..2e1ad2879 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -1265,6 +1265,21 @@ typedef struct _cef_size_t { int height; } cef_size_t; +/// +// Structure representing a draggable region. +/// +typedef struct _cef_draggable_region_t { + /// + // Bounds of the region. + /// + cef_rect_t bounds; + + /// + // True (1) this this region is draggable and false (0) otherwise. + /// + int draggable; +} cef_draggable_region_t; + /// // Existing process IDs. /// diff --git a/include/internal/cef_types_wrappers.h b/include/internal/cef_types_wrappers.h index d120463fa..ce09fd5cf 100644 --- a/include/internal/cef_types_wrappers.h +++ b/include/internal/cef_types_wrappers.h @@ -257,6 +257,50 @@ inline bool operator!=(const CefSize& a, const CefSize& b) { } +struct CefDraggableRegionTraits { + typedef cef_draggable_region_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 = *src; + } +}; + +/// +// Class representing a draggable region. +/// +class CefDraggableRegion : public CefStructBase { + public: + typedef CefStructBase parent; + + CefDraggableRegion() : parent() {} + CefDraggableRegion(const cef_draggable_region_t& r) // NOLINT(runtime/explicit) + : parent(r) {} + CefDraggableRegion(const CefDraggableRegion& r) // NOLINT(runtime/explicit) + : parent(r) {} + CefDraggableRegion(const CefRect& bounds, bool draggable) : parent() { + Set(bounds, draggable); + } + + void Set(const CefRect& bounds, bool draggable) { + this->bounds = bounds, this->draggable = draggable; + } +}; + +inline bool operator==(const CefDraggableRegion& a, + const CefDraggableRegion& b) { + return a.bounds == b.bounds && a.draggable == b.draggable; +} + +inline bool operator!=(const CefDraggableRegion& a, + const CefDraggableRegion& b) { + return !(a == b); +} + + struct CefScreenInfoTraits { typedef cef_screen_info_t struct_type; diff --git a/libcef/browser/browser_host_impl.cc b/libcef/browser/browser_host_impl.cc index 44d0e2f32..3dfeae012 100644 --- a/libcef/browser/browser_host_impl.cc +++ b/libcef/browser/browser_host_impl.cc @@ -2659,6 +2659,8 @@ bool CefBrowserHostImpl::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(CefBrowserHostImpl, message) IPC_MESSAGE_HANDLER(CefHostMsg_FrameIdentified, OnFrameIdentified) IPC_MESSAGE_HANDLER(CefHostMsg_DidFinishLoad, OnDidFinishLoad) + IPC_MESSAGE_HANDLER(CefHostMsg_UpdateDraggableRegions, + OnUpdateDraggableRegions) IPC_MESSAGE_HANDLER(CefHostMsg_Request, OnRequest) IPC_MESSAGE_HANDLER(CefHostMsg_Response, OnResponse) IPC_MESSAGE_HANDLER(CefHostMsg_ResponseAck, OnResponseAck) @@ -2716,6 +2718,26 @@ void CefBrowserHostImpl::OnDidFinishLoad(int64 frame_id, OnLoadEnd(frame, validated_url, http_status_code); } +void CefBrowserHostImpl::OnUpdateDraggableRegions( + const std::vector& regions) { + std::vector draggable_regions; + draggable_regions.reserve(regions.size()); + + std::vector::const_iterator it = regions.begin(); + for (; it != regions.end(); ++it) { + const gfx::Rect& rect(it->bounds); + const CefRect bounds(rect.x(), rect.y(), rect.width(), rect.height()); + draggable_regions.push_back(CefDraggableRegion(bounds, it->draggable)); + } + + if (client_.get()) { + CefRefPtr handler = client_->GetDragHandler(); + if (handler.get()) { + handler->OnDraggableRegionsChanged(this, draggable_regions); + } + } +} + void CefBrowserHostImpl::OnRequest(const Cef_Request_Params& params) { bool success = false; std::string response; diff --git a/libcef/browser/browser_host_impl.h b/libcef/browser/browser_host_impl.h index aeacde981..cc920d4d9 100644 --- a/libcef/browser/browser_host_impl.h +++ b/libcef/browser/browser_host_impl.h @@ -64,6 +64,7 @@ class Widget; class CefWindowX11; #endif +struct Cef_DraggableRegion_Params; struct Cef_Request_Params; struct Cef_Response_Params; class CefBrowserInfo; @@ -477,6 +478,8 @@ class CefBrowserHostImpl : public CefBrowserHost, const GURL& validated_url, bool is_main_frame, int http_status_code); + void OnUpdateDraggableRegions( + const std::vector& regions); void OnRequest(const Cef_Request_Params& params); void OnResponse(const Cef_Response_Params& params); void OnResponseAck(int request_id); diff --git a/libcef/common/cef_messages.h b/libcef/common/cef_messages.h index 2e6588327..f13f020d2 100644 --- a/libcef/common/cef_messages.h +++ b/libcef/common/cef_messages.h @@ -68,6 +68,12 @@ IPC_STRUCT_BEGIN(Cef_CrossOriginWhiteListEntry_Params) IPC_STRUCT_MEMBER(bool, allow_target_subdomains) IPC_STRUCT_END() +// Parameters structure for a draggable region. +IPC_STRUCT_BEGIN(Cef_DraggableRegion_Params) + IPC_STRUCT_MEMBER(gfx::Rect, bounds) + IPC_STRUCT_MEMBER(bool, draggable) +IPC_STRUCT_END() + // Messages sent from the browser to the renderer. @@ -189,6 +195,10 @@ IPC_MESSAGE_ROUTED1(CefHostMsg_Response, IPC_MESSAGE_ROUTED1(CefHostMsg_ResponseAck, int /* request_id */) +// Sent by the renderer when the draggable regions are updated. +IPC_MESSAGE_ROUTED1(CefHostMsg_UpdateDraggableRegions, + std::vector /* regions */) + // Singly-included section for struct and custom IPC traits. #ifndef CEF_LIBCEF_COMMON_CEF_MESSAGES_H_ diff --git a/libcef/renderer/browser_impl.cc b/libcef/renderer/browser_impl.cc index 698c541f5..b46a460d9 100644 --- a/libcef/renderer/browser_impl.cc +++ b/libcef/renderer/browser_impl.cc @@ -571,6 +571,19 @@ void CefBrowserImpl::FocusedNodeChanged(const blink::WebNode& node) { } } +void CefBrowserImpl::DraggableRegionsChanged(blink::WebFrame* frame) { + blink::WebVector webregions = + frame->document().draggableRegions(); + std::vector regions; + for (size_t i = 0; i < webregions.size(); ++i) { + Cef_DraggableRegion_Params region; + region.bounds = webregions[i].bounds; + region.draggable = webregions[i].draggable; + regions.push_back(region); + } + Send(new CefHostMsg_UpdateDraggableRegions(routing_id(), regions)); +} + bool CefBrowserImpl::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CefBrowserImpl, message) diff --git a/libcef/renderer/browser_impl.h b/libcef/renderer/browser_impl.h index 7c991f807..cdd4cacb7 100644 --- a/libcef/renderer/browser_impl.h +++ b/libcef/renderer/browser_impl.h @@ -119,6 +119,7 @@ class CefBrowserImpl : public CefBrowser, bool is_new_navigation) override; void FrameDetached(blink::WebFrame* frame) override; void FocusedNodeChanged(const blink::WebNode& node) override; + void DraggableRegionsChanged(blink::WebFrame* frame) override; bool OnMessageReceived(const IPC::Message& message) override; // RenderViewObserver::OnMessageReceived message handlers. diff --git a/libcef_dll/cpptoc/drag_handler_cpptoc.cc b/libcef_dll/cpptoc/drag_handler_cpptoc.cc index e7186f8cd..ef9ba2171 100644 --- a/libcef_dll/cpptoc/drag_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/drag_handler_cpptoc.cc @@ -46,6 +46,37 @@ int CEF_CALLBACK drag_handler_on_drag_enter(struct _cef_drag_handler_t* self, return _retval; } +void CEF_CALLBACK drag_handler_on_draggable_regions_changed( + struct _cef_drag_handler_t* self, cef_browser_t* browser, + size_t regionsCount, cef_draggable_region_t const* regions) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) + return; + // Verify param: regions; type: simple_vec_byref_const + DCHECK(regionsCount == 0 || regions); + if (regionsCount > 0 && !regions) + return; + + // Translate param: regions; type: simple_vec_byref_const + std::vector regionsList; + if (regionsCount > 0) { + for (size_t i = 0; i < regionsCount; ++i) { + regionsList.push_back(regions[i]); + } + } + + // Execute + CefDragHandlerCppToC::Get(self)->OnDraggableRegionsChanged( + CefBrowserCToCpp::Wrap(browser), + regionsList); +} + } // namespace @@ -53,6 +84,8 @@ int CEF_CALLBACK drag_handler_on_drag_enter(struct _cef_drag_handler_t* self, CefDragHandlerCppToC::CefDragHandlerCppToC() { GetStruct()->on_drag_enter = drag_handler_on_drag_enter; + GetStruct()->on_draggable_regions_changed = + drag_handler_on_draggable_regions_changed; } template<> CefRefPtr CefCppToC browser, return _retval?true:false; } +void CefDragHandlerCToCpp::OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) { + cef_drag_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_draggable_regions_changed)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) + return; + + // Translate param: regions; type: simple_vec_byref_const + const size_t regionsCount = regions.size(); + cef_draggable_region_t* regionsList = NULL; + if (regionsCount > 0) { + regionsList = new cef_draggable_region_t[regionsCount]; + DCHECK(regionsList); + if (regionsList) { + for (size_t i = 0; i < regionsCount; ++i) { + regionsList[i] = regions[i]; + } + } + } + + // Execute + _struct->on_draggable_regions_changed(_struct, + CefBrowserCppToC::Wrap(browser), + regionsCount, + regionsList); + + // Restore param:regions; type: simple_vec_byref_const + if (regionsList) + delete [] regionsList; +} + // CONSTRUCTOR - Do not edit by hand. diff --git a/libcef_dll/ctocpp/drag_handler_ctocpp.h b/libcef_dll/ctocpp/drag_handler_ctocpp.h index 5d7bf48f1..865361739 100644 --- a/libcef_dll/ctocpp/drag_handler_ctocpp.h +++ b/libcef_dll/ctocpp/drag_handler_ctocpp.h @@ -18,6 +18,7 @@ #pragma message("Warning: "__FILE__" may be accessed DLL-side only") #else // BUILDING_CEF_SHARED +#include #include "include/cef_drag_handler.h" #include "include/capi/cef_drag_handler_capi.h" #include "libcef_dll/ctocpp/ctocpp.h" @@ -33,6 +34,8 @@ class CefDragHandlerCToCpp // CefDragHandler methods. bool OnDragEnter(CefRefPtr browser, CefRefPtr dragData, DragOperationsMask mask) override; + void OnDraggableRegionsChanged(CefRefPtr browser, + const std::vector& regions) override; }; #endif // BUILDING_CEF_SHARED diff --git a/tests/cefclient/browser/browser_window.cc b/tests/cefclient/browser/browser_window.cc index e5efac2e8..eedb822a9 100644 --- a/tests/cefclient/browser/browser_window.cc +++ b/tests/cefclient/browser/browser_window.cc @@ -68,4 +68,10 @@ void BrowserWindow::OnSetLoadingState(bool isLoading, delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward); } +void BrowserWindow::OnSetDraggableRegions( + const std::vector& regions) { + REQUIRE_MAIN_THREAD(); + delegate_->OnSetDraggableRegions(regions); +} + } // namespace client diff --git a/tests/cefclient/browser/browser_window.h b/tests/cefclient/browser/browser_window.h index 99f8873e2..ff0bb2e29 100644 --- a/tests/cefclient/browser/browser_window.h +++ b/tests/cefclient/browser/browser_window.h @@ -39,6 +39,10 @@ class BrowserWindow : public ClientHandler::Delegate { bool canGoBack, bool canGoForward) = 0; + // Set the draggable regions. + virtual void OnSetDraggableRegions( + const std::vector& regions) = 0; + protected: virtual ~Delegate() {} }; @@ -101,6 +105,8 @@ class BrowserWindow : public ClientHandler::Delegate { void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE; + void OnSetDraggableRegions( + const std::vector& regions) OVERRIDE; Delegate* delegate_; CefRefPtr browser_; diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 3162771ff..aa30d69f9 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -290,6 +290,14 @@ bool ClientHandler::OnDragEnter(CefRefPtr browser, return false; } +void ClientHandler::OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) { + CEF_REQUIRE_UI_THREAD(); + + NotifyDraggableRegions(regions); +} + bool ClientHandler::OnRequestGeolocationPermission( CefRefPtr browser, const CefString& requesting_url, @@ -676,6 +684,19 @@ void ClientHandler::NotifyLoadingState(bool isLoading, delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward); } +void ClientHandler::NotifyDraggableRegions( + const std::vector& regions) { + if (!CURRENTLY_ON_MAIN_THREAD()) { + // Execute this method on the main thread. + MAIN_POST_CLOSURE( + base::Bind(&ClientHandler::NotifyDraggableRegions, this, regions)); + return; + } + + if (delegate_) + delegate_->OnSetDraggableRegions(regions); +} + void ClientHandler::BuildTestMenu(CefRefPtr model) { if (model->GetCount() > 0) model->AddSeparator(); diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index bf6e98895..ef9f2a2cc 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -57,6 +57,10 @@ class ClientHandler : public CefClient, bool canGoBack, bool canGoForward) = 0; + // Set the draggable regions. + virtual void OnSetDraggableRegions( + const std::vector& regions) = 0; + protected: virtual ~Delegate() {} }; @@ -152,6 +156,10 @@ class ClientHandler : public CefClient, CefRefPtr dragData, CefDragHandler::DragOperationsMask mask) OVERRIDE; + void OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) OVERRIDE; + // CefGeolocationHandler methods bool OnRequestGeolocationPermission( CefRefPtr browser, @@ -263,6 +271,8 @@ class ClientHandler : public CefClient, void NotifyLoadingState(bool isLoading, bool canGoBack, bool canGoForward); + void NotifyDraggableRegions( + const std::vector& regions); // Test context menu creation. void BuildTestMenu(CefRefPtr model); @@ -288,7 +298,7 @@ class ClientHandler : public CefClient, // Handles the browser side of query routing. The renderer side is handled // in client_renderer.cc. CefRefPtr message_router_; - + // MAIN THREAD MEMBERS // The following members will only be accessed on the main thread. This will // be the same as the CEF UI thread except when using multi-threaded message diff --git a/tests/cefclient/browser/root_window_gtk.cc b/tests/cefclient/browser/root_window_gtk.cc index bd4af228e..4f9c984df 100644 --- a/tests/cefclient/browser/root_window_gtk.cc +++ b/tests/cefclient/browser/root_window_gtk.cc @@ -387,6 +387,12 @@ void RootWindowGtk::OnSetLoadingState(bool isLoading, } } +void RootWindowGtk::OnSetDraggableRegions( + const std::vector& regions) { + REQUIRE_MAIN_THREAD(); + // TODO(cef): Implement support for draggable regions on this platform. +} + void RootWindowGtk::NotifyDestroyedIfDone() { // Notify once both the window and the browser have been destroyed. if (window_destroyed_ && browser_destroyed_) diff --git a/tests/cefclient/browser/root_window_gtk.h b/tests/cefclient/browser/root_window_gtk.h index 015ce3212..b759e7200 100644 --- a/tests/cefclient/browser/root_window_gtk.h +++ b/tests/cefclient/browser/root_window_gtk.h @@ -58,6 +58,8 @@ class RootWindowGtk : public RootWindow, void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE; + void OnSetDraggableRegions( + const std::vector& regions) OVERRIDE; void NotifyDestroyedIfDone(); diff --git a/tests/cefclient/browser/root_window_mac.h b/tests/cefclient/browser/root_window_mac.h index f7048de85..a3289c9af 100644 --- a/tests/cefclient/browser/root_window_mac.h +++ b/tests/cefclient/browser/root_window_mac.h @@ -64,6 +64,8 @@ class RootWindowMac : public RootWindow, void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE; + void OnSetDraggableRegions( + const std::vector& regions) OVERRIDE; void NotifyDestroyedIfDone(); diff --git a/tests/cefclient/browser/root_window_mac.mm b/tests/cefclient/browser/root_window_mac.mm index f642183db..ade358501 100644 --- a/tests/cefclient/browser/root_window_mac.mm +++ b/tests/cefclient/browser/root_window_mac.mm @@ -576,6 +576,12 @@ void RootWindowMac::OnSetAddress(const std::string& url) { } } +void RootWindowMac::OnSetDraggableRegions( + const std::vector& regions) { + REQUIRE_MAIN_THREAD(); + // TODO(cef): Implement support for draggable regions on this platform. +} + void RootWindowMac::OnSetTitle(const std::string& title) { REQUIRE_MAIN_THREAD(); diff --git a/tests/cefclient/browser/root_window_win.cc b/tests/cefclient/browser/root_window_win.cc index 25f83c7d5..2f8bcfa21 100644 --- a/tests/cefclient/browser/root_window_win.cc +++ b/tests/cefclient/browser/root_window_win.cc @@ -5,6 +5,7 @@ #include "cefclient/browser/root_window_win.h" #include "include/base/cef_bind.h" +#include "include/base/cef_build.h" #include "include/cef_app.h" #include "cefclient/browser/browser_window_osr_win.h" #include "cefclient/browser/browser_window_std_win.h" @@ -51,6 +52,7 @@ RootWindowWin::RootWindowWin() start_rect_(), initialized_(false), hwnd_(NULL), + draggable_region_(NULL), back_hwnd_(NULL), forward_hwnd_(NULL), reload_hwnd_(NULL), @@ -66,11 +68,16 @@ RootWindowWin::RootWindowWin() window_destroyed_(false), browser_destroyed_(false) { find_buff_[0] = 0; + + // Create a HRGN representing the draggable window area. + draggable_region_ = ::CreateRectRgn(0, 0, 0, 0); } RootWindowWin::~RootWindowWin() { REQUIRE_MAIN_THREAD(); + ::DeleteObject(draggable_region_); + // The window and browser should already have been destroyed. DCHECK(window_destroyed_); DCHECK(browser_destroyed_); @@ -513,6 +520,21 @@ LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd, UINT message, return 0; // Cancel the close. break; + case WM_NCHITTEST: { + LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam); + if (hit == HTCLIENT) { + POINTS points = MAKEPOINTS(lParam); + POINT point = { points.x, points.y }; + ::ScreenToClient(hWnd, &point); + if (::PtInRegion(self->draggable_region_, point.x, point.y)) { + // If cursor is inside a draggable region return HTCAPTION to allow + // dragging. + return HTCAPTION; + } + } + return hit; + } + case WM_NCDESTROY: // Clear the reference to |self|. SetUserDataPtr(hWnd, NULL); @@ -764,6 +786,111 @@ void RootWindowWin::OnSetLoadingState(bool isLoading, } } +namespace { + +LPCWSTR kParentWndProc = L"CefParentWndProc"; +LPCWSTR kDraggableRegion = L"CefDraggableRegion"; + +LRESULT CALLBACK SubclassedWindowProc( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + WNDPROC hParentWndProc = reinterpret_cast( + ::GetPropW(hWnd, kParentWndProc)); + HRGN hRegion = reinterpret_cast( + ::GetPropW(hWnd, kDraggableRegion)); + + if (message == WM_NCHITTEST) { + LRESULT hit = CallWindowProc( + hParentWndProc, hWnd, message, wParam, lParam); + if (hit == HTCLIENT) { + POINTS points = MAKEPOINTS(lParam); + POINT point = { points.x, points.y }; + ::ScreenToClient(hWnd, &point); + if (::PtInRegion(hRegion, point.x, point.y)) { + // Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT + // in child windows. + return HTTRANSPARENT; + } + } + return hit; + } + + return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam); +} + +void SubclassWindow(HWND hWnd, HRGN hRegion) { + HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc); + if (hParentWndProc) { + return; + } + + SetLastError(0); + LONG_PTR hOldWndProc = SetWindowLongPtr( + hWnd, GWLP_WNDPROC, reinterpret_cast(SubclassedWindowProc)); + if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) { + return; + } + + ::SetPropW(hWnd, kParentWndProc, reinterpret_cast(hOldWndProc)); + ::SetPropW(hWnd, kDraggableRegion, reinterpret_cast(hRegion)); +} + +void UnSubclassWindow(HWND hWnd) { + LONG_PTR hParentWndProc = reinterpret_cast( + ::GetPropW(hWnd, kParentWndProc)); + if (hParentWndProc) { + LONG_PTR hPreviousWndProc = + SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc); + ALLOW_UNUSED_LOCAL(hPreviousWndProc); + DCHECK_EQ(hPreviousWndProc, + reinterpret_cast(SubclassedWindowProc)); + } + + ::RemovePropW(hWnd, kParentWndProc); + ::RemovePropW(hWnd, kDraggableRegion); +} + +BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) { + SubclassWindow(hwnd, reinterpret_cast(lParam)); + return TRUE; +} + +BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) { + UnSubclassWindow(hwnd); + return TRUE; +} + +} // namespace + +void RootWindowWin::OnSetDraggableRegions( + const std::vector& regions) { + REQUIRE_MAIN_THREAD(); + + // Reset draggable region. + ::SetRectRgn(draggable_region_, 0, 0, 0, 0); + + // Determine new draggable region. + std::vector::const_iterator it = regions.begin(); + for (;it != regions.end(); ++it) { + HRGN region = ::CreateRectRgn( + it->bounds.x, it->bounds.y, + it->bounds.x + it->bounds.width, + it->bounds.y + it->bounds.height); + ::CombineRgn( + draggable_region_, draggable_region_, region, + it->draggable ? RGN_OR : RGN_DIFF); + ::DeleteObject(region); + } + + // Subclass child window procedures in order to do hit-testing. + // This will be a no-op, if it is already subclassed. + if (hwnd_) { + WNDENUMPROC proc = !regions.empty() ? + SubclassWindowsProc : UnSubclassWindowsProc; + ::EnumChildWindows( + hwnd_, proc, reinterpret_cast(draggable_region_)); + } +} + void RootWindowWin::NotifyDestroyedIfDone() { // Notify once both the window and the browser have been destroyed. if (window_destroyed_ && browser_destroyed_) diff --git a/tests/cefclient/browser/root_window_win.h b/tests/cefclient/browser/root_window_win.h index 3584f24b3..a1e4ffcb8 100644 --- a/tests/cefclient/browser/root_window_win.h +++ b/tests/cefclient/browser/root_window_win.h @@ -90,6 +90,8 @@ class RootWindowWin : public RootWindow, void OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) OVERRIDE; + void OnSetDraggableRegions( + const std::vector& regions) OVERRIDE; void NotifyDestroyedIfDone(); @@ -106,6 +108,9 @@ class RootWindowWin : public RootWindow, // Main window. HWND hwnd_; + // Draggable region. + HRGN draggable_region_; + // Buttons. HWND back_hwnd_; HWND forward_hwnd_; @@ -115,7 +120,7 @@ class RootWindowWin : public RootWindow, // URL text field. HWND edit_hwnd_; WNDPROC edit_wndproc_old_; - + // Find dialog. HWND find_hwnd_; UINT find_message_id_; diff --git a/tests/unittests/draggable_regions_unittest.cc b/tests/unittests/draggable_regions_unittest.cc new file mode 100644 index 000000000..6e217cf7a --- /dev/null +++ b/tests/unittests/draggable_regions_unittest.cc @@ -0,0 +1,171 @@ +// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +// Include this first to avoid type conflicts with CEF headers. +#include "tests/unittests/chromium_includes.h" + +#include "include/wrapper/cef_closure_task.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "tests/unittests/test_handler.h" + +namespace { + +const char kTestURLWithRegions[] = "http://test.com/regions"; +const char kTestHTMLWithRegions[] = + "" + " " + "
" + "
" + "
" + "
" + " " + ""; + +const char kTestURLWithoutRegions[] = "http://test.com/no-regions"; +const char kTestHTMLWithoutRegions[] = + "Hello World!"; + +const char kTestURLWithChangingRegions[] = "http://test.com/changing-regions"; +const char kTestHTMLWithChangingRegions[] = + "" + " " + "
" + "
" + "
" + "
" + " " + " " + ""; + +class DraggableRegionsTestHandler : public TestHandler { + public: + DraggableRegionsTestHandler() + : step_(kStepWithRegions) {} + + void RunTest() override { + // Add HTML documents with and without draggable regions. + AddResource(kTestURLWithRegions, kTestHTMLWithRegions, "text/html"); + AddResource(kTestURLWithoutRegions, kTestHTMLWithoutRegions, "text/html"); + AddResource(kTestURLWithChangingRegions, kTestHTMLWithChangingRegions, + "text/html"); + + // Create the browser + CreateBrowser(kTestURLWithRegions); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + void OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) override { + EXPECT_TRUE(CefCurrentlyOn(TID_UI)); + EXPECT_TRUE(browser->IsSame(GetBrowser())); + + did_call_on_draggable_regions_changed_.yes(); + + switch (step_) { + case kStepWithRegions: + case kStepWithChangingRegions1: + EXPECT_EQ(2U, regions.size()); + EXPECT_EQ(50, regions[0].bounds.x); + EXPECT_EQ(50, regions[0].bounds.y); + EXPECT_EQ(200, regions[0].bounds.width); + EXPECT_EQ(200, regions[0].bounds.height); + EXPECT_EQ(1, regions[0].draggable); + EXPECT_EQ(125, regions[1].bounds.x); + EXPECT_EQ(125, regions[1].bounds.y); + EXPECT_EQ(50, regions[1].bounds.width); + EXPECT_EQ(50, regions[1].bounds.height); + EXPECT_EQ(0, regions[1].draggable); + break; + case kStepWithChangingRegions2: + EXPECT_EQ(2U, regions.size()); + EXPECT_EQ(0, regions[0].bounds.x); + EXPECT_EQ(0, regions[0].bounds.y); + EXPECT_EQ(200, regions[0].bounds.width); + EXPECT_EQ(200, regions[0].bounds.height); + EXPECT_EQ(1, regions[0].draggable); + EXPECT_EQ(75, regions[1].bounds.x); + EXPECT_EQ(75, regions[1].bounds.y); + EXPECT_EQ(50, regions[1].bounds.width); + EXPECT_EQ(50, regions[1].bounds.height); + EXPECT_EQ(0, regions[1].draggable); + break; + case kStepWithoutRegions: + // Should not be reached. + EXPECT_TRUE(false); + break; + } + + NextTest(browser); + } + + void DestroyTest() override { + EXPECT_EQ(false, did_call_on_draggable_regions_changed_); + + TestHandler::DestroyTest(); + } + + private: + void NextTest(CefRefPtr browser) { + CefRefPtr frame(browser->GetMainFrame()); + + did_call_on_draggable_regions_changed_.reset(); + + switch (step_) { + case kStepWithRegions: + step_ = kStepWithChangingRegions1; + frame->LoadURL(kTestURLWithChangingRegions); + break; + case kStepWithChangingRegions1: + step_ = kStepWithChangingRegions2; + break; + case kStepWithChangingRegions2: + step_ = kStepWithoutRegions; + frame->LoadURL(kTestURLWithoutRegions); + // Needed because this test doesn't call OnDraggableRegionsChanged. + CefPostDelayedTask(TID_UI, + base::Bind(&DraggableRegionsTestHandler::DestroyTest, this), 500); + break; + case kStepWithoutRegions: { + // Should not be reached. + EXPECT_TRUE(false); + break; + } + } + } + + enum Step { + kStepWithRegions, + kStepWithChangingRegions1, + kStepWithChangingRegions2, + kStepWithoutRegions, + } step_; + + TrackCallback did_call_on_draggable_regions_changed_; +}; + +} // namespace + +// Verify that draggable regions work. +TEST(DraggableRegionsTest, DraggableRegions) { + CefRefPtr handler = + new DraggableRegionsTestHandler(); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} diff --git a/tests/unittests/os_rendering_unittest.cc b/tests/unittests/os_rendering_unittest.cc index 6bca2ca03..4cc2409ad 100644 --- a/tests/unittests/os_rendering_unittest.cc +++ b/tests/unittests/os_rendering_unittest.cc @@ -805,7 +805,7 @@ class OSRTestHandler : public RoutingTestHandler, bool StartDragging(CefRefPtr browser, CefRefPtr drag_data, - DragOperationsMask allowed_ops, + CefRenderHandler::DragOperationsMask allowed_ops, int x, int y) override { if (test_type_ == OSR_TEST_DRAG_DROP_START_DRAGGING && started()) { DestroySucceededTestSoon(); diff --git a/tests/unittests/test_handler.h b/tests/unittests/test_handler.h index ddc452cc4..2b51706d5 100644 --- a/tests/unittests/test_handler.h +++ b/tests/unittests/test_handler.h @@ -37,6 +37,7 @@ class TestHandler : public CefClient, public CefDialogHandler, public CefDisplayHandler, public CefDownloadHandler, + public CefDragHandler, public CefGeolocationHandler, public CefJSDialogHandler, public CefLifeSpanHandler, @@ -139,6 +140,9 @@ class TestHandler : public CefClient, CefRefPtr GetDownloadHandler() override { return this; } + CefRefPtr GetDragHandler() override { + return this; + } CefRefPtr GetGeolocationHandler() override { return this; } @@ -162,6 +166,11 @@ class TestHandler : public CefClient, const CefString& suggested_name, CefRefPtr callback) override {} + // CefDragHandler methods + void OnDraggableRegionsChanged( + CefRefPtr browser, + const std::vector& regions) override {} + // CefLifeSpanHandler methods void OnAfterCreated(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; diff --git a/tools/cef_parser.py b/tools/cef_parser.py index c1b2ff47e..3be7bb97f 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -403,6 +403,7 @@ _simpletypes = { 'CefPoint' : ['cef_point_t', 'CefPoint()'], 'CefRect' : ['cef_rect_t', 'CefRect()'], 'CefSize' : ['cef_size_t', 'CefSize()'], + 'CefDraggableRegion' : ['cef_draggable_region_t', 'CefDraggableRegion()'], 'CefPageRange' : ['cef_page_range_t', 'CefPageRange()'], 'CefThreadId' : ['cef_thread_id_t', 'TID_UI'], 'CefTime' : ['cef_time_t', 'CefTime()'],