diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 9961d6cc6601..3ca15ab871fa 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -9,6 +9,7 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/atom_browser_context.h" +#include "atom/browser/api/atom_api_web_contents.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/file_path_converter.h" @@ -101,6 +102,19 @@ struct Converter { } }; +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + content::DownloadItem* val) { + mate::Dictionary dict(isolate, v8::Object::New(isolate)); + dict.Set("url", val->GetURL()); + dict.Set("filename", val->GetSuggestedFilename()); + dict.Set("mimeType", val->GetMimeType()); + dict.Set("hasUserGesture", val->HasUserGesture()); + return dict.GetHandle(); + } +}; + } // namespace mate namespace atom { @@ -109,6 +123,10 @@ namespace api { namespace { +// The wrapSession funtion which is implemented in JavaScript +using WrapSessionCallback = base::Callback)>; +WrapSessionCallback g_wrap_session; + class ResolveProxyHelper { public: ResolveProxyHelper(AtomBrowserContext* browser_context, @@ -215,9 +233,28 @@ void SetProxyInIO(net::URLRequestContextGetter* getter, Session::Session(AtomBrowserContext* browser_context) : browser_context_(browser_context) { AttachAsUserData(browser_context); + // Observe DownloadManger to get download notifications. + auto download_manager = + content::BrowserContext::GetDownloadManager(browser_context); + download_manager->AddObserver(this); } Session::~Session() { + auto download_manager = + content::BrowserContext::GetDownloadManager(browser_context_); + download_manager->RemoveObserver(this); +} + +void Session::OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) { + auto web_contents = item->GetWebContents(); + bool prevent_default = Emit("will-download", item, + api::WebContents::CreateFrom(isolate(), + web_contents)); + if (prevent_default) { + item->Cancel(true); + item->Remove(); + } } void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) { @@ -288,9 +325,33 @@ mate::Handle Session::CreateFrom( if (existing) return mate::CreateHandle(isolate, static_cast(existing)); - return mate::CreateHandle(isolate, new Session(browser_context)); + auto handle = mate::CreateHandle(isolate, new Session(browser_context)); + g_wrap_session.Run(handle.ToV8()); + return handle; +} + +void SetWrapSession(const WrapSessionCallback& callback) { + g_wrap_session = callback; +} + +void ClearWrapSession() { + g_wrap_session.Reset(); } } // namespace api } // namespace atom + +namespace { + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession); + dict.SetMethod("_clearWrapSession", &atom::api::ClearWrapSession); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_session, Initialize) diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index b353c61c2109..c06348974ff4 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -8,6 +8,7 @@ #include #include "atom/browser/api/trackable_object.h" +#include "content/public/browser/download_manager.h" #include "native_mate/handle.h" #include "net/base/completion_callback.h" @@ -27,7 +28,8 @@ class AtomBrowserContext; namespace api { -class Session: public mate::TrackableObject { +class Session: public mate::TrackableObject, + public content::DownloadManager::Observer { public: using ResolveProxyCallback = base::Callback; @@ -41,6 +43,10 @@ class Session: public mate::TrackableObject { explicit Session(AtomBrowserContext* browser_context); ~Session(); + // content::DownloadManager::Observer: + void OnDownloadCreated(content::DownloadManager* manager, + content::DownloadItem* item) override; + // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override; diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 2fbc1d899777..45027309ea65 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -10,8 +10,8 @@ #include "atom/browser/api/trackable_object.h" #include "atom/browser/common_web_contents_delegate.h" -#include "content/public/common/favicon_url.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/common/favicon_url.h" #include "native_mate/handle.h" #include "ui/gfx/image/image.h" diff --git a/atom/browser/api/lib/app.coffee b/atom/browser/api/lib/app.coffee index 3e05106d1927..b3446e86e23a 100644 --- a/atom/browser/api/lib/app.coffee +++ b/atom/browser/api/lib/app.coffee @@ -1,10 +1,15 @@ EventEmitter = require('events').EventEmitter bindings = process.atomBinding 'app' +sessionBindings = process.atomBinding 'session' app = bindings.app app.__proto__ = EventEmitter.prototype +wrapSession = (session) -> + # session is an Event Emitter. + session.__proto__ = EventEmitter.prototype + app.setApplicationMenu = (menu) -> require('menu').setApplicationMenu menu @@ -41,5 +46,9 @@ app.getDataPath = -> @getPath 'userData' app.setDataPath = (path) -> @setPath 'userData', path app.resolveProxy = -> @defaultSession.resolveProxy.apply @defaultSession, arguments +# Session wrapper. +sessionBindings._setWrapSession wrapSession +process.once 'exit', sessionBindings._clearWrapSession + # Only one App object pemitted. module.exports = app diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 5aed5619f9cf..09666a470b6c 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -40,6 +40,7 @@ REFERENCE_MODULE(atom_browser_power_monitor); REFERENCE_MODULE(atom_browser_power_save_blocker); REFERENCE_MODULE(atom_browser_protocol); REFERENCE_MODULE(atom_browser_global_shortcut); +REFERENCE_MODULE(atom_browser_session); REFERENCE_MODULE(atom_browser_tray); REFERENCE_MODULE(atom_browser_web_contents); REFERENCE_MODULE(atom_browser_web_view_manager); diff --git a/docs/api/session.md b/docs/api/session.md index 7b0c1b44c7dc..28a0c92ca655 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -13,6 +13,30 @@ win.loadUrl("http://github.com"); var session = win.webContents.session ``` +## Events + +### Event: 'will-download' + +* `event` Event +* `downloadItem` Object + * `url` String + * `filename` String + * `mimeType` String + * `hasUserGesture` Boolean +* `webContents` (WebContents)[web-contents.md] + +Fired when a download is about to start. Calling `preventDefault()` +will cancel the download. + +```javascript +session.on('will-download', function(e, downloadItem, webContents) { + e.preventDefault(); + require('request')(downloadItem.url, function(data) { + require('fs').writeFileSync('/somewhere', data); + }); +}); +``` + ## Methods The `session` object has the following methods: