diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000000..444ce0b4c821 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,24 @@ +# Contributor Code of Conduct + +As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery +- Personal attacks +- Trolling or insulting/derogatory comments +- Public or private harassment +- Publishing other's private information, such as physical or electronic addresses, without explicit permission +- Other unethical or unprofessional conduct + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at [atom@github.com](mailto:atom@github.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. + +This Code of Conduct is adapted from the Contributor Covenant, version 1.3.0, available from http://contributor-covenant.org/version/1/3/0/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 630b8b0ba0e2..67c1323205e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,9 @@ :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0). -By participating, you are expected to uphold this code. Please report unacceptable behavior to atom@github.com. +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable +behavior to atom@github.com. The following is a set of guidelines for contributing to Electron. These are just guidelines, not rules, use your best judgment and feel free to diff --git a/README-ko.md b/README-ko.md index 606d4e973e3e..7546f8c9f9a0 100644 --- a/README-ko.md +++ b/README-ko.md @@ -16,9 +16,7 @@ Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주 Electron에 대한 중요한 알림을 받고 싶다면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 팔로우 하세요. -이 프로젝트는 [기여자 규약 1.2](http://contributor-covenant.org/version/1/2/0/)을 -준수합니다. 따라서 이 프로젝트의 개발에 참여하려면 이 계약을 지켜야 합니다. 받아들일 수 -없는 행위를 발견했을 경우 atom@github.com로 보고 하십시오. +[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) ## 다운로드 diff --git a/README.md b/README.md index db82fac88ec6..bb814b245139 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ editor](https://github.com/atom/atom). Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important announcements. -This project adheres to the [Contributor Covenant 1.2](http://contributor-covenant.org/version/1/2/0/). -By participating, you are expected to uphold this code. Please report -unacceptable behavior to atom@github.com. +This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable +behavior to atom@github.com. ## Downloads @@ -67,6 +67,7 @@ locations: forums - `#atom-shell` channel on Freenode - [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack +- [`electron-br`](https://electron-br.slack.com) *(Brazilian Portuguese)* Check out [awesome-electron](https://github.com/sindresorhus/awesome-electron) for a community maintained list of useful example apps, tools and resources. diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index a3b2c37c9acd..3b1bd499be4c 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -322,14 +322,6 @@ void Cookies::OnSetCookies(const CookiesCallback& callback, callback)); } -mate::ObjectTemplateBuilder Cookies::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("get", &Cookies::Get) - .SetMethod("remove", &Cookies::Remove) - .SetMethod("set", &Cookies::Set); -} - net::CookieStore* Cookies::GetCookieStore() { return request_context_getter_->GetURLRequestContext()->cookie_store(); } @@ -341,6 +333,15 @@ mate::Handle Cookies::Create( return mate::CreateHandle(isolate, new Cookies(browser_context)); } +// static +void Cookies::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("get", &Cookies::Get) + .SetMethod("remove", &Cookies::Remove) + .SetMethod("set", &Cookies::Set); +} + } // namespace api } // namespace atom diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 0c309b3f18ee..5afa1bd23ca6 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -7,8 +7,8 @@ #include +#include "atom/browser/api/trackable_object.h" #include "base/callback.h" -#include "native_mate/wrappable.h" #include "native_mate/handle.h" #include "net/cookies/canonical_cookie.h" @@ -33,7 +33,7 @@ namespace atom { namespace api { -class Cookies : public mate::Wrappable { +class Cookies : public mate::TrackableObject { public: // node.js style callback function(error, result) typedef base::Callback, v8::Local)> @@ -42,6 +42,10 @@ class Cookies : public mate::Wrappable { static mate::Handle Create(v8::Isolate* isolate, content::BrowserContext* browser_context); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit Cookies(content::BrowserContext* browser_context); ~Cookies(); @@ -70,10 +74,6 @@ class Cookies : public mate::Wrappable { void OnSetCookies(const CookiesCallback& callback, bool set_success); - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - private: // Must be called on IO thread. net::CookieStore* GetCookieStore(); diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index 7dd271304a43..f186821e7d31 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -70,29 +70,20 @@ DownloadItem::DownloadItem(content::DownloadItem* download_item) : } DownloadItem::~DownloadItem() { - Destroy(); -} - -void DownloadItem::Destroy() { - if (download_item_) { - download_item_->RemoveObserver(this); - auto iter = g_download_item_objects.find(download_item_->GetId()); - if (iter != g_download_item_objects.end()) - g_download_item_objects.erase(iter); - download_item_ = nullptr; - } -} - -bool DownloadItem::IsDestroyed() const { - return download_item_ == nullptr; + if (download_item_) + OnDownloadDestroyed(download_item_); } void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) { download_item_->IsDone() ? Emit("done", item->GetState()) : Emit("updated"); } -void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download) { - Destroy(); +void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download_item) { + download_item_->RemoveObserver(this); + auto iter = g_download_item_objects.find(download_item_->GetId()); + if (iter != g_download_item_objects.end()) + g_download_item_objects.erase(iter); + download_item_ = nullptr; } int64 DownloadItem::GetReceivedBytes() { @@ -144,9 +135,11 @@ void DownloadItem::Cancel() { download_item_->Cancel(true); } -mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) +// static +void DownloadItem::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() .SetMethod("pause", &DownloadItem::Pause) .SetMethod("resume", &DownloadItem::Resume) .SetMethod("cancel", &DownloadItem::Cancel) diff --git a/atom/browser/api/atom_api_download_item.h b/atom/browser/api/atom_api_download_item.h index 955801cd9940..471913c2266a 100644 --- a/atom/browser/api/atom_api_download_item.h +++ b/atom/browser/api/atom_api_download_item.h @@ -17,7 +17,7 @@ namespace atom { namespace api { -class DownloadItem : public mate::EventEmitter, +class DownloadItem : public mate::TrackableObject, public content::DownloadItem::Observer { public: class SavePathData : public base::SupportsUserData::Data { @@ -32,6 +32,10 @@ class DownloadItem : public mate::EventEmitter, content::DownloadItem* item); static void* UserDataKey(); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit DownloadItem(content::DownloadItem* download_item); ~DownloadItem(); @@ -53,13 +57,6 @@ class DownloadItem : public mate::EventEmitter, void SetSavePath(const base::FilePath& path); private: - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - - void Destroy(); - content::DownloadItem* download_item_; DISALLOW_COPY_AND_ASSIGN(DownloadItem); diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc index 6ab4fa4b6186..f5a03e4abf90 100644 --- a/atom/browser/api/atom_api_global_shortcut.cc +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -23,9 +23,6 @@ GlobalShortcut::GlobalShortcut() { } GlobalShortcut::~GlobalShortcut() { -} - -void GlobalShortcut::Destroy() { UnregisterAll(); } diff --git a/atom/browser/api/atom_api_global_shortcut.h b/atom/browser/api/atom_api_global_shortcut.h index 93eb7853ae0d..d7057b000320 100644 --- a/atom/browser/api/atom_api_global_shortcut.h +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -27,9 +27,6 @@ class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, GlobalShortcut(); ~GlobalShortcut() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override; diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 1f16a428da01..96cba3fe9914 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -27,14 +27,6 @@ Menu::Menu() Menu::~Menu() { } -void Menu::Destroy() { - model_.reset(); -} - -bool Menu::IsDestroyed() const { - return !model_; -} - void Menu::AfterInit(v8::Isolate* isolate) { mate::Dictionary wrappable(isolate, GetWrapper(isolate)); mate::Dictionary delegate; @@ -159,6 +151,7 @@ bool Menu::IsVisibleAt(int index) const { void Menu::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() .SetMethod("insertItem", &Menu::InsertItemAt) .SetMethod("insertCheckItem", &Menu::InsertCheckItemAt) .SetMethod("insertRadioItem", &Menu::InsertRadioItemAt) diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 545dd18e386c..17bb9073a719 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -39,11 +39,7 @@ class Menu : public mate::TrackableObject, Menu(); ~Menu() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable: - bool IsDestroyed() const override; void AfterInit(v8::Isolate* isolate) override; // ui::SimpleMenuModel::Delegate: diff --git a/atom/browser/api/atom_api_menu_mac.h b/atom/browser/api/atom_api_menu_mac.h index baa2aff349e1..5a086776a639 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -19,7 +19,6 @@ class MenuMac : public Menu { protected: MenuMac(); - void Destroy() override; void Popup(Window* window) override; void PopupAt(Window* window, int x, int y) override; diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index 5936e0439f4f..071753218d6a 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -18,11 +18,6 @@ namespace api { MenuMac::MenuMac() { } -void MenuMac::Destroy() { - menu_controller_.reset(); - Menu::Destroy(); -} - void MenuMac::Popup(Window* window) { NativeWindow* native_window = window->window(); if (!native_window) diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index eeb9475c2847..31b35e10cea8 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -19,9 +19,6 @@ PowerMonitor::PowerMonitor() { } PowerMonitor::~PowerMonitor() { -} - -void PowerMonitor::Destroy() { base::PowerMonitor::Get()->RemoveObserver(this); } diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 9303b3ab288f..8fb52eeec95e 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -23,9 +23,6 @@ class PowerMonitor : public mate::TrackableObject, PowerMonitor(); ~PowerMonitor() override; - // mate::TrackableObject: - void Destroy() override; - // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; void OnSuspend() override; diff --git a/atom/browser/api/atom_api_power_save_blocker.cc b/atom/browser/api/atom_api_power_save_blocker.cc index f77979ae417f..58983e6c846a 100644 --- a/atom/browser/api/atom_api_power_save_blocker.cc +++ b/atom/browser/api/atom_api_power_save_blocker.cc @@ -45,11 +45,6 @@ PowerSaveBlocker::PowerSaveBlocker() PowerSaveBlocker::~PowerSaveBlocker() { } -void PowerSaveBlocker::Destroy() { - power_save_blocker_types_.clear(); - power_save_blocker_.reset(); -} - void PowerSaveBlocker::UpdatePowerSaveBlocker() { if (power_save_blocker_types_.empty()) { power_save_blocker_.reset(); diff --git a/atom/browser/api/atom_api_power_save_blocker.h b/atom/browser/api/atom_api_power_save_blocker.h index e7ce97878ffb..a698d746ceb0 100644 --- a/atom/browser/api/atom_api_power_save_blocker.h +++ b/atom/browser/api/atom_api_power_save_blocker.h @@ -28,9 +28,6 @@ class PowerSaveBlocker : public mate::TrackableObject { PowerSaveBlocker(); ~PowerSaveBlocker() override; - // mate::TrackableObject: - void Destroy() override; - // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( v8::Isolate* isolate) override; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 27e1521f3d3f..f39073825c00 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -253,7 +253,6 @@ Session::Session(AtomBrowserContext* browser_context) Session::~Session() { content::BrowserContext::GetDownloadManager(browser_context())-> RemoveObserver(this); - Destroy(); } void Session::OnDownloadCreated(content::DownloadManager* manager, @@ -271,14 +270,6 @@ void Session::OnDownloadCreated(content::DownloadManager* manager, } } -bool Session::IsDestroyed() const { - return !browser_context_; -} - -void Session::Destroy() { - browser_context_ = nullptr; -} - void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) { new ResolveProxyHelper(browser_context(), url, callback); } @@ -376,20 +367,6 @@ v8::Local Session::Cookies(v8::Isolate* isolate) { return v8::Local::New(isolate, cookies_); } -mate::ObjectTemplateBuilder Session::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("resolveProxy", &Session::ResolveProxy) - .SetMethod("clearCache", &Session::ClearCache) - .SetMethod("clearStorageData", &Session::ClearStorageData) - .SetMethod("setProxy", &Session::SetProxy) - .SetMethod("setDownloadPath", &Session::SetDownloadPath) - .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) - .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) - .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) - .SetProperty("cookies", &Session::Cookies); -} - // static mate::Handle Session::CreateFrom( v8::Isolate* isolate, AtomBrowserContext* browser_context) { @@ -410,6 +387,22 @@ mate::Handle Session::FromPartition( static_cast(browser_context.get())); } +// static +void Session::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() + .SetMethod("resolveProxy", &Session::ResolveProxy) + .SetMethod("clearCache", &Session::ClearCache) + .SetMethod("clearStorageData", &Session::ClearStorageData) + .SetMethod("setProxy", &Session::SetProxy) + .SetMethod("setDownloadPath", &Session::SetDownloadPath) + .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) + .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) + .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) + .SetProperty("cookies", &Session::Cookies); +} + void ClearWrapSession() { g_wrap_session.Reset(); } diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 01dc0a408a8c..e800a992d4b7 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -48,6 +48,10 @@ class Session: public mate::TrackableObject, AtomBrowserContext* browser_context() const { return browser_context_.get(); } + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit Session(AtomBrowserContext* browser_context); ~Session(); @@ -56,15 +60,7 @@ class Session: public mate::TrackableObject, void OnDownloadCreated(content::DownloadManager* manager, content::DownloadItem* item) override; - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - private: - // mate::TrackableObject: - void Destroy() override; - void ResolveProxy(const GURL& url, ResolveProxyCallback callback); void ClearCache(const net::CompletionCallback& callback); void ClearStorageData(mate::Arguments* args); diff --git a/atom/browser/api/atom_api_tray.cc b/atom/browser/api/atom_api_tray.cc index 3b1a3a6321bb..5f351f485049 100644 --- a/atom/browser/api/atom_api_tray.cc +++ b/atom/browser/api/atom_api_tray.cc @@ -94,14 +94,6 @@ void Tray::OnDragEnded() { Emit("drag-end"); } -bool Tray::IsDestroyed() const { - return !tray_icon_; -} - -void Tray::Destroy() { - tray_icon_.reset(); -} - void Tray::SetImage(mate::Arguments* args, const gfx::Image& image) { tray_icon_->SetImage(image); } @@ -162,8 +154,7 @@ v8::Local Tray::ModifiersToObject(v8::Isolate* isolate, void Tray::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("destroy", &Tray::Destroy, true) - .SetMethod("isDestroyed", &Tray::IsDestroyed, true) + .MakeDestroyable() .SetMethod("setImage", &Tray::SetImage) .SetMethod("setPressedImage", &Tray::SetPressedImage) .SetMethod("setToolTip", &Tray::SetToolTip) diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index d8d6dcead16c..0e0d153ad0d3 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -54,12 +54,6 @@ class Tray : public mate::TrackableObject, void OnDragExited() override; void OnDragEnded() override; - // mate::Wrappable: - bool IsDestroyed() const override; - - // mate::TrackableObject: - void Destroy() override; - void SetImage(mate::Arguments* args, const gfx::Image& image); void SetPressedImage(mate::Arguments* args, const gfx::Image& image); void SetToolTip(mate::Arguments* args, const std::string& tool_tip); diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index f377e8fda9e8..b16ab8c89960 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -194,8 +194,6 @@ namespace api { namespace { -v8::Persistent template_; - // The wrapWebContents function which is implemented in JavaScript using WrapWebContentsCallback = base::Callback)>; WrapWebContentsCallback g_wrap_web_contents; @@ -290,7 +288,15 @@ WebContents::WebContents(v8::Isolate* isolate, } WebContents::~WebContents() { - Destroy(); + if (type_ == WEB_VIEW && managed_web_contents()) { + // When force destroying the "destroyed" event is not emitted. + WebContentsDestroyed(); + + guest_delegate_->Destroy(); + + Observe(nullptr); + DestroyWebContents(); + } } bool WebContents::AddMessageToConsole(content::WebContents* source, @@ -591,19 +597,6 @@ void WebContents::NavigationEntryCommitted( details.is_in_page, details.did_replace_entry); } -void WebContents::Destroy() { - session_.Reset(); - if (type_ == WEB_VIEW && managed_web_contents()) { - // When force destroying the "destroyed" event is not emitted. - WebContentsDestroyed(); - - guest_delegate_->Destroy(); - - Observe(nullptr); - DestroyWebContents(); - } -} - int WebContents::GetID() const { return web_contents()->GetRenderProcessHost()->GetID(); } @@ -634,6 +627,15 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { web_contents()->GetController().LoadURLWithParams(params); } +void WebContents::DownloadURL(const GURL& url) { + auto browser_context = web_contents()->GetBrowserContext(); + auto download_manager = + content::BrowserContext::GetDownloadManager(browser_context); + + download_manager->DownloadUrl( + content::DownloadUrlParameters::FromWebContents(web_contents(), url)); +} + GURL WebContents::GetURL() const { return web_contents()->GetURL(); } @@ -987,82 +989,73 @@ v8::Local WebContents::DevToolsWebContents(v8::Isolate* isolate) { return v8::Local::New(isolate, devtools_web_contents_); } -mate::ObjectTemplateBuilder WebContents::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - if (template_.IsEmpty()) - template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) - .SetMethod("destroy", &WebContents::Destroy, true) - .SetMethod("isDestroyed", &WebContents::IsDestroyed, true) - .SetMethod("getId", &WebContents::GetID) - .SetMethod("equal", &WebContents::Equal) - .SetMethod("_loadURL", &WebContents::LoadURL) - .SetMethod("_getURL", &WebContents::GetURL) - .SetMethod("getTitle", &WebContents::GetTitle) - .SetMethod("isLoading", &WebContents::IsLoading) - .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) - .SetMethod("_stop", &WebContents::Stop) - .SetMethod("_goBack", &WebContents::GoBack) - .SetMethod("_goForward", &WebContents::GoForward) - .SetMethod("_goToOffset", &WebContents::GoToOffset) - .SetMethod("isCrashed", &WebContents::IsCrashed) - .SetMethod("setUserAgent", &WebContents::SetUserAgent) - .SetMethod("getUserAgent", &WebContents::GetUserAgent) - .SetMethod("insertCSS", &WebContents::InsertCSS) - .SetMethod("savePage", &WebContents::SavePage) - .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) - .SetMethod("openDevTools", &WebContents::OpenDevTools) - .SetMethod("closeDevTools", &WebContents::CloseDevTools) - .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened) - .SetMethod("enableDeviceEmulation", - &WebContents::EnableDeviceEmulation) - .SetMethod("disableDeviceEmulation", - &WebContents::DisableDeviceEmulation) - .SetMethod("toggleDevTools", &WebContents::ToggleDevTools) - .SetMethod("inspectElement", &WebContents::InspectElement) - .SetMethod("setAudioMuted", &WebContents::SetAudioMuted) - .SetMethod("isAudioMuted", &WebContents::IsAudioMuted) - .SetMethod("undo", &WebContents::Undo) - .SetMethod("redo", &WebContents::Redo) - .SetMethod("cut", &WebContents::Cut) - .SetMethod("copy", &WebContents::Copy) - .SetMethod("paste", &WebContents::Paste) - .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle) - .SetMethod("delete", &WebContents::Delete) - .SetMethod("selectAll", &WebContents::SelectAll) - .SetMethod("unselect", &WebContents::Unselect) - .SetMethod("replace", &WebContents::Replace) - .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling) - .SetMethod("focus", &WebContents::Focus) - .SetMethod("tabTraverse", &WebContents::TabTraverse) - .SetMethod("_send", &WebContents::SendIPCMessage, true) - .SetMethod("sendInputEvent", &WebContents::SendInputEvent) - .SetMethod("beginFrameSubscription", - &WebContents::BeginFrameSubscription) - .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) - .SetMethod("setSize", &WebContents::SetSize) - .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) - .SetMethod("isGuest", &WebContents::IsGuest) - .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) - .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) - .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) - .SetMethod("unregisterServiceWorker", - &WebContents::UnregisterServiceWorker) - .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker) - .SetMethod("print", &WebContents::Print) - .SetMethod("_printToPDF", &WebContents::PrintToPDF) - .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) - .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) - .SetProperty("session", &WebContents::Session, true) - .SetProperty("devToolsWebContents", - &WebContents::DevToolsWebContents, true) - .Build()); - - return mate::ObjectTemplateBuilder( - isolate, v8::Local::New(isolate, template_)); -} - -bool WebContents::IsDestroyed() const { - return !web_contents(); +// static +void WebContents::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .MakeDestroyable() + .SetMethod("getId", &WebContents::GetID) + .SetMethod("equal", &WebContents::Equal) + .SetMethod("_loadURL", &WebContents::LoadURL) + .SetMethod("downloadURL", &WebContents::DownloadURL) + .SetMethod("_getURL", &WebContents::GetURL) + .SetMethod("getTitle", &WebContents::GetTitle) + .SetMethod("isLoading", &WebContents::IsLoading) + .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse) + .SetMethod("_stop", &WebContents::Stop) + .SetMethod("_goBack", &WebContents::GoBack) + .SetMethod("_goForward", &WebContents::GoForward) + .SetMethod("_goToOffset", &WebContents::GoToOffset) + .SetMethod("isCrashed", &WebContents::IsCrashed) + .SetMethod("setUserAgent", &WebContents::SetUserAgent) + .SetMethod("getUserAgent", &WebContents::GetUserAgent) + .SetMethod("insertCSS", &WebContents::InsertCSS) + .SetMethod("savePage", &WebContents::SavePage) + .SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript) + .SetMethod("openDevTools", &WebContents::OpenDevTools) + .SetMethod("closeDevTools", &WebContents::CloseDevTools) + .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened) + .SetMethod("enableDeviceEmulation", + &WebContents::EnableDeviceEmulation) + .SetMethod("disableDeviceEmulation", + &WebContents::DisableDeviceEmulation) + .SetMethod("toggleDevTools", &WebContents::ToggleDevTools) + .SetMethod("inspectElement", &WebContents::InspectElement) + .SetMethod("setAudioMuted", &WebContents::SetAudioMuted) + .SetMethod("isAudioMuted", &WebContents::IsAudioMuted) + .SetMethod("undo", &WebContents::Undo) + .SetMethod("redo", &WebContents::Redo) + .SetMethod("cut", &WebContents::Cut) + .SetMethod("copy", &WebContents::Copy) + .SetMethod("paste", &WebContents::Paste) + .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle) + .SetMethod("delete", &WebContents::Delete) + .SetMethod("selectAll", &WebContents::SelectAll) + .SetMethod("unselect", &WebContents::Unselect) + .SetMethod("replace", &WebContents::Replace) + .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling) + .SetMethod("focus", &WebContents::Focus) + .SetMethod("tabTraverse", &WebContents::TabTraverse) + .SetMethod("_send", &WebContents::SendIPCMessage) + .SetMethod("sendInputEvent", &WebContents::SendInputEvent) + .SetMethod("beginFrameSubscription", + &WebContents::BeginFrameSubscription) + .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription) + .SetMethod("setSize", &WebContents::SetSize) + .SetMethod("setAllowTransparency", &WebContents::SetAllowTransparency) + .SetMethod("isGuest", &WebContents::IsGuest) + .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) + .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) + .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker) + .SetMethod("unregisterServiceWorker", + &WebContents::UnregisterServiceWorker) + .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker) + .SetMethod("print", &WebContents::Print) + .SetMethod("_printToPDF", &WebContents::PrintToPDF) + .SetMethod("addWorkSpace", &WebContents::AddWorkSpace) + .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace) + .SetProperty("session", &WebContents::Session) + .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents); } AtomBrowserContext* WebContents::GetBrowserContext() const { diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 568a563e2c84..fb8892f105b9 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -54,12 +54,10 @@ class WebContents : public mate::TrackableObject, static mate::Handle Create( v8::Isolate* isolate, const mate::Dictionary& options); - // mate::TrackableObject: - void Destroy() override; - int GetID() const; bool Equal(const WebContents* web_contents) const; void LoadURL(const GURL& url, const mate::Dictionary& options); + void DownloadURL(const GURL& url); GURL GetURL() const; base::string16 GetTitle() const; bool IsLoading() const; @@ -144,16 +142,15 @@ class WebContents : public mate::TrackableObject, v8::Local Session(v8::Isolate* isolate); v8::Local DevToolsWebContents(v8::Isolate* isolate); + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + protected: explicit WebContents(content::WebContents* web_contents); WebContents(v8::Isolate* isolate, const mate::Dictionary& options); ~WebContents(); - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - bool IsDestroyed() const override; - // content::WebContentsDelegate: bool AddMessageToConsole(content::WebContents* source, int32 level, diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 569640554468..79c91db3fa5d 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -157,8 +157,8 @@ Window::Window(v8::Isolate* isolate, const mate::Dictionary& options) { } Window::~Window() { - if (window_) - Destroy(); + if (!window_->IsClosed()) + window_->CloseContents(nullptr); } void Window::WillCloseWindow(bool* prevent_default) { @@ -166,19 +166,19 @@ void Window::WillCloseWindow(bool* prevent_default) { } void Window::OnWindowClosed() { - if (api_web_contents_) { - api_web_contents_->DestroyWebContents(); - api_web_contents_ = nullptr; - web_contents_.Reset(); - } + api_web_contents_->DestroyWebContents(); RemoveFromWeakMap(); window_->RemoveObserver(this); + // We can not call Destroy here because we need to call Emit first, but we + // also do not want any method to be used, so just mark as destroyed here. + MarkDestroyed(); + Emit("closed"); - // Clean up the resources after window has been closed. - base::MessageLoop::current()->DeleteSoon(FROM_HERE, window_.release()); + // Destroy the native class when window is closed. + base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure()); } void Window::OnWindowBlur() { @@ -276,15 +276,6 @@ mate::Wrappable* Window::New(v8::Isolate* isolate, mate::Arguments* args) { return new Window(isolate, options); } -bool Window::IsDestroyed() const { - return !window_ || window_->IsClosed(); -} - -void Window::Destroy() { - if (window_) - window_->CloseContents(nullptr); -} - void Window::Close() { window_->Close(); } @@ -622,8 +613,7 @@ v8::Local Window::WebContents(v8::Isolate* isolate) { void Window::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) - .SetMethod("destroy", &Window::Destroy, true) - .SetMethod("isDestroyed", &Window::IsDestroyed, true) + .MakeDestroyable() .SetMethod("close", &Window::Close) .SetMethod("focus", &Window::Focus) .SetMethod("isFocused", &Window::IsFocused) @@ -695,8 +685,8 @@ void Window::BuildPrototype(v8::Isolate* isolate, .SetMethod("showDefinitionForSelection", &Window::ShowDefinitionForSelection) #endif - .SetProperty("id", &Window::ID, true) - .SetProperty("webContents", &Window::WebContents, true); + .SetProperty("id", &Window::ID) + .SetProperty("webContents", &Window::WebContents); } // static diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index a04e00cfb48e..6d5ce22f4316 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -77,13 +77,7 @@ class Window : public mate::TrackableObject, void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override; #endif - // mate::Wrappable: - bool IsDestroyed() const override; - private: - // mate::TrackableObject: - void Destroy() override; - // APIs for NativeWindow. void Close(); void Focus(); diff --git a/atom/browser/api/trackable_object.cc b/atom/browser/api/trackable_object.cc index 50bfa59e6a7a..77a936cde02c 100644 --- a/atom/browser/api/trackable_object.cc +++ b/atom/browser/api/trackable_object.cc @@ -30,11 +30,11 @@ class IDUserData : public base::SupportsUserData::Data { TrackableObjectBase::TrackableObjectBase() : weak_map_id_(0), wrapped_(nullptr), weak_factory_(this) { - RegisterDestructionCallback( - base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr())); + cleanup_ = RegisterDestructionCallback(GetDestroyClosure()); } TrackableObjectBase::~TrackableObjectBase() { + cleanup_.Run(); } void TrackableObjectBase::AfterInit(v8::Isolate* isolate) { @@ -42,6 +42,18 @@ void TrackableObjectBase::AfterInit(v8::Isolate* isolate) { AttachAsUserData(wrapped_); } +void TrackableObjectBase::MarkDestroyed() { + GetWrapper(isolate())->SetAlignedPointerInInternalField(0, nullptr); +} + +base::Closure TrackableObjectBase::GetDestroyClosure() { + return base::Bind(&TrackableObjectBase::Destroy, weak_factory_.GetWeakPtr()); +} + +void TrackableObjectBase::Destroy() { + delete this; +} + void TrackableObjectBase::AttachAsUserData(base::SupportsUserData* wrapped) { if (weak_map_id_ != 0) { wrapped->SetUserData(kTrackedObjectKey, new IDUserData(weak_map_id_)); @@ -63,9 +75,9 @@ int32_t TrackableObjectBase::GetIDFromWrappedClass(base::SupportsUserData* w) { } // static -void TrackableObjectBase::RegisterDestructionCallback( - const base::Closure& closure) { - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(closure); +base::Closure TrackableObjectBase::RegisterDestructionCallback( + const base::Closure& c) { + return atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback(c); } } // namespace mate diff --git a/atom/browser/api/trackable_object.h b/atom/browser/api/trackable_object.h index 8ff7d1c04041..7c4ed03fe052 100644 --- a/atom/browser/api/trackable_object.h +++ b/atom/browser/api/trackable_object.h @@ -12,6 +12,7 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "native_mate/object_template_builder.h" namespace base { class SupportsUserData; @@ -30,26 +31,32 @@ class TrackableObjectBase : public mate::EventEmitter { // Wrap TrackableObject into a class that SupportsUserData. void AttachAsUserData(base::SupportsUserData* wrapped); - // Subclasses should implement this to destroy their native types. - virtual void Destroy() = 0; - protected: ~TrackableObjectBase() override; // mate::Wrappable: void AfterInit(v8::Isolate* isolate) override; + // Mark the JS object as destroyed. + void MarkDestroyed(); + + // Returns a closure that can destroy the native class. + base::Closure GetDestroyClosure(); + // Get the weak_map_id from SupportsUserData. static int32_t GetIDFromWrappedClass(base::SupportsUserData* wrapped); // Register a callback that should be destroyed before JavaScript environment // gets destroyed. - static void RegisterDestructionCallback(const base::Closure& closure); + static base::Closure RegisterDestructionCallback(const base::Closure& c); int32_t weak_map_id_; base::SupportsUserData* wrapped_; private: + void Destroy(); + + base::Closure cleanup_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(TrackableObjectBase); @@ -91,11 +98,6 @@ class TrackableObject : public TrackableObjectBase { return std::vector>(); } - TrackableObject() { - RegisterDestructionCallback( - base::Bind(&TrackableObject::ReleaseAllWeakReferences)); - } - // Removes this instance from the weak map. void RemoveFromWeakMap() { if (weak_map_ && weak_map_->Has(weak_map_id())) @@ -103,28 +105,49 @@ class TrackableObject : public TrackableObjectBase { } protected: + TrackableObject() {} ~TrackableObject() override { RemoveFromWeakMap(); } void AfterInit(v8::Isolate* isolate) override { - if (!weak_map_) + if (!weak_map_) { weak_map_.reset(new atom::IDWeakMap); + RegisterDestructionCallback( + base::Bind(&TrackableObject::ReleaseAllWeakReferences)); + } weak_map_id_ = weak_map_->Add(isolate, GetWrapper(isolate)); TrackableObjectBase::AfterInit(isolate); } private: + // mate::Wrappable: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + if (template_.IsEmpty()) { + auto templ = v8::ObjectTemplate::New(isolate); + T::BuildPrototype(isolate, templ); + template_.Reset(isolate, templ); + } + + return ObjectTemplateBuilder( + isolate, v8::Local::New(isolate, template_)); + } + // Releases all weak references in weak map, called when app is terminating. static void ReleaseAllWeakReferences() { weak_map_.reset(); } + static v8::Persistent template_; static scoped_ptr weak_map_; DISALLOW_COPY_AND_ASSIGN(TrackableObject); }; +template +v8::Persistent TrackableObject::template_; + template scoped_ptr TrackableObject::weak_map_; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 3bfe3748f136..fd72f5e4ae9a 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -25,6 +25,11 @@ namespace atom { +template +void Erase(T* container, typename T::iterator iter) { + container->erase(iter); +} + // static AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL; @@ -56,9 +61,10 @@ bool AtomBrowserMainParts::SetExitCode(int code) { return true; } -void AtomBrowserMainParts::RegisterDestructionCallback( +base::Closure AtomBrowserMainParts::RegisterDestructionCallback( const base::Closure& callback) { - destruction_callbacks_.push_back(callback); + auto iter = destructors_.insert(destructors_.end(), callback); + return base::Bind(&Erase>, &destructors_, iter); } void AtomBrowserMainParts::PreEarlyInitialization() { @@ -150,8 +156,13 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() { // Make sure destruction callbacks are called before message loop is // destroyed, otherwise some objects that need to be deleted on IO thread // won't be freed. - for (const auto& callback : destruction_callbacks_) + // We don't use ranged for loop because iterators are getting invalided when + // the callback runs. + for (auto iter = destructors_.begin(); iter != destructors_.end();) { + base::Closure& callback = *iter; + ++iter; callback.Run(); + } // Destroy JavaScript environment immediately after running destruction // callbacks. diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index bb4b204669fd..fbc59f7f811d 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -36,7 +36,8 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { // Register a callback that should be destroyed before JavaScript environment // gets destroyed. - void RegisterDestructionCallback(const base::Closure& callback); + // Returns a closure that can be used to remove |callback| from the list. + base::Closure RegisterDestructionCallback(const base::Closure& callback); Browser* browser() { return browser_.get(); } @@ -82,7 +83,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { base::Timer gc_timer_; // List of callbacks should be executed before destroying JS env. - std::list destruction_callbacks_; + std::list destructors_; static AtomBrowserMainParts* self_; diff --git a/atom/browser/lib/guest-window-manager.coffee b/atom/browser/lib/guest-window-manager.coffee index c311e01cf487..35ef7b89bb44 100644 --- a/atom/browser/lib/guest-window-manager.coffee +++ b/atom/browser/lib/guest-window-manager.coffee @@ -5,7 +5,7 @@ frameToGuest = {} # Copy attribute of |parent| to |child| if it is not defined in |child|. mergeOptions = (child, parent) -> - for own key, value of parent when key not in child + for own key, value of parent when key not in Object.keys child if typeof value is 'object' child[key] = mergeOptions {}, value else @@ -39,11 +39,12 @@ createGuest = (embedder, url, frameName, options) -> # When |embedder| is destroyed we should also destroy attached guest, and if # guest is closed by user then we should prevent |embedder| from double # closing guest. + guestId = guest.id closedByEmbedder = -> guest.removeListener 'closed', closedByUser guest.destroy() closedByUser = -> - embedder.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', guest.id + embedder.send 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED', guestId embedder.removeListener 'render-view-deleted', closedByEmbedder embedder.once 'render-view-deleted', closedByEmbedder guest.once 'closed', closedByUser diff --git a/atom/common/api/atom_api_asar.cc b/atom/common/api/atom_api_asar.cc index 4ea7d8c5c362..7aee71fc3294 100644 --- a/atom/common/api/atom_api_asar.cc +++ b/atom/common/api/atom_api_asar.cc @@ -18,6 +18,8 @@ namespace { +v8::Persistent template_; + class Archive : public mate::Wrappable { public: static v8::Local Create(v8::Isolate* isolate, @@ -101,15 +103,20 @@ class Archive : public mate::Wrappable { // mate::Wrappable: mate::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetValue("path", archive_->path()) - .SetMethod("getFileInfo", &Archive::GetFileInfo) - .SetMethod("stat", &Archive::Stat) - .SetMethod("readdir", &Archive::Readdir) - .SetMethod("realpath", &Archive::Realpath) - .SetMethod("copyFileOut", &Archive::CopyFileOut) - .SetMethod("getFd", &Archive::GetFD) - .SetMethod("destroy", &Archive::Destroy); + if (template_.IsEmpty()) + template_.Reset(isolate, mate::ObjectTemplateBuilder(isolate) + .SetValue("path", archive_->path()) + .SetMethod("getFileInfo", &Archive::GetFileInfo) + .SetMethod("stat", &Archive::Stat) + .SetMethod("readdir", &Archive::Readdir) + .SetMethod("realpath", &Archive::Realpath) + .SetMethod("copyFileOut", &Archive::CopyFileOut) + .SetMethod("getFd", &Archive::GetFD) + .SetMethod("destroy", &Archive::Destroy) + .Build()); + + return mate::ObjectTemplateBuilder( + isolate, v8::Local::New(isolate, template_)); } private: diff --git a/atom/common/native_mate_converters/callback.cc b/atom/common/native_mate_converters/callback.cc index 87faa3df3cd5..8bf5c459b0a2 100644 --- a/atom/common/native_mate_converters/callback.cc +++ b/atom/common/native_mate_converters/callback.cc @@ -4,7 +4,9 @@ #include "atom/common/native_mate_converters/callback.h" -#include "atom/browser/atom_browser_main_parts.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; namespace mate { @@ -56,31 +58,59 @@ v8::Local BindFunctionWith(v8::Isolate* isolate, } // namespace +// Destroy the class on UI thread when possible. +struct DeleteOnUIThread { + template + static void Destruct(const T* x) { + if (Locker::IsBrowserProcess() && + !BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, x); + } else { + delete x; + } + } +}; + +// Like v8::Global, but ref-counted. +template +class RefCountedGlobal : public base::RefCountedThreadSafe, + DeleteOnUIThread> { + public: + RefCountedGlobal(v8::Isolate* isolate, v8::Local value) + : handle_(isolate, v8::Local::Cast(value)) { + } + + bool IsAlive() const { + return !handle_.IsEmpty(); + } + + v8::Local NewHandle(v8::Isolate* isolate) const { + return v8::Local::New(isolate, handle_); + } + + private: + v8::Global handle_; + + DISALLOW_COPY_AND_ASSIGN(RefCountedGlobal); +}; + SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local value) - : v8_function_(new RefCountedPersistent(isolate, value)), - weak_factory_(this) { - Init(); + : v8_function_(new RefCountedGlobal(isolate, value)) { } SafeV8Function::SafeV8Function(const SafeV8Function& other) - : v8_function_(other.v8_function_), - weak_factory_(this) { - Init(); + : v8_function_(other.v8_function_) { } -v8::Local SafeV8Function::NewHandle() const { - return v8_function_->NewHandle(); +SafeV8Function::~SafeV8Function() { } -void SafeV8Function::Init() { - if (Locker::IsBrowserProcess() && atom::AtomBrowserMainParts::Get()) - atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( - base::Bind(&SafeV8Function::FreeHandle, weak_factory_.GetWeakPtr())); +bool SafeV8Function::IsAlive() const { + return v8_function_.get() && v8_function_->IsAlive(); } -void SafeV8Function::FreeHandle() { - Locker locker(v8_function_->isolate()); - v8_function_ = nullptr; +v8::Local SafeV8Function::NewHandle(v8::Isolate* isolate) const { + return v8_function_->NewHandle(isolate); } v8::Local CreateFunctionFromTranslater( diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 5dd9d3cec9b1..3cba2b32a820 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -19,23 +19,21 @@ namespace mate { namespace internal { -// Manages the V8 function with RAII, and automatically cleans the handle when -// JavaScript context is destroyed, even when the class is not destroyed. +template +class RefCountedGlobal; + +// Manages the V8 function with RAII. class SafeV8Function { public: SafeV8Function(v8::Isolate* isolate, v8::Local value); SafeV8Function(const SafeV8Function& other); + ~SafeV8Function(); - bool is_alive() const { return v8_function_.get(); } - - v8::Local NewHandle() const; + bool IsAlive() const; + v8::Local NewHandle(v8::Isolate* isolate) const; private: - void Init(); - void FreeHandle(); - - scoped_refptr> v8_function_; - base::WeakPtrFactory weak_factory_; + scoped_refptr> v8_function_; }; // Helper to invoke a V8 function with C++ parameters. @@ -49,12 +47,12 @@ struct V8FunctionInvoker(ArgTypes...)> { ArgTypes... raw) { Locker locker(isolate); v8::EscapableHandleScope handle_scope(isolate); - if (!function.is_alive()) + if (!function.IsAlive()) return v8::Null(isolate); scoped_ptr script_scope( Locker::IsBrowserProcess() ? nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -70,12 +68,12 @@ struct V8FunctionInvoker { ArgTypes... raw) { Locker locker(isolate); v8::HandleScope handle_scope(isolate); - if (!function.is_alive()) + if (!function.IsAlive()) return; scoped_ptr script_scope( Locker::IsBrowserProcess() ? nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -91,12 +89,12 @@ struct V8FunctionInvoker { Locker locker(isolate); v8::HandleScope handle_scope(isolate); ReturnType ret = ReturnType(); - if (!function.is_alive()) + if (!function.IsAlive()) return ret; scoped_ptr script_scope( Locker::IsBrowserProcess() ? nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function.NewHandle(); + v8::Local holder = function.NewHandle(isolate); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; diff --git a/atom/renderer/lib/web-view/web-view.coffee b/atom/renderer/lib/web-view/web-view.coffee index 720e6e84c5d4..da36886f6ffa 100644 --- a/atom/renderer/lib/web-view/web-view.coffee +++ b/atom/renderer/lib/web-view/web-view.coffee @@ -288,6 +288,7 @@ registerWebViewElement = -> 'replace' 'replaceMisspelling' 'getId' + 'downloadURL' 'inspectServiceWorker' 'print' 'printToPDF' diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index e976b0ddb814..c35bbc9ae33c 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -242,6 +242,13 @@ const options = {"extraHeaders" : "pragma: no-cache\n"} webContents.loadURL(url, options) ``` +### `webContents.downloadURL(url)` + +* `url` URL + +Initiates a download of the resource at `url` without navigating. The +`will-download` event of `session` will be triggered. + ### `webContents.getURL()` Returns URL of the current web page. diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 56909a52e30f..e317ef8eab1e 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -164,6 +164,7 @@ The `webview` tag has the following methods: **Note:** The webview element must be loaded before using the methods. **Example** + ```javascript webview.addEventListener("dom-ready", function() { webview.openDevTools(); diff --git a/spec/api-browser-window-spec.coffee b/spec/api-browser-window-spec.coffee index 8df4c9a9479e..86beb1fdc600 100644 --- a/spec/api-browser-window-spec.coffee +++ b/spec/api-browser-window-spec.coffee @@ -262,6 +262,7 @@ describe 'browser-window module', -> w.loadURL "file://#{fixtures}/pages/window-open.html" it 'emits when link with target is called', (done) -> + @timeout 10000 w.webContents.once 'new-window', (e, url, frameName) -> e.preventDefault() assert.equal url, 'http://host/' diff --git a/spec/api-session-spec.coffee b/spec/api-session-spec.coffee index 6d0a8ac167c0..98e47e357a58 100644 --- a/spec/api-session-spec.coffee +++ b/spec/api-session-spec.coffee @@ -87,23 +87,43 @@ describe 'session module', -> res.end mockPDF downloadServer.close() - it 'can download successfully', (done) -> + assertDownload = (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port) -> + assert.equal state, 'completed' + assert.equal filename, 'mock.pdf' + assert.equal url, "http://127.0.0.1:#{port}/" + assert.equal mimeType, 'application/pdf' + assert.equal receivedBytes, mockPDF.length + assert.equal totalBytes, mockPDF.length + assert.equal disposition, contentDisposition + assert fs.existsSync downloadFilePath + fs.unlinkSync downloadFilePath + + it 'can download using BrowserWindow.loadURL', (done) -> downloadServer.listen 0, '127.0.0.1', -> {port} = downloadServer.address() ipcRenderer.sendSync 'set-download-option', false w.loadURL "#{url}:#{port}" ipcRenderer.once 'download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) -> - assert.equal state, 'completed' - assert.equal filename, 'mock.pdf' - assert.equal url, "http://127.0.0.1:#{port}/" - assert.equal mimeType, 'application/pdf' - assert.equal receivedBytes, mockPDF.length - assert.equal totalBytes, mockPDF.length - assert.equal disposition, contentDisposition - assert fs.existsSync downloadFilePath - fs.unlinkSync downloadFilePath + assertDownload event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port done() + it 'can download using WebView.downloadURL', (done) -> + downloadServer.listen 0, '127.0.0.1', -> + {port} = downloadServer.address() + ipcRenderer.sendSync 'set-download-option', false + + webview = new WebView + webview.src = "file://#{fixtures}/api/blank.html" + webview.addEventListener 'did-finish-load', -> + webview.downloadURL "#{url}:#{port}/" + + ipcRenderer.once 'download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) -> + assertDownload event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port + document.body.removeChild(webview) + done() + + document.body.appendChild webview + it 'can cancel download', (done) -> downloadServer.listen 0, '127.0.0.1', -> {port} = downloadServer.address() diff --git a/spec/chromium-spec.coffee b/spec/chromium-spec.coffee index 435f7bbbe3e5..211de346846b 100644 --- a/spec/chromium-spec.coffee +++ b/spec/chromium-spec.coffee @@ -75,13 +75,22 @@ describe 'chromium feature', -> it 'inherit options of parent window', (done) -> listener = (event) -> - size = remote.getCurrentWindow().getSize() - assert.equal event.data, "size: #{size.width} #{size.height}" + [width, height] = remote.getCurrentWindow().getSize() + assert.equal event.data, "size: #{width} #{height}" b.close() done() window.addEventListener 'message', listener b = window.open "file://#{fixtures}/pages/window-open-size.html", '', 'show=no' + it 'does not override child options', (done) -> + size = {width: 350, height: 450} + listener = (event) -> + assert.equal event.data, "size: #{size.width} #{size.height}" + b.close() + done() + window.addEventListener 'message', listener + b = window.open "file://#{fixtures}/pages/window-open-size.html", '', "show=no,width=#{size.width},height=#{size.height}" + describe 'window.opener', -> @timeout 10000 diff --git a/spec/fixtures/pages/window-open-size.html b/spec/fixtures/pages/window-open-size.html index b32e39889a4c..93b5039f7966 100644 --- a/spec/fixtures/pages/window-open-size.html +++ b/spec/fixtures/pages/window-open-size.html @@ -2,7 +2,7 @@ diff --git a/vendor/native_mate b/vendor/native_mate index 93984941005b..e859228db163 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit 93984941005bab194a2d47aff655d525c064efcb +Subproject commit e859228db163c27410fb200c2df0715478fdf0d7