Skip to content

Commit 81cccea

Browse files
committed
Mouse context menu is now configurable, see ApplicationSettints.context_menu.
New menu items added: "Reload", "Open in external browser", "Developer Tools". (Issue 122) Added HTML controls section to the wxpython example.
1 parent dddbd6e commit 81cccea

10 files changed

Lines changed: 328 additions & 5 deletions

File tree

cefpython/browser.pyx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ cpdef PyBrowser GetBrowserByWindowHandle(WindowHandle windowHandle):
113113
return pyBrowser
114114
return None
115115

116+
cdef public void PyBrowser_ShowDevTools(CefRefPtr[CefBrowser] cefBrowser
117+
) except * with gil:
118+
# Called from ClientHandler::OnContextMenuCommand
119+
cdef PyBrowser pyBrowser
120+
try:
121+
pyBrowser = GetPyBrowser(cefBrowser)
122+
pyBrowser.ShowDevTools()
123+
except:
124+
(exc_type, exc_value, exc_trace) = sys.exc_info()
125+
sys.excepthook(exc_type, exc_value, exc_trace)
126+
127+
# -----------------------------------------------------------------------------
128+
116129
cdef class PyBrowser:
117130
cdef CefRefPtr[CefBrowser] cefBrowser
118131

@@ -499,7 +512,7 @@ cdef class PyBrowser:
499512
# | http://localhost:54008/devtools/devtools.html?ws=localhost:54008
500513
# | /devtools/page/1538ed984a2a4a90e5ed941c7d142a12
501514
# Let's replace "localhost" with "127.0.0.1", using the ip address
502-
# is more reliable.
515+
# which is more reliable.
503516
url = url.replace("localhost:", "127.0.0.1:")
504517
jsCode = ("window.open('%s');" % url)
505518
self.GetMainFrame().ExecuteJavascript(jsCode)

cefpython/cef3/client_handler/client_handler.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,51 @@
88
#include "cefpython_public_api.h"
99
#include "DebugLog.h"
1010

11+
#if defined(OS_WIN)
12+
#include <Shellapi.h>
13+
#pragma comment(lib, "Shell32.lib")
14+
#elif defined(OS_LINUX)
15+
#include <unistd.h>
16+
#include <stdlib.h>
17+
#endif
18+
19+
// ----------------------------------------------------------------------------
20+
// Linux equivalent of ShellExecute
21+
// ----------------------------------------------------------------------------
22+
23+
#if defined(OS_LINUX)
24+
void OpenInExternalBrowser(const std::string& url)
25+
{
26+
if (url.empty()) {
27+
DebugLog("Browser: OpenInExternalBrowser() FAILED: url is empty");
28+
return;
29+
}
30+
std::string msg = "Browser: OpenInExternalBrowser(): url=";
31+
msg.append(url.c_str());
32+
DebugLog(msg.c_str());
33+
34+
// xdg-open is a desktop-independent tool for running
35+
// default applications. Installed by default on Ubuntu.
36+
// xdg-open process is running in the backround until
37+
// cefpython app closes.
38+
std::string prog = "xdg-open";
39+
40+
// Using system() opens up for bugs and exploits, not
41+
// recommended.
42+
43+
// Fork yourself and run in parallel, do not block the
44+
// current proces.
45+
char *args[3];
46+
args[0] = (char*) prog.c_str();
47+
args[1] = (char*) url.c_str();
48+
args[2] = 0;
49+
pid_t pid = fork();
50+
if (!pid) {
51+
execvp(prog.c_str(), args);
52+
}
53+
}
54+
#endif
55+
1156
// ----------------------------------------------------------------------------
1257
// CefClient
1358
// ----------------------------------------------------------------------------
@@ -832,3 +877,126 @@ void ClientHandler::OnDownloadUpdated(
832877
DebugLog("Browser: Download was cancelled");
833878
}
834879
}
880+
881+
// ----------------------------------------------------------------------------
882+
// CefContextMenuHandler
883+
// ----------------------------------------------------------------------------
884+
885+
#define _MENU_ID_DEVTOOLS MENU_ID_USER_FIRST + 1
886+
#define _MENU_ID_RELOAD_PAGE MENU_ID_USER_FIRST + 2
887+
#define _MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER MENU_ID_USER_FIRST + 3
888+
#define _MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER MENU_ID_USER_FIRST + 4
889+
890+
///
891+
// Called before a context menu is displayed. |params| provides information
892+
// about the context menu state. |model| initially contains the default
893+
// context menu. The |model| can be cleared to show no context menu or
894+
// modified to show a custom menu. Do not keep references to |params| or
895+
// |model| outside of this callback.
896+
///
897+
/*--cef()--*/
898+
void ClientHandler::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
899+
CefRefPtr<CefFrame> frame,
900+
CefRefPtr<CefContextMenuParams> params,
901+
CefRefPtr<CefMenuModel> model) {
902+
bool enabled = ApplicationSettings_GetBoolFromDict(\
903+
"context_menu", "enabled");
904+
bool navigation = ApplicationSettings_GetBoolFromDict(\
905+
"context_menu", "navigation");
906+
bool print = ApplicationSettings_GetBoolFromDict(\
907+
"context_menu", "print");
908+
bool view_source = ApplicationSettings_GetBoolFromDict(\
909+
"context_menu", "view_source");
910+
bool external_browser = ApplicationSettings_GetBoolFromDict(\
911+
"context_menu", "external_browser");
912+
bool devtools = ApplicationSettings_GetBoolFromDict(\
913+
"context_menu", "devtools");
914+
915+
if (!enabled) {
916+
model->Clear();
917+
return;
918+
}
919+
if (!navigation) {
920+
model->Remove(MENU_ID_BACK);
921+
model->Remove(MENU_ID_FORWARD);
922+
// Remove separator
923+
model->RemoveAt(0);
924+
}
925+
if (!print) {
926+
model->Remove(MENU_ID_PRINT);
927+
}
928+
if (!view_source) {
929+
model->Remove(MENU_ID_VIEW_SOURCE);
930+
}
931+
if (!params->IsEditable() && params->GetSelectionText().empty()
932+
&& (params->GetPageUrl().length()
933+
|| params->GetFrameUrl().length())) {
934+
if (external_browser) {
935+
model->AddItem(_MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER,
936+
"Open in external browser");
937+
if (params->GetFrameUrl().length()
938+
&& params->GetPageUrl() != params->GetFrameUrl()) {
939+
model->AddItem(_MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER,
940+
"Open frame in external browser");
941+
}
942+
}
943+
if (navigation) {
944+
model->InsertItemAt(2, _MENU_ID_RELOAD_PAGE, "Reload");
945+
}
946+
if (devtools) {
947+
model->AddSeparator();
948+
model->AddItem(_MENU_ID_DEVTOOLS, "Developer Tools");
949+
}
950+
}
951+
}
952+
953+
///
954+
// Called to execute a command selected from the context menu. Return true if
955+
// the command was handled or false for the default implementation. See
956+
// cef_menu_id_t for the command ids that have default implementations. All
957+
// user-defined command ids should be between MENU_ID_USER_FIRST and
958+
// MENU_ID_USER_LAST. |params| will have the same values as what was passed to
959+
// OnBeforeContextMenu(). Do not keep a reference to |params| outside of this
960+
// callback.
961+
///
962+
/*--cef()--*/
963+
bool ClientHandler::OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
964+
CefRefPtr<CefFrame> frame,
965+
CefRefPtr<CefContextMenuParams> params,
966+
int command_id,
967+
EventFlags event_flags) {
968+
if (command_id == _MENU_ID_OPEN_PAGE_IN_EXTERNAL_BROWSER) {
969+
#if defined(OS_WIN)
970+
ShellExecute(0, L"open", params->GetPageUrl().ToWString().c_str(),
971+
0, 0, SW_SHOWNORMAL);
972+
#elif defined(OS_LINUX)
973+
OpenInExternalBrowser(params->GetPageUrl().ToString());
974+
#endif
975+
return true;
976+
} else if (command_id == _MENU_ID_OPEN_FRAME_IN_EXTERNAL_BROWSER) {
977+
#if defined(OS_WIN)
978+
ShellExecute(0, L"open", params->GetFrameUrl().ToWString().c_str(),
979+
0, 0, SW_SHOWNORMAL);
980+
#elif defined(OS_LINUX)
981+
OpenInExternalBrowser(params->GetFrameUrl().ToString());
982+
#endif
983+
return true;
984+
} else if (command_id == _MENU_ID_RELOAD_PAGE) {
985+
browser->ReloadIgnoreCache();
986+
return true;
987+
} else if (command_id == _MENU_ID_DEVTOOLS) {
988+
PyBrowser_ShowDevTools(browser);
989+
return true;
990+
}
991+
return false;
992+
}
993+
994+
///
995+
// Called when the context menu is dismissed irregardless of whether the menu
996+
// was empty or a command was selected.
997+
///
998+
/*--cef()--*/
999+
void ClientHandler::OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
1000+
CefRefPtr<CefFrame> frame) {
1001+
}
1002+

cefpython/cef3/client_handler/client_handler.h

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ class ClientHandler :
2121
public CefLoadHandler,
2222
public CefRenderHandler,
2323
public CefJSDialogHandler,
24-
public CefDownloadHandler
24+
public CefDownloadHandler,
25+
public CefContextMenuHandler
2526
{
2627
public:
2728
ClientHandler(){}
@@ -33,7 +34,7 @@ class ClientHandler :
3334
///
3435
/*--cef()--*/
3536
virtual CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() OVERRIDE {
36-
return NULL;
37+
return this;
3738
}
3839

3940
///
@@ -739,6 +740,54 @@ class ClientHandler :
739740
CefRefPtr<CefDownloadItem> download_item,
740741
CefRefPtr<CefDownloadItemCallback> callback) OVERRIDE;
741742

743+
// --------------------------------------------------------------------------
744+
// CefDownloadHandler
745+
// --------------------------------------------------------------------------
746+
747+
///
748+
// Implement this interface to handle context menu events. The methods of this
749+
// class will be called on the UI thread.
750+
///
751+
752+
typedef cef_event_flags_t EventFlags;
753+
754+
///
755+
// Called before a context menu is displayed. |params| provides information
756+
// about the context menu state. |model| initially contains the default
757+
// context menu. The |model| can be cleared to show no context menu or
758+
// modified to show a custom menu. Do not keep references to |params| or
759+
// |model| outside of this callback.
760+
///
761+
/*--cef()--*/
762+
virtual void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
763+
CefRefPtr<CefFrame> frame,
764+
CefRefPtr<CefContextMenuParams> params,
765+
CefRefPtr<CefMenuModel> model) OVERRIDE;
766+
767+
///
768+
// Called to execute a command selected from the context menu. Return true if
769+
// the command was handled or false for the default implementation. See
770+
// cef_menu_id_t for the command ids that have default implementations. All
771+
// user-defined command ids should be between MENU_ID_USER_FIRST and
772+
// MENU_ID_USER_LAST. |params| will have the same values as what was passed to
773+
// OnBeforeContextMenu(). Do not keep a reference to |params| outside of this
774+
// callback.
775+
///
776+
/*--cef()--*/
777+
virtual bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
778+
CefRefPtr<CefFrame> frame,
779+
CefRefPtr<CefContextMenuParams> params,
780+
int command_id,
781+
EventFlags event_flags) OVERRIDE;
782+
783+
///
784+
// Called when the context menu is dismissed irregardless of whether the menu
785+
// was empty or a command was selected.
786+
///
787+
/*--cef()--*/
788+
virtual void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
789+
CefRefPtr<CefFrame> frame) OVERRIDE;
790+
742791

743792
private:
744793

cefpython/cef3/linux/binaries_32bit/wxpython.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ <h2>Table of contents</h2>
3939
<li><a href="#html5-video">HTML 5 video</a></li>
4040
<li><a href="#developer-tools">Developer Tools</a></li>
4141
<li><a href="#downloads">Downloads</a></li>
42+
<li><a href="#html-controls">HTML controls</a></li>
4243
<li><a href="#browser-object">Browser object</a></li>
4344
<li><a href="#frame-object">Frame object</a></li>
4445
<li><a href="#javascript-bindings">Javascript bindings</a></li>
@@ -170,6 +171,28 @@ <h2>Downloads</h2>
170171
A download handler with callbacks like `OnBeforeDownload` and
171172
`OnDownloadUpdated` may be exposed to CEF Python in the future.
172173

174+
<!-- ********************************************************************** -->
175+
<!-- HTML controls -->
176+
<!-- ********************************************************************** -->
177+
178+
<a name="html-controls"></a>
179+
<h2>HTML controls</h2>
180+
181+
<h3>Textarea</h3>
182+
<textarea cols="40" rows="5"></textarea>
183+
<br>
184+
185+
<h3>Inputs</h3>
186+
Text: <input type="text"><br>
187+
Password: <input type="password"><br>
188+
189+
<h3>Select</h3>
190+
<select><option>Option 1</option><option>Option 2</option></select>
191+
192+
<h3>Buttons</h3>
193+
Submit: <input type="submit"><br>
194+
Button: <input type="button" value="Button"><br>
195+
173196
<!-- ********************************************************************** -->
174197
<!-- Browser object -->
175198
<!-- ********************************************************************** -->

cefpython/cef3/linux/binaries_32bit/wxpython.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,15 @@ def GetSources():
706706
# A value of 0 will generate a random port. To disable devtools
707707
# support set it to -1.
708708
"remote_debugging_port": 0,
709+
# Mouse context menu
710+
"context_menu": {
711+
"enabled": True,
712+
"navigation": True, # Back, Forward, Reload
713+
"print": True,
714+
"view_source": True,
715+
"external_browser": True, # Open in external browser
716+
"devtools": True, # Developer Tools
717+
},
709718
}
710719

711720
# Browser settings. You may have different settings for each

cefpython/cef3/linux/binaries_64bit/wxpython.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ <h2>Table of contents</h2>
3939
<li><a href="#html5-video">HTML 5 video</a></li>
4040
<li><a href="#developer-tools">Developer Tools</a></li>
4141
<li><a href="#downloads">Downloads</a></li>
42+
<li><a href="#html-controls">HTML controls</a></li>
4243
<li><a href="#browser-object">Browser object</a></li>
4344
<li><a href="#frame-object">Frame object</a></li>
4445
<li><a href="#javascript-bindings">Javascript bindings</a></li>
@@ -170,6 +171,28 @@ <h2>Downloads</h2>
170171
A download handler with callbacks like `OnBeforeDownload` and
171172
`OnDownloadUpdated` may be exposed to CEF Python in the future.
172173

174+
<!-- ********************************************************************** -->
175+
<!-- HTML controls -->
176+
<!-- ********************************************************************** -->
177+
178+
<a name="html-controls"></a>
179+
<h2>HTML controls</h2>
180+
181+
<h3>Textarea</h3>
182+
<textarea cols="40" rows="5"></textarea>
183+
<br>
184+
185+
<h3>Inputs</h3>
186+
Text: <input type="text"><br>
187+
Password: <input type="password"><br>
188+
189+
<h3>Select</h3>
190+
<select><option>Option 1</option><option>Option 2</option></select>
191+
192+
<h3>Buttons</h3>
193+
Submit: <input type="submit"><br>
194+
Button: <input type="button" value="Button"><br>
195+
173196
<!-- ********************************************************************** -->
174197
<!-- Browser object -->
175198
<!-- ********************************************************************** -->

cefpython/cef3/linux/binaries_64bit/wxpython.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,15 @@ def GetSources():
706706
# A value of 0 will generate a random port. To disable devtools
707707
# support set it to -1.
708708
"remote_debugging_port": 0,
709+
# Mouse context menu
710+
"context_menu": {
711+
"enabled": True,
712+
"navigation": True, # Back, Forward, Reload
713+
"print": True,
714+
"view_source": True,
715+
"external_browser": True, # Open in external browser
716+
"devtools": True, # Developer Tools
717+
},
709718
}
710719

711720
# Browser settings. You may have different settings for each

0 commit comments

Comments
 (0)