diff --git a/README.md b/README.md index a8721b78..2713eeb3 100644 --- a/README.md +++ b/README.md @@ -456,6 +456,8 @@ Additional information for v31.2 release: * [FlushStore](api/CookieManager.md#flushstore) * [CookieVisitor (interface)](api/CookieVisitor.md#cookievisitor-interface) * [Visit](api/CookieVisitor.md#visit) +* [DialogHandler (interface)](api/DialogHandler.md#dialoghandler-interface) + * [OnFileDialog](api/DialogHandler.md#onfiledialog) * [DisplayHandler (interface)](api/DisplayHandler.md#displayhandler-interface) * [OnAddressChange](api/DisplayHandler.md#onaddresschange) * [OnTitleChange](api/DisplayHandler.md#ontitlechange) @@ -469,15 +471,26 @@ Additional information for v31.2 release: * [IsProcessDpiAware](api/DpiAware.md#isprocessdpiaware) * [SetProcessDpiAware](api/DpiAware.md#setprocessdpiaware) * [DragData (object)](api/DragData.md#dragdata-object) + * [AddFile](api/DragData.md#addfile) * [IsLink](api/DragData.md#islink) + * [IsFile](api/DragData.md#isfile) * [IsFragment](api/DragData.md#isfragment) * [GetLinkUrl](api/DragData.md#getlinkurl) * [GetLinkTitle](api/DragData.md#getlinktitle) + * [GetFileName](api/DragData.md#getfilename) + * [GetFileNames](api/DragData.md#getfilenames) * [GetFragmentText](api/DragData.md#getfragmenttext) * [GetFragmentHtml](api/DragData.md#getfragmenthtml) * [GetImage](api/DragData.md#getimage) * [GetImageHotspot](api/DragData.md#getimagehotspot) * [HasImage](api/DragData.md#hasimage) + * [SetFragmentText](api/DragData.md#setfragmenttext) + * [SetFragmentHtml](api/DragData.md#setfragmenthtml) +* [DragHandler (interface)](api/DragHandler.md#draghandler-interface) + * [OnDragEnter](api/DragHandler.md#ondragenter) +* [FileDialogCallback (object)](api/FileDialogCallback.md#filedialogcallback-object) + * [Cancel](api/FileDialogCallback.md#cancel) + * [Continue](api/FileDialogCallback.md#continue) * [FocusHandler (interface)](api/FocusHandler.md#focushandler-interface) * [OnTakeFocus](api/FocusHandler.md#ontakefocus) * [OnSetFocus](api/FocusHandler.md#onsetfocus) diff --git a/api/API-index.md b/api/API-index.md index e998df93..f5bf399b 100644 --- a/api/API-index.md +++ b/api/API-index.md @@ -198,6 +198,8 @@ * [FlushStore](CookieManager.md#flushstore) * [CookieVisitor (interface)](CookieVisitor.md#cookievisitor-interface) * [Visit](CookieVisitor.md#visit) +* [DialogHandler (interface)](DialogHandler.md#dialoghandler-interface) + * [OnFileDialog](DialogHandler.md#onfiledialog) * [DisplayHandler (interface)](DisplayHandler.md#displayhandler-interface) * [OnAddressChange](DisplayHandler.md#onaddresschange) * [OnTitleChange](DisplayHandler.md#ontitlechange) @@ -211,15 +213,26 @@ * [IsProcessDpiAware](DpiAware.md#isprocessdpiaware) * [SetProcessDpiAware](DpiAware.md#setprocessdpiaware) * [DragData (object)](DragData.md#dragdata-object) + * [AddFile](DragData.md#addfile) * [IsLink](DragData.md#islink) + * [IsFile](DragData.md#isfile) * [IsFragment](DragData.md#isfragment) * [GetLinkUrl](DragData.md#getlinkurl) * [GetLinkTitle](DragData.md#getlinktitle) + * [GetFileName](DragData.md#getfilename) + * [GetFileNames](DragData.md#getfilenames) * [GetFragmentText](DragData.md#getfragmenttext) * [GetFragmentHtml](DragData.md#getfragmenthtml) * [GetImage](DragData.md#getimage) * [GetImageHotspot](DragData.md#getimagehotspot) * [HasImage](DragData.md#hasimage) + * [SetFragmentText](DragData.md#setfragmenttext) + * [SetFragmentHtml](DragData.md#setfragmenthtml) +* [DragHandler (interface)](DragHandler.md#draghandler-interface) + * [OnDragEnter](DragHandler.md#ondragenter) +* [FileDialogCallback (object)](FileDialogCallback.md#filedialogcallback-object) + * [Cancel](FileDialogCallback.md#cancel) + * [Continue](FileDialogCallback.md#continue) * [FocusHandler (interface)](FocusHandler.md#focushandler-interface) * [OnTakeFocus](FocusHandler.md#ontakefocus) * [OnSetFocus](FocusHandler.md#onsetfocus) diff --git a/api/DialogHandler.md b/api/DialogHandler.md new file mode 100644 index 00000000..0c47d27c --- /dev/null +++ b/api/DialogHandler.md @@ -0,0 +1,45 @@ +[API categories](API-categories.md) | [API index](API-index.md) + + +# DialogHandler (interface) + +Description from upstream CEF: +> Implement this interface to handle dialog events. +> The methods of this class will be called on the browser process UI thread. + + +Table of contents: +* [Callbacks](#callbacks) + * [OnFileDialog](#onfiledialog) + +## Callbacks + + +### OnFileDialog + +| Parameter | Type | +| --- | --- | +| browser | [Browser](Browser.md) | +| mode | int | +| title | string | +| default_file_path | string | +| accept_filters | list | +| selected_accept_filter | int | +| file_dialog_callback | [FileDialogCallback](FileDialogCallback.md)| +| __Return__ | bool | + +Description from upstream CEF: +> Called to run a file chooser dialog. +> |mode| represents the type of dialog to display. +> |title| to the title to be used for the dialog and may be empty to show the default title ("Open" or "Save" depending +> on the mode). |default_file_path| is the path with optional directory and/or file name component that should +> be initially selected in the dialog. |accept_filters| are used to restrict the selectable file types and +> may anycombination of (a) valid lower-cased MIME types (e.g. "text/*" or "image/*"), (b) individual file +> extensions (e.g. ".txt" or ".png"), or (c) combined description and file extension delimited using "|" and ";" +> (e.g. "Image Types|.png;.gif;.jpg"). +> |selected_accept_filter| is the 0-based index of the filter that should be selected by default. To display a custom +> dialog return true and execute +> |callback| either inline or at a later time. To display the default dialog return false. + + + diff --git a/api/DragData.md b/api/DragData.md index cc7dee86..65969d29 100644 --- a/api/DragData.md +++ b/api/DragData.md @@ -6,19 +6,34 @@ Table of contents: * [Methods](#methods) + * [AddFile](#addfile) * [IsLink](#islink) + * [IsFile](#isfile) * [IsFragment](#isfragment) * [GetLinkUrl](#getlinkurl) * [GetLinkTitle](#getlinktitle) + * [GetFileName](#getfilename) + * [GetFileNames](#getfilenames) * [GetFragmentText](#getfragmenttext) * [GetFragmentHtml](#getfragmenthtml) * [GetImage](#getimage) * [GetImageHotspot](#getimagehotspot) * [HasImage](#hasimage) - + * [SetFragmentText](#setfragmenttext) + * [SetFragmentHtml](#setfragmenthtml) ## Methods +### AddFile + +| | | +| --- | --- | +| path | string | +| display_name | string | +| __Return__ | void | + +Add a file that is being dragged into the webview. + ### IsLink @@ -29,6 +44,15 @@ Table of contents: Returns true if the drag data is a link. +### IsFile + +| | | +| --- | --- | +| __Return__ | bool | + +Returns true if the drag data is a file. + + ### IsFragment | | | @@ -57,6 +81,26 @@ Return the link URL that is being dragged. Return the title associated with the link being dragged. +### GetFileName + +| | | +| --- | --- | +| __Return__ | string | + + +Return the name of the file being dragged out of the browser window. + + +### GetFileNames + +| | | +| --- | --- | +| __Return__ | list | + + +Return the list of file names that are being dragged into the browser window. + + ### GetFragmentText | | | @@ -108,3 +152,20 @@ Linux-only currently (#251). Whether image representation of drag data is available. +### SetFragmentText + +| | | +| --- | --- | +| text | string | +| __Return__ | void | + +Set the plain text fragment that is being dragged. + +### SetFragmentHtml + +| | | +| --- | --- | +| html | string | +| __Return__ | void | + +Set the text/html fragment that is being dragged. diff --git a/api/DragHandler.md b/api/DragHandler.md new file mode 100644 index 00000000..97550f99 --- /dev/null +++ b/api/DragHandler.md @@ -0,0 +1,26 @@ +[API categories](API-categories.md) | [API index](API-index.md) + + +# DragHandler (interface) + +Implement this interface to handle events related to dragging. The methods of this class will be called on the UI thread. + + +Table of contents: +* [Callbacks](#callbacks) + * [OnDragEnter](#ondragenter) + +## Callbacks + + +### OnDragEnter + +| Parameter | Type | +| --- | --- | +| browser | [Browser](Browser.md) | +| dragData | [DragData](DragData.md) | +| mask | int | +| __Return__ | bool | + +Description from upstream CEF: +> Called when an external drag event enters the browser window. diff --git a/api/FileDialogCallback.md b/api/FileDialogCallback.md new file mode 100644 index 00000000..e6fe78c7 --- /dev/null +++ b/api/FileDialogCallback.md @@ -0,0 +1,42 @@ +[API categories](API-categories.md) | [API index](API-index.md) + + +# FileDialogCallback (object) + +Description from upstream CEF: +> Callback interface for asynchronous continuation of file dialog requests. + + +Table of contents: +* [Methods](#methods) + * [Cancel](#cancel) + * [Continue](#continue) + + +## Methods + + +### Cancel + +| Parameter | Type | +| --- | --- | +| __Return__ | void | + +Description from upstream CEF: +> Cancel the file selection. + + +### Continue + +| Parameter | Type | +| --- | --- | +| selected_accept_filter | int | +| file_paths | list | +| __Return__ | void | + +Description from upstream CEF: +> Continue the file selection. +> |selected_accept_filter| should be the 0-based index of the value selected from the accept filters array passed to +> CefDialogHandler::OnFileDialog. |file_paths| should be a single value or a list of values +> depending on the dialog mode. An empty +> |file_paths| value is treated the same as calling Cancel(). \ No newline at end of file diff --git a/src/browser.pyx b/src/browser.pyx index 406a01c9..f17994d1 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -237,6 +237,12 @@ cdef class PyBrowser: self.allowedClientCallbacks += ["OnTakeFocus", "OnSetFocus", "OnGotFocus"] + # DragHandler + self.allowedClientCallbacks += ["OnDragEnter"] + + # DialogHanlder + self.allowedClientCallbacks += ["OnFileDialog"] + if name not in self.allowedClientCallbacks: raise Exception("Browser.SetClientCallback() failed: unknown " "callback: %s" % name) diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 7b5755eb..46fefce3 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -431,6 +431,7 @@ from cef_command_line cimport * from cef_request_context cimport * from cef_request_context_handler cimport * from request_context_handler cimport * +from cef_dialog_handler cimport * from cef_jsdialog_handler cimport * from cef_path_util cimport * from cef_drag_data cimport * @@ -517,6 +518,8 @@ IF UNAME_SYSNAME == "Linux": include "handlers/browser_process_handler.pyx" include "handlers/display_handler.pyx" include "handlers/focus_handler.pyx" +include "handlers/drag_handler.pyx" +include "handlers/dialog_handler.pyx" include "handlers/javascript_dialog_handler.pyx" include "handlers/keyboard_handler.pyx" include "handlers/lifespan_handler.pyx" @@ -527,6 +530,7 @@ include "handlers/request_handler.pyx" include "handlers/v8context_handler.pyx" include "handlers/v8function_handler.pyx" + # ----------------------------------------------------------------------------- # Utility functions to provide settings to the C++ browser process code. diff --git a/src/client_handler/client_handler.h b/src/client_handler/client_handler.h index 3e9e3917..4d3658c2 100644 --- a/src/client_handler/client_handler.h +++ b/src/client_handler/client_handler.h @@ -21,6 +21,7 @@ #include "load_handler.h" #include "render_handler.h" #include "request_handler.h" +#include "drag_handler.h" class ClientHandler : public CefClient, @@ -34,7 +35,8 @@ class ClientHandler : public CefClient, public LifespanHandler, public LoadHandler, public RenderHandler, - public RequestHandler + public RequestHandler, + public DragHandler { public: ClientHandler(){} @@ -44,11 +46,9 @@ class ClientHandler : public CefClient, return this; } -#if defined(OS_LINUX) CefRefPtr GetDialogHandler() override { return this; } -#endif CefRefPtr GetDisplayHandler() override { return this; @@ -86,6 +86,10 @@ class ClientHandler : public CefClient, return this; } + CefRefPtr GetDragHandler() override { + return this; + } + bool OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message diff --git a/src/client_handler/dialog_handler.cpp b/src/client_handler/dialog_handler.cpp index ab90de9b..f271ef29 100644 --- a/src/client_handler/dialog_handler.cpp +++ b/src/client_handler/dialog_handler.cpp @@ -4,7 +4,6 @@ #include "dialog_handler.h" - DialogHandler::DialogHandler() { #if defined(OS_LINUX) @@ -22,6 +21,17 @@ bool DialogHandler::OnFileDialog(CefRefPtr browser, int selected_accept_filter, CefRefPtr callback) { + bool result; + result = DialogHandler_OnFileDialog(browser, + mode, + title, + default_file_path, + accept_filters, + selected_accept_filter, + callback); + if(result){ + return result; + } #if defined(OS_LINUX) return dialog_handler_->OnFileDialog(browser, mode, @@ -33,5 +43,4 @@ bool DialogHandler::OnFileDialog(CefRefPtr browser, #else return false; #endif - } diff --git a/src/client_handler/drag_handler.cpp b/src/client_handler/drag_handler.cpp new file mode 100644 index 00000000..4082164a --- /dev/null +++ b/src/client_handler/drag_handler.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2017 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +#include "drag_handler.h" + + +bool DragHandler::OnDragEnter(CefRefPtr browser, + CefRefPtr dragData, + cef_drag_operations_mask_t mask) +{ + REQUIRE_UI_THREAD(); + return DragHandler_OnDragEnter(browser, dragData, mask); +} diff --git a/src/client_handler/drag_handler.h b/src/client_handler/drag_handler.h new file mode 100644 index 00000000..cb107327 --- /dev/null +++ b/src/client_handler/drag_handler.h @@ -0,0 +1,23 @@ +// Copyright (c) 2017 CEF Python, see the Authors file. +// All rights reserved. Licensed under BSD 3-clause license. +// Project website: https://github.com/cztomczak/cefpython + +#include "common/cefpython_public_api.h" +#include "include/cef_drag_handler.h" + + +class DragHandler : public CefDragHandler +{ +public: + DragHandler(){} + virtual ~DragHandler(){} + + bool OnDragEnter(CefRefPtr browser, + CefRefPtr dragData, + cef_drag_operations_mask_t mask) override; + + + +private: + IMPLEMENT_REFCOUNTING(DragHandler); +}; diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 35f85002..10ec798a 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,3 @@ # This file was generated by setup.py -DEF UNAME_SYSNAME = "Linux" +DEF UNAME_SYSNAME = "Windows" DEF PY_MAJOR_VERSION = 2 diff --git a/src/drag_data.pyx b/src/drag_data.pyx index ddb7d9f9..0d163505 100644 --- a/src/drag_data.pyx +++ b/src/drag_data.pyx @@ -18,6 +18,24 @@ cdef class DragData: self.cef_drag_data.get().SetFragmentHtml(PyToCefStringValue("none")) self.cef_drag_data.get().SetFragmentBaseURL(PyToCefStringValue("")) + cpdef py_bool IsFile(self): + return self.cef_drag_data.get().IsFile() + + cpdef py_string GetFileName(self): + return CefToPyString(self.cef_drag_data.get().GetFileName()) + + cpdef list GetFileNames(self): + cdef cpp_vector[CefString] cefNames + self.cef_drag_data.get().GetFileNames(cefNames) + cdef list names = [] + cdef cpp_vector[CefString].iterator iterator = cefNames.begin() + cdef CefString cefString + while iterator != cefNames.end(): + cefString = deref(iterator) + names.append(CefToPyString(cefString)) + preinc(iterator) + return names + cpdef py_bool IsLink(self): return self.cef_drag_data.get().IsLink() @@ -36,6 +54,26 @@ cdef class DragData: cpdef py_string GetFragmentHtml(self): return CefToPyString(self.cef_drag_data.get().GetFragmentHtml()) + cpdef py_void SetFragmentText(self, text): + cdef CefString cefText + PyToCefString(text, cefText) + self.cef_drag_data.get().SetFragmentText(cefText) + + cpdef py_void SetFragmentHtml(self, html): + cdef CefString cefHtml + PyToCefString(html, cefHtml) + self.cef_drag_data.get().SetFragmentHtml(cefHtml) + + cpdef py_void AddFile(self, path, display_name): + cdef CefString cefPath + cdef CefString cefDisplayName + PyToCefString(path, cefPath) + PyToCefString(display_name, cefDisplayName) + self.cef_drag_data.get().AddFile(cefPath, cefDisplayName) + + cpdef py_void ResetFileContents(self): + self.cef_drag_data.get().ResetFileContents() + IF UNAME_SYSNAME == "Linux": cpdef PyImage GetImage(self): diff --git a/src/extern/cef/cef_dialog_handler.pxd b/src/extern/cef/cef_dialog_handler.pxd new file mode 100644 index 00000000..bc86d553 --- /dev/null +++ b/src/extern/cef/cef_dialog_handler.pxd @@ -0,0 +1,13 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +from cef_string cimport CefString +from libcpp.vector cimport vector as cpp_vector + +cdef extern from "include/cef_dialog_handler.h": + + cdef cppclass CefFileDialogCallback: + void Continue(int selected_accept_filter, + const cpp_vector[CefString]& file_paths) + void Cancel() diff --git a/src/extern/cef/cef_drag_data.pxd b/src/extern/cef/cef_drag_data.pxd index 88fd3fa5..c254004f 100644 --- a/src/extern/cef/cef_drag_data.pxd +++ b/src/extern/cef/cef_drag_data.pxd @@ -8,14 +8,21 @@ from cef_ptr cimport CefRefPtr from cef_image cimport CefImage from cef_types cimport CefPoint +from libcpp.vector cimport vector as cpp_vector + cdef extern from "include/cef_drag_data.h": cdef cppclass CefDragData: + void AddFile(const CefString& path, const CefString& display_name) cpp_bool IsLink() + cpp_bool IsFile() cpp_bool IsFragment() CefString GetLinkURL() CefString GetLinkTitle() + CefString GetFileName() + cpp_bool GetFileNames(cpp_vector[CefString]& names) CefString GetFragmentText() CefString GetFragmentHtml() + void ResetFileContents() void SetFragmentText(const CefString& text) void SetFragmentHtml(const CefString& html) void SetFragmentBaseURL(const CefString& base_url) @@ -23,5 +30,4 @@ cdef extern from "include/cef_drag_data.h": CefRefPtr[CefImage] GetImage() CefPoint GetImageHotspot() - cdef CefRefPtr[CefDragData] CefDragData_Create "CefDragData::Create"() diff --git a/src/handlers/dialog_handler.pyx b/src/handlers/dialog_handler.pyx new file mode 100644 index 00000000..3c4700bc --- /dev/null +++ b/src/handlers/dialog_handler.pyx @@ -0,0 +1,48 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +include "../cefpython.pyx" +include "../browser.pyx" + + +cdef public cpp_bool DialogHandler_OnFileDialog( + CefRefPtr[CefBrowser] cef_browser, + uint32 mode, + const CefString& cefTitle, + const CefString& cefDefaultFilePath, + const cpp_vector[CefString]& cefAcceptFilters, + int selected_accept_filter, + CefRefPtr[CefFileDialogCallback] cefFileDialogCallback + ) except * with gil: + + cdef PyBrowser pyBrowser + cdef py_bool returnValue + cdef py_string pyTitle + cdef py_string pyDefaultFilePath + cdef list pyAcceptFilters = [] + + try: + pyBrowser = GetPyBrowser(cef_browser, "OnFileDialog") + + pyTitle = CefToPyString(cefTitle) + pyDefaultFilePath = CefToPyString(cefDefaultFilePath) + + for i in range(cefAcceptFilters.size()): + pyAcceptFilters.append(CefToPyString(cefAcceptFilters[i])) + + callback = pyBrowser.GetClientCallback("OnFileDialog") + if callback: + returnValue = callback( + browser=pyBrowser, + mode=mode, + title=pyTitle, + default_file_path=pyDefaultFilePath, + accept_filters=pyAcceptFilters, + selected_accept_filter=selected_accept_filter, + file_dialog_callback = CreatePyFileDialogCallback(cefFileDialogCallback)) + return bool(returnValue) + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) + return False diff --git a/src/handlers/drag_handler.pyx b/src/handlers/drag_handler.pyx new file mode 100644 index 00000000..04f868d8 --- /dev/null +++ b/src/handlers/drag_handler.pyx @@ -0,0 +1,46 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython + +include "../cefpython.pyx" +include "../browser.pyx" + +cdef PyFileDialogCallback CreatePyFileDialogCallback( + CefRefPtr[CefFileDialogCallback] cefCallback): + cdef PyFileDialogCallback pyCallback = PyFileDialogCallback() + pyCallback.cefCallback = cefCallback + return pyCallback + +cdef class PyFileDialogCallback: + cdef CefRefPtr[CefFileDialogCallback] cefCallback + cpdef py_void Continue(self,int selected_accept_filter,list file_paths): + cdef cpp_vector[CefString] filePaths + for f in file_paths: + filePaths.push_back(PyToCefStringValue(f)) + self.cefCallback.get().Continue(selected_accept_filter,filePaths) + + cpdef py_void Cancel(self): + self.cefCallback.get().Cancel() + +cdef public cpp_bool DragHandler_OnDragEnter( + CefRefPtr[CefBrowser] cef_browser, + CefRefPtr[CefDragData] cef_drag_data, + uint32 mask + ) except * with gil: + + cdef PyBrowser pyBrowser + cdef DragData drag_data + cdef py_bool returnValue + try: + pyBrowser = GetPyBrowser(cef_browser, "OnDragEnter") + pyDragData = DragData_Init(cef_drag_data) + callback = pyBrowser.GetClientCallback("OnDragEnter") + if callback: + returnValue = callback(browser=pyBrowser, + dragData=pyDragData, + mask=mask) + return bool(returnValue) + except: + (exc_type, exc_value, exc_trace) = sys.exc_info() + sys.excepthook(exc_type, exc_value, exc_trace) + return False diff --git a/src/version/cef_version_win.h b/src/version/cef_version_win.h index 526e4630..da4e43e9 100644 --- a/src/version/cef_version_win.h +++ b/src/version/cef_version_win.h @@ -99,4 +99,4 @@ CEF_EXPORT const char* cef_api_hash(int entry); #endif // APSTUDIO_HIDDEN_SYMBOLS -#endif // CEF_INCLUDE_CEF_VERSION_H_ +#endif // CEF_INCLUDE_CEF_VERSION_H_ \ No newline at end of file diff --git a/unittests/_common.py b/unittests/_common.py new file mode 100644 index 00000000..dda6abc7 --- /dev/null +++ b/unittests/_common.py @@ -0,0 +1,50 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython +"""Common function for unittests.""" +import base64 +import sys,time +from cefpython3 import cefpython as cef + +g_subtests_ran = 0 +def subtest_message(message): + global g_subtests_ran + g_subtests_ran += 1 + print(str(g_subtests_ran) + ". " + message) + sys.stdout.flush() + +def display_number_of_unittest(message): + print("\nRan " + str(g_subtests_ran) + " " + message) + +def html_to_data_uri(html): + html = html.encode("utf-8", "replace") + b64 = base64.b64encode(html).decode("utf-8", "replace") + ret = "data:text/html;base64,{data}".format(data=b64) + return ret + +def cef_waiting(loop_range): + for i in range(loop_range): + cef.MessageLoopWork() + time.sleep(0.01) + +def automatic_check_handlers(test_case,handlers=[]): + # Automatic check of asserts in handlers. + for obj in handlers: + test_for_True = False # Test whether asserts are working correctly + for key, value in obj.__dict__.items(): + if key == "test_for_True": + test_for_True = True + continue + if "_True" in key: + test_case.assertTrue(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_True", "") + + " ok") + elif "_False" in key: + test_case.assertFalse(value, "Check assert: " + + obj.__class__.__name__ + "." + key) + subtest_message(obj.__class__.__name__ + "." + + key.replace("_False", "") + + " ok") + test_case.assertTrue(test_for_True) \ No newline at end of file diff --git a/unittests/main_test.py b/unittests/main_test.py index d7a2fbf9..b03546ec 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -7,10 +7,10 @@ import unittest # noinspection PyUnresolvedReferences import _test_runner +from _common import subtest_message, display_number_of_unittest, html_to_data_uri, cef_waiting +from _common import automatic_check_handlers from os.path import basename from cefpython3 import cefpython as cef -import time -import base64 import sys # To show the window for an extended period of time increase this number. @@ -76,28 +76,23 @@ py_callback("String sent from Javascript"); print("py_callback() ok"); }); + + // Test receiving a file from OnFileDialog. + document.getElementById('selectFile').addEventListener('change', function(e){ + print('Received file "'+this.files[0].name + '" OK') + }, true); }; +

Main test

""" -g_datauri = "data:text/html;base64,"+base64.b64encode(g_datauri_data.encode( - "utf-8", "replace")).decode("utf-8", "replace") - -g_subtests_ran = 0 - - -def subtest_message(message): - global g_subtests_ran - g_subtests_ran += 1 - print(str(g_subtests_ran) + ". " + message) - sys.stdout.flush() - +g_datauri = html_to_data_uri(g_datauri_data) class MainTest_IsolatedTest(unittest.TestCase): @@ -132,8 +127,8 @@ def test_main(self): self.assertIsNotNone(browser, "Browser object") subtest_message("cef.CreateBrowserSync() ok") - # Test other handlers: LoadHandler, DisplayHandler etc. - client_handlers = [LoadHandler(self), DisplayHandler(self)] + # Test other handlers: LoadHandler, DisplayHandler, DialogHandler etc. + client_handlers = [LoadHandler(self), DisplayHandler(self), DialogHandler(self)] for handler in client_handlers: browser.SetClientHandler(handler) subtest_message("browser.SetClientHandler() ok") @@ -152,11 +147,16 @@ def test_main(self): # Run message loop for some time. # noinspection PyTypeChecker - for i in range(MESSAGE_LOOP_RANGE): - cef.MessageLoopWork() - time.sleep(0.01) + cef_waiting(MESSAGE_LOOP_RANGE) subtest_message("cef.MessageLoopWork() ok") + #Simulating file dialog click event for testing OnFileDialog handler + browser.SendMouseClickEvent(67, 20, cef.MOUSEBUTTON_LEFT, False, 1) + browser.SendMouseClickEvent(67, 20, cef.MOUSEBUTTON_LEFT, True, 1) + + # Run message loop for some time. + cef_waiting(MESSAGE_LOOP_RANGE) + # Test browser closing. Remember to clean reference. browser.CloseBrowser(True) del browser @@ -164,37 +164,17 @@ def test_main(self): # Give it some time to close before calling shutdown. # noinspection PyTypeChecker - for i in range(25): - cef.MessageLoopWork() - time.sleep(0.01) + cef_waiting(25) # Automatic check of asserts in handlers and in external - for obj in [] + client_handlers + [global_handler, external]: - test_for_True = False # Test whether asserts are working correctly - for key, value in obj.__dict__.items(): - if key == "test_for_True": - test_for_True = True - continue - if "_True" in key: - self.assertTrue(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_True", "") + - " ok") - elif "_False" in key: - self.assertFalse(value, "Check assert: " + - obj.__class__.__name__ + "." + key) - subtest_message(obj.__class__.__name__ + "." + - key.replace("_False", "") + - " ok") - self.assertTrue(test_for_True) + automatic_check_handlers(self, [] + client_handlers + [global_handler, external]) # Test shutdown of CEF cef.Shutdown() subtest_message("cef.Shutdown() ok") # Display real number of tests there were run - print("\nRan " + str(g_subtests_ran) + " sub-tests in test_main") + display_number_of_unittest("sub-tests in test_main") sys.stdout.flush() @@ -282,6 +262,21 @@ def OnConsoleMessage(self, message, **_): subtest_message(message) +class DialogHandler(object): + def __init__(self, test_case): + self.test_case = test_case + + # Asserts for True/False will be checked just before shutdown + self.test_for_True = True # Test whether asserts are working correctly + self.OnFileDialog_True = False + + def OnFileDialog(self, browser, mode, title, default_file_path ,accept_filters ,selected_accept_filter ,file_dialog_callback): + self.test_case.assertFalse(self.OnFileDialog_True) + self.OnFileDialog_True = True + file_dialog_callback.Continue(selected_accept_filter, [__file__]) + return True + + class FrameSourceVisitor(object): """Visitor for Frame.GetSource().""" diff --git a/unittests/osr_test.py b/unittests/osr_test.py new file mode 100644 index 00000000..3ecc2eeb --- /dev/null +++ b/unittests/osr_test.py @@ -0,0 +1,178 @@ +# Copyright (c) 2017 CEF Python, see the Authors file. +# All rights reserved. Licensed under BSD 3-clause license. +# Project website: https://github.com/cztomczak/cefpython +"""OSR testing of CEF Python.""" + +import unittest +import _test_runner +from _common import subtest_message, display_number_of_unittest, html_to_data_uri, cef_waiting +from _common import automatic_check_handlers +from cefpython3 import cefpython as cef +import platform +import sys,time,os + +g_datauri_data = """ + + + + + Title + + + + + + +
+ + + +""" + +g_datauri = html_to_data_uri(g_datauri_data) + + +class OSRTest_IsolatedTest(unittest.TestCase): + def test_osr(self): + sys.excepthook = cef.ExceptHook + cef.Initialize(settings={"windowless_rendering_enabled": True}) + parent_window_handle = 0 + window_info = cef.WindowInfo() + window_info.SetAsOffscreen(parent_window_handle) + browser = cef.CreateBrowserSync(window_info=window_info, + url=g_datauri) + + client_handlers = [LoadHandler(self), + DisplayHandler(self), + RenderHandler(self), + DragHandler(self)] + + for handler in client_handlers: + browser.SetClientHandler(handler) + + browser.SendFocusEvent(True) + browser.WasResized() + + # Test setting DragData. + self.subtest_dragdata() + cef_waiting(200) + + # Automatic check of asserts in handlers + automatic_check_handlers(self, [] + client_handlers) + + browser.CloseBrowser(True) + del browser + cef.Shutdown() + + def subtest_dragdata(self): + # Test setting DragData. + dragData = cef.DragData() + testString = "Testing DragData" + fileUri = __file__ + dragData.SetFragmentText(testString) + self.assertEqual(testString,dragData.GetFragmentText(),"SetFragmentText") + subtest_message("DragData.SetFragmentText() OK") + dragData.SetFragmentHtml(testString) + self.assertEqual(testString, dragData.GetFragmentHtml(), "SetFragmentHtml") + subtest_message("DragData.SetFragmentHtml() OK") + dragData.AddFile(fileUri,'testfile') + subtest_message("DragData.AddFile() OK") + self.assertIn(fileUri, dragData.GetFileNames(), "GetFileNames") + subtest_message("DragData.GetFileNames() OK") + +class DisplayHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.test_for_True = True + + def OnConsoleMessage(self, message, **_): + if "error" in message.lower() or "uncaught" in message.lower(): + raise Exception(message) + else: + subtest_message(message) + +class LoadHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.test_for_True = True + self.OnLoadEnd_True = False + + def OnLoadEnd(self,browser,frame,http_code): + self.test_case.assertFalse(self.OnLoadEnd_True) + self.OnLoadEnd_True = True + + # testing trigger StartDragging handler. + # TODO: this test fails, following the steps of SendMouse*Event, + # it's not successfuly triggered. + # browser.SendMouseMoveEvent(305, 20, False, 0) + # browser.SendMouseClickEvent(300, 20, cef.MOUSEBUTTON_LEFT, False, 1, cef.EVENTFLAG_LEFT_MOUSE_BUTTON) + # browser.SendMouseMoveEvent(305, 25, False, cef.EVENTFLAG_LEFT_MOUSE_BUTTON) + # browser.SendMouseClickEvent(305, 25, cef.MOUSEBUTTON_LEFT, True, 1, cef.EVENTFLAG_LEFT_MOUSE_BUTTON) + + # testing drag event + # TODO: It can trigging cef.OnDragEnter, + # but still not trigger dragenter event in JS. + dragData = cef.DragData() + browser.DragTargetDragEnter(dragData, 301, 21, cef.DRAG_OPERATION_COPY) + browser.DragTargetDragOver(302, 22, cef.DRAG_OPERATION_COPY) + browser.DragTargetDrop(303, 23) + browser.DragSourceEndedAt(303, 23, cef.DRAG_OPERATION_COPY) + browser.DragSourceSystemDragEnded() + +class RenderHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.test_for_True = True + # self.StartDragging_True = False + + def StartDragging(self,browser, drag_data, allowed_ops, x, y): + # self.test_case.assertFalse(self.StartDragging_True) + # self.StartDragging_True = True + return False + +class DragHandler(object): + def __init__(self, test_case): + self.test_case = test_case + self.test_for_True = True + self.OnDragEnter_True = False + + def OnDragEnter(self, browser, dragData, mask): + self.test_case.assertFalse(self.OnDragEnter_True) + self.OnDragEnter_True = True + return False + +if __name__ == "__main__": + unittests._test_runner.main(basename(__file__))