Skip to content

Commit 7406ba9

Browse files
committed
Implemented a default DownloadHandler. File downloads are handled transparently, no additional
code is required (Issue 87). Fixes to keyboard handler (F5, Esc) in the wxpython examples.
1 parent cb35b21 commit 7406ba9

File tree

11 files changed

+310
-28
lines changed

11 files changed

+310
-28
lines changed

cefpython/cef3/client_handler/client_handler.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// License: New BSD License.
33
// Website: http://code.google.com/p/cefpython/
44

5+
// ClientHandler code is running in the Browser process only.
6+
57
#include "client_handler.h"
68
#include "cefpython_public_api.h"
79
#include "DebugLog.h"
@@ -776,3 +778,57 @@ void ClientHandler::OnDialogClosed(CefRefPtr<CefBrowser> browser) {
776778
return JavascriptDialogHandler_OnJavascriptDialogClosed(browser);
777779
}
778780

781+
// ----------------------------------------------------------------------------
782+
// CefDownloadHandler
783+
// ----------------------------------------------------------------------------
784+
785+
///
786+
// Class used to handle file downloads. The methods of this class will called
787+
// on the browser process UI thread.
788+
///
789+
790+
///
791+
// Called before a download begins. |suggested_name| is the suggested name for
792+
// the download file. By default the download will be canceled. Execute
793+
// |callback| either asynchronously or in this method to continue the download
794+
// if desired. Do not keep a reference to |download_item| outside of this
795+
// method.
796+
///
797+
/*--cef()--*/
798+
void ClientHandler::OnBeforeDownload(CefRefPtr<CefBrowser> browser,
799+
CefRefPtr<CefDownloadItem> download_item,
800+
const CefString& suggested_name,
801+
CefRefPtr<CefBeforeDownloadCallback> callback) {
802+
REQUIRE_UI_THREAD();
803+
bool downloads_enabled = ApplicationSettings_GetBool("downloads_enabled");
804+
if (downloads_enabled) {
805+
std::string msg = "Browser: About to download file: ";
806+
msg.append(suggested_name.ToString().c_str());
807+
DebugLog(msg.c_str());
808+
callback->Continue(suggested_name, true);
809+
} else {
810+
DebugLog("Browser: Tried to download file, but downloads are disabled");
811+
}
812+
}
813+
814+
///
815+
// Called when a download's status or progress information has been updated.
816+
// This may be called multiple times before and after OnBeforeDownload().
817+
// Execute |callback| either asynchronously or in this method to cancel the
818+
// download if desired. Do not keep a reference to |download_item| outside of
819+
// this method.
820+
///
821+
/*--cef()--*/
822+
void ClientHandler::OnDownloadUpdated(
823+
CefRefPtr<CefBrowser> browser,
824+
CefRefPtr<CefDownloadItem> download_item,
825+
CefRefPtr<CefDownloadItemCallback> callback) {
826+
REQUIRE_UI_THREAD();
827+
if (download_item->IsComplete()) {
828+
std::string msg = "Browser: Download completed, saved to: ";
829+
msg.append(download_item->GetFullPath().ToString().c_str());
830+
DebugLog(msg.c_str());
831+
} else if (download_item->IsCanceled()) {
832+
DebugLog("Browser: Download was cancelled");
833+
}
834+
}

cefpython/cef3/client_handler/client_handler.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// License: New BSD License.
33
// Website: http://code.google.com/p/cefpython/
44

5+
// ClientHandler code is running in the Browser process only.
6+
57
#pragma once
68

79
#if defined(_WIN32)
@@ -18,7 +20,8 @@ class ClientHandler :
1820
public CefRequestHandler,
1921
public CefLoadHandler,
2022
public CefRenderHandler,
21-
public CefJSDialogHandler
23+
public CefJSDialogHandler,
24+
public CefDownloadHandler
2225
{
2326
public:
2427
ClientHandler(){}
@@ -56,7 +59,7 @@ class ClientHandler :
5659
///
5760
/*--cef()--*/
5861
virtual CefRefPtr<CefDownloadHandler> GetDownloadHandler() OVERRIDE {
59-
return NULL;
62+
return this;
6063
}
6164

6265
///
@@ -700,6 +703,42 @@ class ClientHandler :
700703
/*--cef()--*/
701704
virtual void OnDialogClosed(CefRefPtr<CefBrowser> browser) OVERRIDE;
702705

706+
// --------------------------------------------------------------------------
707+
// CefDownloadHandler
708+
// --------------------------------------------------------------------------
709+
710+
///
711+
// Class used to handle file downloads. The methods of this class will called
712+
// on the browser process UI thread.
713+
///
714+
715+
///
716+
// Called before a download begins. |suggested_name| is the suggested name for
717+
// the download file. By default the download will be canceled. Execute
718+
// |callback| either asynchronously or in this method to continue the download
719+
// if desired. Do not keep a reference to |download_item| outside of this
720+
// method.
721+
///
722+
/*--cef()--*/
723+
virtual void OnBeforeDownload(
724+
CefRefPtr<CefBrowser> browser,
725+
CefRefPtr<CefDownloadItem> download_item,
726+
const CefString& suggested_name,
727+
CefRefPtr<CefBeforeDownloadCallback> callback) OVERRIDE;
728+
729+
///
730+
// Called when a download's status or progress information has been updated.
731+
// This may be called multiple times before and after OnBeforeDownload().
732+
// Execute |callback| either asynchronously or in this method to cancel the
733+
// download if desired. Do not keep a reference to |download_item| outside of
734+
// this method.
735+
///
736+
/*--cef()--*/
737+
virtual void OnDownloadUpdated(
738+
CefRefPtr<CefBrowser> browser,
739+
CefRefPtr<CefDownloadItem> download_item,
740+
CefRefPtr<CefDownloadItemCallback> callback) OVERRIDE;
741+
703742

704743
private:
705744

cefpython/cef3/linux/binaries_32bit/wxpython.html

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<title>wxPython CEF 3 example (utf-8: ąś)</title>
66
<style>
77
body { font: 13px Segoe UI, Arial; line-height: 1.4em; }
8-
pre { background: #ddd; font: 12px Consolas, Courier New; }
8+
pre { background: #eee; font: 12px Consolas, Courier New; }
99
</style>
1010
<script>
1111
function ShowSource(func) {
@@ -37,6 +37,7 @@ <h2>Table of contents</h2>
3737
<li><a href="#user-agent">User agent</a></li>
3838
<li><a href="#popups">Popups</a></li>
3939
<li><a href="#html5-video">HTML 5 video</a></li>
40+
<li><a href="#downloads">Downloads</a></li>
4041
<li><a href="#browser-object">Browser object</a></li>
4142
<li><a href="#frame-object">Frame object</a></li>
4243
<li><a href="#javascript-bindings">Javascript bindings</a></li>
@@ -116,6 +117,42 @@ <h2>HTML5 video and accelerated content</h2>
116117
<a href="http://www.webkit.org/blog-files/3d-transforms/poster-circle.html">
117118
Accelerated layers</a><br>
118119

120+
<!-- ********************************************************************** -->
121+
<!-- Downloads -->
122+
<!-- ********************************************************************** -->
123+
124+
<a name="downloads"></a>
125+
<h2>Downloads</h2>
126+
127+
Download sample Ubuntu wallpapers:<br>
128+
<a href="https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip">
129+
https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip</a>
130+
131+
<p>
132+
Notes: On Linux it seems that OnLoadError with errorCode = ERR_ABORTED
133+
is called even for successful downloads, you can ignore this behavior.
134+
The proper console messages about successful/aborted download originate
135+
from C++ Browser process code, these are:
136+
</p>
137+
138+
<pre>Browser: About to download file: ubuntu-wallpapers2.zip
139+
Browser: Download completed, saved to: /Downloads/ubuntu-wallpapers2.zip
140+
</pre>
141+
142+
If download was aborted the messages will be:
143+
144+
<pre>Browser: About to download file: ubuntu-wallpapers2.zip
145+
Browser: Download was cancelled
146+
</pre>
147+
148+
<p>
149+
Additionally on Linux there are more errors reported by Chromium
150+
about org.gnome.SessionManager.inhibit. These can be safely ignored as well.
151+
</p>
152+
153+
A download handler with callbacks like `OnBeforeDownload` and
154+
`OnDownloadUpdated` may be exposed to CEF Python in the future.
155+
119156
<!-- ********************************************************************** -->
120157
<!-- Browser object -->
121158
<!-- ********************************************************************** -->
@@ -138,6 +175,14 @@ <h3>LoadUrl, GetUrl</h3>
138175
<a href="javascript:window.open('data:text/html,Test#Browser.LoadUrl')">
139176
window.open('data:text/html,Test#Browser.LoadUrl')</a>
140177

178+
<h3>ReloadIgnoreCache, StopLoad</h3>
179+
Press F5 to reload page and ignore cache.<br>
180+
Press Esc during webpage loading to abort.<br>
181+
<script>ShowSource("OnKeyEvent");</script>
182+
Also, when Esc is pressed OnLoadError may get called. See how abort
183+
of page loading or file download is handled:
184+
<script>ShowSource("OnLoadError");</script>
185+
141186
<!-- ********************************************************************** -->
142187
<!-- Frame object -->
143188
<!-- ********************************************************************** -->

cefpython/cef3/linux/binaries_32bit/wxpython.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,38 @@ def OnPreKeyEvent(self, browser, event, eventHandle,
369369
print("[wxpython.py] KeyboardHandler::OnPreKeyEvent()")
370370

371371
def OnKeyEvent(self, browser, event, eventHandle):
372+
if event["type"] == cefpython.KEYEVENT_KEYUP:
373+
# OnKeyEvent is called twice for F5/Esc keys, with event
374+
# type KEYEVENT_RAWKEYDOWN and KEYEVENT_KEYUP.
375+
# Normal characters a-z should have KEYEVENT_CHAR.
376+
return False
372377
print("[wxpython.py] KeyboardHandler::OnKeyEvent()")
373-
print(" native_key_code = %s" % event["native_key_code"])
378+
print(" type=%s" % event["type"])
379+
print(" modifiers=%s" % event["modifiers"])
380+
print(" windows_key_code=%s" % event["windows_key_code"])
381+
print(" native_key_code=%s" % event["native_key_code"])
382+
print(" is_system_key=%s" % event["is_system_key"])
383+
print(" character=%s" % event["character"])
384+
print(" unmodified_character=%s" % event["unmodified_character"])
385+
print(" focus_on_editable_field=%s"\
386+
% event["focus_on_editable_field"])
374387
if platform.system() == "Linux":
375-
# F5 = 71
388+
# F5
376389
if event["native_key_code"] == 71:
377-
print("[wxpython.py] F5 pressed! Reloading page..")
390+
print("[wxpython.py] F5 pressed, calling"\
391+
" browser.ReloadIgnoreCache()")
378392
browser.ReloadIgnoreCache()
393+
return True
394+
# Escape
395+
if event["native_key_code"] == 9:
396+
print("[wxpython.py] Esc pressed, calling browser.StopLoad()")
397+
browser.StopLoad()
398+
return True
399+
elif platform.system() == "Windows":
400+
# F5 todo
401+
# Escape todo
402+
pass
403+
return False
379404

380405
# -------------------------------------------------------------------------
381406
# RequestHandler
@@ -522,6 +547,13 @@ def OnLoadError(self, browser, frame, errorCode, errorTextList, failedUrl):
522547
print(" error code = %s" % errorCode)
523548
print(" error text = %s" % errorTextList[0])
524549
print(" failed url = %s" % failedUrl)
550+
# Handle ERR_ABORTED error code, to handle the following cases:
551+
# 1. Esc key was pressed which calls browser.StopLoad() in OnKeyEvent
552+
# 2. Download of a file was aborted
553+
if errorCode == cefpython.ERR_ABORTED:
554+
print("[wxpython.py] LoadHandler::OnLoadError(): Ignoring load"\
555+
" error: Esc was pressed or file download was aborted")
556+
return;
525557
customErrorMessage = "My custom error message!"
526558
frame.LoadUrl("data:text/html,%s" % customErrorMessage)
527559

@@ -656,7 +688,10 @@ def GetSources():
656688
# to work. It affects renderer processes, when this option
657689
# is set to True. It will force a separate renderer process
658690
# for each browser created using CreateBrowserSync.
659-
"unique_request_context_per_browser": True
691+
"unique_request_context_per_browser": True,
692+
# Downloads are handled automatically. A default SaveAs file
693+
# dialog provided by OS will be displayed.
694+
"downloads_enabled": True,
660695
}
661696

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

cefpython/cef3/linux/binaries_64bit/wxpython.html

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<title>wxPython CEF 3 example (utf-8: ąś)</title>
66
<style>
77
body { font: 13px Segoe UI, Arial; line-height: 1.4em; }
8-
pre { background: #ddd; font: 12px Consolas, Courier New; }
8+
pre { background: #eee; font: 12px Consolas, Courier New; }
99
</style>
1010
<script>
1111
function ShowSource(func) {
@@ -37,6 +37,7 @@ <h2>Table of contents</h2>
3737
<li><a href="#user-agent">User agent</a></li>
3838
<li><a href="#popups">Popups</a></li>
3939
<li><a href="#html5-video">HTML 5 video</a></li>
40+
<li><a href="#downloads">Downloads</a></li>
4041
<li><a href="#browser-object">Browser object</a></li>
4142
<li><a href="#frame-object">Frame object</a></li>
4243
<li><a href="#javascript-bindings">Javascript bindings</a></li>
@@ -116,6 +117,42 @@ <h2>HTML5 video and accelerated content</h2>
116117
<a href="http://www.webkit.org/blog-files/3d-transforms/poster-circle.html">
117118
Accelerated layers</a><br>
118119

120+
<!-- ********************************************************************** -->
121+
<!-- Downloads -->
122+
<!-- ********************************************************************** -->
123+
124+
<a name="downloads"></a>
125+
<h2>Downloads</h2>
126+
127+
Download sample Ubuntu wallpapers:<br>
128+
<a href="https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip">
129+
https://cefpython.googlecode.com/files/ubuntu-wallpapers2.zip</a>
130+
131+
<p>
132+
Notes: On Linux it seems that OnLoadError with errorCode = ERR_ABORTED
133+
is called even for successful downloads, you can ignore this behavior.
134+
The proper console messages about successful/aborted download originate
135+
from C++ Browser process code, these are:
136+
</p>
137+
138+
<pre>Browser: About to download file: ubuntu-wallpapers2.zip
139+
Browser: Download completed, saved to: /Downloads/ubuntu-wallpapers2.zip
140+
</pre>
141+
142+
If download was aborted the messages will be:
143+
144+
<pre>Browser: About to download file: ubuntu-wallpapers2.zip
145+
Browser: Download was cancelled
146+
</pre>
147+
148+
<p>
149+
Additionally on Linux there are more errors reported by Chromium
150+
about org.gnome.SessionManager.inhibit. These can be safely ignored as well.
151+
</p>
152+
153+
A download handler with callbacks like `OnBeforeDownload` and
154+
`OnDownloadUpdated` may be exposed to CEF Python in the future.
155+
119156
<!-- ********************************************************************** -->
120157
<!-- Browser object -->
121158
<!-- ********************************************************************** -->
@@ -138,6 +175,14 @@ <h3>LoadUrl, GetUrl</h3>
138175
<a href="javascript:window.open('data:text/html,Test#Browser.LoadUrl')">
139176
window.open('data:text/html,Test#Browser.LoadUrl')</a>
140177

178+
<h3>ReloadIgnoreCache, StopLoad</h3>
179+
Press F5 to reload page and ignore cache.<br>
180+
Press Esc during webpage loading to abort.<br>
181+
<script>ShowSource("OnKeyEvent");</script>
182+
Also, when Esc is pressed OnLoadError may get called. See how abort
183+
of page loading or file download is handled:
184+
<script>ShowSource("OnLoadError");</script>
185+
141186
<!-- ********************************************************************** -->
142187
<!-- Frame object -->
143188
<!-- ********************************************************************** -->

0 commit comments

Comments
 (0)