diff --git a/README.md b/README.md index 8fb9128d9c10..beb96b4e5c2a 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ :zap: *Formerly known as Atom Shell* :zap: The Electron framework lets you write cross-platform desktop applications -using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org) and +using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) and [Chromium](http://www.chromium.org) and is used in the [Atom 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). +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. @@ -62,7 +62,7 @@ repository to see a minimal Electron app in action. You can ask questions and interact with the community in the following locations: -- [`electron`](http://discuss.atom.io/category/electron) category on the Atom +- [`electron`](http://discuss.atom.io/c/electron) category on the Atom forums - `#atom-shell` channel on Freenode - [`Atom`](http://atom-slack.herokuapp.com/) channel on Slack diff --git a/atom.gyp b/atom.gyp index 7b88df4d8013..a57b7dbf58f8 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.34.2', + 'version%': '0.34.3', }, 'includes': [ 'filenames.gypi', diff --git a/atom/app/atom_main_delegate.cc b/atom/app/atom_main_delegate.cc index e4ac7cfc0a99..3bc1ac497082 100644 --- a/atom/app/atom_main_delegate.cc +++ b/atom/app/atom_main_delegate.cc @@ -102,13 +102,6 @@ void AtomMainDelegate::PreSandboxStartup() { if (!IsBrowserProcess(command_line)) return; -#if defined(OS_WIN) - // Disable the LegacyRenderWidgetHostHWND, it made frameless windows unable - // to move and resize. We may consider enabling it again after upgraded to - // Chrome 38, which should have fixed the problem. - command_line->AppendSwitch(switches::kDisableLegacyIntermediateWindow); -#endif - // Disable renderer sandbox for most of node's functions. command_line->AppendSwitch(switches::kNoSandbox); diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 04ed06e0138f..1c5c2c04f145 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -339,6 +339,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder( auto browser = base::Unretained(Browser::Get()); return mate::ObjectTemplateBuilder(isolate) .SetMethod("quit", base::Bind(&Browser::Quit, browser)) + .SetMethod("exit", base::Bind(&Browser::Exit, browser)) .SetMethod("focus", base::Bind(&Browser::Focus, browser)) .SetMethod("getVersion", base::Bind(&Browser::GetVersion, browser)) .SetMethod("setVersion", base::Bind(&Browser::SetVersion, browser)) diff --git a/atom/browser/api/atom_api_download_item.cc b/atom/browser/api/atom_api_download_item.cc index ec4dcd84b285..691cfbfef594 100644 --- a/atom/browser/api/atom_api_download_item.cc +++ b/atom/browser/api/atom_api_download_item.cc @@ -6,6 +6,7 @@ #include +#include "atom/browser/atom_browser_main_parts.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/gurl_converter.h" @@ -159,14 +160,6 @@ mate::ObjectTemplateBuilder DownloadItem::GetObjectTemplateBuilder( .SetMethod("setSavePath", &DownloadItem::SetSavePath); } -void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) { - g_wrap_download_item = callback; -} - -void ClearWrapDownloadItem() { - g_wrap_download_item.Reset(); -} - // static mate::Handle DownloadItem::Create( v8::Isolate* isolate, content::DownloadItem* item) { @@ -182,6 +175,18 @@ void* DownloadItem::UserDataKey() { return &kDownloadItemSavePathKey; } +void ClearWrapDownloadItem() { + g_wrap_download_item.Reset(); +} + +void SetWrapDownloadItem(const WrapDownloadItemCallback& callback) { + g_wrap_download_item = callback; + + // Cleanup the wrapper on exit. + atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( + base::Bind(ClearWrapDownloadItem)); +} + } // namespace api } // namespace atom @@ -193,7 +198,6 @@ void Initialize(v8::Local exports, v8::Local unused, v8::Isolate* isolate = context->GetIsolate(); mate::Dictionary dict(isolate, exports); dict.SetMethod("_setWrapDownloadItem", &atom::api::SetWrapDownloadItem); - dict.SetMethod("_clearWrapDownloadItem", &atom::api::ClearWrapDownloadItem); } } // namespace diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc index f5a03e4abf90..6ab4fa4b6186 100644 --- a/atom/browser/api/atom_api_global_shortcut.cc +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -23,6 +23,9 @@ 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 15cd3d4e0edb..93eb7853ae0d 100644 --- a/atom/browser/api/atom_api_global_shortcut.h +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -8,9 +8,9 @@ #include #include +#include "atom/browser/api/trackable_object.h" #include "base/callback.h" #include "chrome/browser/extensions/global_shortcut_listener.h" -#include "native_mate/wrappable.h" #include "native_mate/handle.h" #include "ui/base/accelerators/accelerator.h" @@ -19,13 +19,16 @@ namespace atom { namespace api { class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, - public mate::Wrappable { + public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate); protected: GlobalShortcut(); - virtual ~GlobalShortcut(); + ~GlobalShortcut() override; + + // mate::TrackableObject: + void Destroy() override; // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 9bd724a9612e..1f16a428da01 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -27,6 +27,14 @@ 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; diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 0d93c0d46be6..545dd18e386c 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -8,16 +8,16 @@ #include #include "atom/browser/api/atom_api_window.h" +#include "atom/browser/api/trackable_object.h" #include "atom/browser/ui/atom_menu_model.h" #include "base/callback.h" #include "base/memory/scoped_ptr.h" -#include "native_mate/wrappable.h" namespace atom { namespace api { -class Menu : public mate::Wrappable, +class Menu : public mate::TrackableObject, public AtomMenuModel::Delegate { public: static mate::Wrappable* Create(); @@ -37,9 +37,13 @@ class Menu : public mate::Wrappable, protected: Menu(); - virtual ~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 5a086776a639..baa2aff349e1 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -19,6 +19,7 @@ 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 071753218d6a..5936e0439f4f 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -18,6 +18,11 @@ 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 31b35e10cea8..eeb9475c2847 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -19,6 +19,9 @@ 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 dcf0906b437f..9303b3ab288f 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -5,7 +5,7 @@ #ifndef ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_ #define ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_ -#include "atom/browser/api/event_emitter.h" +#include "atom/browser/api/trackable_object.h" #include "base/compiler_specific.h" #include "base/power_monitor/power_observer.h" #include "native_mate/handle.h" @@ -14,14 +14,17 @@ namespace atom { namespace api { -class PowerMonitor : public mate::EventEmitter, +class PowerMonitor : public mate::TrackableObject, public base::PowerObserver { public: static v8::Local Create(v8::Isolate* isolate); protected: PowerMonitor(); - virtual ~PowerMonitor(); + ~PowerMonitor() override; + + // mate::TrackableObject: + void Destroy() override; // base::PowerObserver implementations: void OnPowerStateChange(bool on_battery_power) override; diff --git a/atom/browser/api/atom_api_power_save_blocker.cc b/atom/browser/api/atom_api_power_save_blocker.cc index 58983e6c846a..f77979ae417f 100644 --- a/atom/browser/api/atom_api_power_save_blocker.cc +++ b/atom/browser/api/atom_api_power_save_blocker.cc @@ -45,6 +45,11 @@ 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 9861f2b0f7cd..e7ce97878ffb 100644 --- a/atom/browser/api/atom_api_power_save_blocker.h +++ b/atom/browser/api/atom_api_power_save_blocker.h @@ -7,10 +7,10 @@ #include +#include "atom/browser/api/trackable_object.h" #include "base/memory/scoped_ptr.h" #include "content/public/browser/power_save_blocker.h" #include "native_mate/handle.h" -#include "native_mate/wrappable.h" namespace mate { class Dictionary; @@ -20,13 +20,16 @@ namespace atom { namespace api { -class PowerSaveBlocker : public mate::Wrappable { +class PowerSaveBlocker : public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate); protected: PowerSaveBlocker(); - virtual ~PowerSaveBlocker(); + ~PowerSaveBlocker() override; + + // mate::TrackableObject: + void Destroy() override; // mate::Wrappable implementations: mate::ObjectTemplateBuilder GetObjectTemplateBuilder( @@ -48,7 +51,6 @@ class PowerSaveBlocker : public mate::Wrappable { std::map; PowerSaveBlockerTypeMap power_save_blocker_types_; - DISALLOW_COPY_AND_ASSIGN(PowerSaveBlocker); }; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 5bb96710f226..0ec9c05ed84e 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -9,9 +9,10 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_download_item.h" -#include "atom/browser/atom_browser_context.h" #include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/api/save_page_handler.h" +#include "atom/browser/atom_browser_context.h" +#include "atom/browser/atom_browser_main_parts.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" @@ -395,14 +396,18 @@ mate::Handle Session::FromPartition( static_cast(browser_context.get())); } -void SetWrapSession(const WrapSessionCallback& callback) { - g_wrap_session = callback; -} - void ClearWrapSession() { g_wrap_session.Reset(); } +void SetWrapSession(const WrapSessionCallback& callback) { + g_wrap_session = callback; + + // Cleanup the wrapper on exit. + atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( + base::Bind(ClearWrapSession)); +} + } // namespace api } // namespace atom @@ -415,7 +420,6 @@ void Initialize(v8::Local exports, v8::Local unused, mate::Dictionary dict(isolate, exports); dict.SetMethod("fromPartition", &atom::api::Session::FromPartition); dict.SetMethod("_setWrapSession", &atom::api::SetWrapSession); - dict.SetMethod("_clearWrapSession", &atom::api::ClearWrapSession); } } // namespace diff --git a/atom/browser/api/atom_api_tray.h b/atom/browser/api/atom_api_tray.h index dc9302597cf3..9a423f61764f 100644 --- a/atom/browser/api/atom_api_tray.h +++ b/atom/browser/api/atom_api_tray.h @@ -8,7 +8,7 @@ #include #include -#include "atom/browser/api/event_emitter.h" +#include "atom/browser/api/trackable_object.h" #include "atom/browser/ui/tray_icon_observer.h" #include "base/memory/scoped_ptr.h" @@ -29,7 +29,7 @@ namespace api { class Menu; -class Tray : public mate::EventEmitter, +class Tray : public mate::TrackableObject, public TrayIconObserver { public: static mate::Wrappable* New(v8::Isolate* isolate, const gfx::Image& image); @@ -39,7 +39,7 @@ class Tray : public mate::EventEmitter, protected: explicit Tray(const gfx::Image& image); - virtual ~Tray(); + ~Tray() override; // TrayIconObserver: void OnClicked(const gfx::Rect& bounds, int modifiers) override; @@ -53,7 +53,9 @@ class Tray : public mate::EventEmitter, // mate::Wrappable: bool IsDestroyed() const override; - void Destroy(); + // 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 c329b257a290..5165877d1670 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1124,14 +1124,18 @@ mate::Handle WebContents::Create( return handle; } -void SetWrapWebContents(const WrapWebContentsCallback& callback) { - g_wrap_web_contents = callback; -} - void ClearWrapWebContents() { g_wrap_web_contents.Reset(); } +void SetWrapWebContents(const WrapWebContentsCallback& callback) { + g_wrap_web_contents = callback; + + // Cleanup the wrapper on exit. + atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( + base::Bind(ClearWrapWebContents)); +} + } // namespace api } // namespace atom @@ -1145,7 +1149,6 @@ void Initialize(v8::Local exports, v8::Local unused, mate::Dictionary dict(isolate, exports); dict.SetMethod("create", &atom::api::WebContents::Create); dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents); - dict.SetMethod("_clearWrapWebContents", &atom::api::ClearWrapWebContents); } } // namespace diff --git a/atom/browser/api/lib/app.coffee b/atom/browser/api/lib/app.coffee index 18c80dc2b1f4..4df7aef10bdf 100644 --- a/atom/browser/api/lib/app.coffee +++ b/atom/browser/api/lib/app.coffee @@ -11,14 +11,14 @@ wrapSession = (session) -> # session is an Event Emitter. session.__proto__ = EventEmitter.prototype -wrapDownloadItem = (download_item) -> - # download_item is an Event Emitter. - download_item.__proto__ = EventEmitter.prototype +wrapDownloadItem = (downloadItem) -> + # downloadItem is an Event Emitter. + downloadItem.__proto__ = EventEmitter.prototype # Be compatible with old APIs. - download_item.url = download_item.getUrl() - download_item.filename = download_item.getFilename() - download_item.mimeType = download_item.getMimeType() - download_item.hasUserGesture = download_item.hasUserGesture() + downloadItem.url = downloadItem.getUrl() + downloadItem.filename = downloadItem.getFilename() + downloadItem.mimeType = downloadItem.getMimeType() + downloadItem.hasUserGesture = downloadItem.hasUserGesture() app.setApplicationMenu = (menu) -> require('menu').setApplicationMenu menu @@ -50,19 +50,15 @@ app.getAppPath = -> # Be compatible with old API. app.once 'ready', -> @emit 'finish-launching' app.terminate = app.quit -app.exit = process.exit app.getHomeDir = -> @getPath 'home' app.getDataPath = -> @getPath 'userData' app.setDataPath = (path) -> @setPath 'userData', path app.resolveProxy = -> @defaultSession.resolveProxy.apply @defaultSession, arguments app.on 'activate', (event, hasVisibleWindows) -> @emit 'activate-with-no-open-windows' if not hasVisibleWindows -# Session wrapper. +# Wrappers for native classes. sessionBindings._setWrapSession wrapSession -process.once 'exit', sessionBindings._clearWrapSession - downloadItemBindings._setWrapDownloadItem wrapDownloadItem -process.once 'exit', downloadItemBindings._clearWrapDownloadItem # Only one App object pemitted. module.exports = app diff --git a/atom/browser/api/lib/web-contents.coffee b/atom/browser/api/lib/web-contents.coffee index eab9f1967158..331a561189d7 100644 --- a/atom/browser/api/lib/web-contents.coffee +++ b/atom/browser/api/lib/web-contents.coffee @@ -112,7 +112,6 @@ wrapWebContents = (webContents) -> @_printToPDF printingSetting, callback binding._setWrapWebContents wrapWebContents -process.once 'exit', binding._clearWrapWebContents module.exports.create = (options={}) -> binding.create(options) diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 5fae5bfdbedd..0a8c16ca223f 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -30,6 +30,7 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL; AtomBrowserMainParts::AtomBrowserMainParts() : fake_browser_process_(new BrowserProcess), + exit_code_(nullptr), browser_(new Browser), node_bindings_(NodeBindings::Create(true)), atom_bindings_(new AtomBindings), @@ -47,6 +48,14 @@ AtomBrowserMainParts* AtomBrowserMainParts::Get() { return self_; } +bool AtomBrowserMainParts::SetExitCode(int code) { + if (!exit_code_) + return false; + + *exit_code_ = code; + return true; +} + void AtomBrowserMainParts::RegisterDestructionCallback( const base::Closure& callback) { destruction_callbacks_.push_back(callback); @@ -118,6 +127,11 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { #endif } +bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) { + exit_code_ = result_code; + return brightray::BrowserMainParts::MainMessageLoopRun(result_code); +} + void AtomBrowserMainParts::PostMainMessageLoopStart() { brightray::BrowserMainParts::PostMainMessageLoopStart(); #if defined(OS_POSIX) @@ -128,11 +142,23 @@ void AtomBrowserMainParts::PostMainMessageLoopStart() { void AtomBrowserMainParts::PostMainMessageLoopRun() { brightray::BrowserMainParts::PostMainMessageLoopRun(); +#if defined(OS_MACOSX) + FreeAppDelegate(); +#endif + // 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_) callback.Run(); + + // Destroy JavaScript environment immediately after running destruction + // callbacks. + gc_timer_.Stop(); + node_debugger_.reset(); + atom_bindings_.reset(); + node_bindings_.reset(); + js_env_.reset(); } } // namespace atom diff --git a/atom/browser/atom_browser_main_parts.h b/atom/browser/atom_browser_main_parts.h index 65b142157dc1..bb4b204669fd 100644 --- a/atom/browser/atom_browser_main_parts.h +++ b/atom/browser/atom_browser_main_parts.h @@ -31,6 +31,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { static AtomBrowserMainParts* Get(); + // Sets the exit code, will fail if the the message loop is not ready. + bool SetExitCode(int code); + // Register a callback that should be destroyed before JavaScript environment // gets destroyed. void RegisterDestructionCallback(const base::Closure& callback); @@ -42,11 +45,11 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { void PreEarlyInitialization() override; void PostEarlyInitialization() override; void PreMainMessageLoopRun() override; + bool MainMessageLoopRun(int* result_code) override; void PostMainMessageLoopStart() override; void PostMainMessageLoopRun() override; #if defined(OS_MACOSX) void PreMainMessageLoopStart() override; - void PostDestroyThreads() override; #endif private: @@ -56,6 +59,10 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { void HandleShutdownSignals(); #endif +#if defined(OS_MACOSX) + void FreeAppDelegate(); +#endif + // A fake BrowserProcess object that used to feed the source code from chrome. scoped_ptr fake_browser_process_; @@ -63,6 +70,9 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts { // with a task runner that will post all work to main loop. scoped_refptr bridge_task_runner_; + // Pointer to exit code. + int* exit_code_; + scoped_ptr browser_; scoped_ptr js_env_; scoped_ptr node_bindings_; diff --git a/atom/browser/atom_browser_main_parts_mac.mm b/atom/browser/atom_browser_main_parts_mac.mm index 1de07dfc0e44..42e3100f490e 100644 --- a/atom/browser/atom_browser_main_parts_mac.mm +++ b/atom/browser/atom_browser_main_parts_mac.mm @@ -34,7 +34,7 @@ void AtomBrowserMainParts::PreMainMessageLoopStart() { setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"]; } -void AtomBrowserMainParts::PostDestroyThreads() { +void AtomBrowserMainParts::FreeAppDelegate() { [[NSApp delegate] release]; [NSApp setDelegate:nil]; } diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index e80cb4e60e20..57741786520d 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -7,6 +7,7 @@ #include #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/native_window.h" #include "atom/browser/window_list.h" #include "base/message_loop/message_loop.h" #include "content/public/browser/client_certificate_delegate.h" @@ -45,6 +46,27 @@ void Browser::Quit() { window_list->CloseAllWindows(); } +void Browser::Exit(int code) { + if (!AtomBrowserMainParts::Get()->SetExitCode(code)) { + // Message loop is not ready, quit directly. + exit(code); + } else { + // Prepare to quit when all windows have been closed.. + is_quiting_ = true; + + // Must destroy windows before quitting, otherwise bad things can happen. + atom::WindowList* window_list = atom::WindowList::GetInstance(); + if (window_list->size() == 0) { + NotifyAndShutdown(); + } else { + // Unlike Quit(), we do not ask to close window, but destroy the window + // without asking. + for (NativeWindow* window : *window_list) + window->CloseContents(nullptr); // e.g. Destroy() + } + } +} + void Browser::Shutdown() { if (is_shutdown_) return; diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 447a526de6df..e20db080b67a 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -42,6 +42,9 @@ class Browser : public WindowListObserver { // Try to close all windows and quit the application. void Quit(); + // Exit the application immediately and set exit code. + void Exit(int code); + // Cleanup everything and shutdown the application gracefully. void Shutdown(); diff --git a/atom/browser/lib/init.coffee b/atom/browser/lib/init.coffee index 9f92d700c73a..80d2da31b705 100644 --- a/atom/browser/lib/init.coffee +++ b/atom/browser/lib/init.coffee @@ -53,6 +53,9 @@ app = require 'app' app.on 'quit', -> process.emit 'exit' +# Map process.exit to app.exit, which quits gracefully. +process.exit = app.exit + # Load the RPC server. require './rpc-server' diff --git a/atom/browser/native_window_views_win.cc b/atom/browser/native_window_views_win.cc index d49683acb3cf..02ebd2ef32fc 100644 --- a/atom/browser/native_window_views_win.cc +++ b/atom/browser/native_window_views_win.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "atom/browser/native_window_views.h" +#include "content/public/browser/browser_accessibility_state.h" namespace atom { @@ -83,6 +84,20 @@ bool NativeWindowViews::PreHandleMSG( NotifyWindowMessage(message, w_param, l_param); switch (message) { + // Screen readers send WM_GETOBJECT in order to get the accessibility + // object, so take this opportunity to push Chromium into accessible + // mode if it isn't already, always say we didn't handle the message + // because we still want Chromium to handle returning the actual + // accessibility object. + case WM_GETOBJECT: { + const DWORD obj_id = static_cast(l_param); + if (obj_id == OBJID_CLIENT) { + const auto axState = content::BrowserAccessibilityState::GetInstance(); + if (axState && !axState->IsAccessibleBrowser()) + axState->OnScreenReaderDetected(); + } + return false; + } case WM_COMMAND: // Handle thumbar button click message. if (HIWORD(w_param) == THBN_CLICKED) diff --git a/atom/browser/resources/mac/Info.plist b/atom/browser/resources/mac/Info.plist index a703c01a766c..a7df1a582e08 100644 --- a/atom/browser/resources/mac/Info.plist +++ b/atom/browser/resources/mac/Info.plist @@ -17,9 +17,9 @@ CFBundleIconFile atom.icns CFBundleVersion - 0.34.2 + 0.34.3 CFBundleShortVersionString - 0.34.2 + 0.34.3 LSApplicationCategoryType public.app-category.developer-tools LSMinimumSystemVersion diff --git a/atom/browser/resources/win/atom.rc b/atom/browser/resources/win/atom.rc index 8401e1d7d09e..d3ebfaa8e0d9 100644 --- a/atom/browser/resources/win/atom.rc +++ b/atom/browser/resources/win/atom.rc @@ -56,8 +56,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,34,2,0 - PRODUCTVERSION 0,34,2,0 + FILEVERSION 0,34,3,0 + PRODUCTVERSION 0,34,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -74,12 +74,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "0.34.2" + VALUE "FileVersion", "0.34.3" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "0.34.2" + VALUE "ProductVersion", "0.34.3" VALUE "SquirrelAwareVersion", "1" END END diff --git a/atom/browser/ui/win/notify_icon.cc b/atom/browser/ui/win/notify_icon.cc index c88d4c810ef8..b2ca4bceedd1 100644 --- a/atom/browser/ui/win/notify_icon.cc +++ b/atom/browser/ui/win/notify_icon.cc @@ -113,7 +113,7 @@ void NotifyIcon::SetToolTip(const std::string& tool_tip) { NOTIFYICONDATA icon_data; InitIconData(&icon_data); icon_data.uFlags |= NIF_TIP; - wcscpy_s(icon_data.szTip, base::UTF8ToUTF16(tool_tip).c_str()); + wcsncpy_s(icon_data.szTip, base::UTF8ToUTF16(tool_tip).c_str(), _TRUNCATE); BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); if (!result) LOG(WARNING) << "Unable to set tooltip for status tray icon"; @@ -126,8 +126,8 @@ void NotifyIcon::DisplayBalloon(const gfx::Image& icon, InitIconData(&icon_data); icon_data.uFlags |= NIF_INFO; icon_data.dwInfoFlags = NIIF_INFO; - wcscpy_s(icon_data.szInfoTitle, title.c_str()); - wcscpy_s(icon_data.szInfo, contents.c_str()); + wcsncpy_s(icon_data.szInfoTitle, title.c_str(), _TRUNCATE); + wcsncpy_s(icon_data.szInfo, contents.c_str(), _TRUNCATE); icon_data.uTimeout = 0; base::win::Version win_version = base::win::GetVersion(); diff --git a/atom/browser/ui/win/taskbar_host.cc b/atom/browser/ui/win/taskbar_host.cc index a8e6ff2926cd..0d250829110f 100644 --- a/atom/browser/ui/win/taskbar_host.cc +++ b/atom/browser/ui/win/taskbar_host.cc @@ -97,7 +97,8 @@ bool TaskbarHost::SetThumbarButtons( // Set tooltip. if (!button.tooltip.empty()) { thumb_button.dwMask |= THB_TOOLTIP; - wcscpy_s(thumb_button.szTip, base::UTF8ToUTF16(button.tooltip).c_str()); + wcsncpy_s(thumb_button.szTip, base::UTF8ToUTF16(button.tooltip).c_str(), + _TRUNCATE); } // Save callback. diff --git a/atom/common/api/lib/shell.coffee b/atom/common/api/lib/shell.coffee index bd7109e34518..5fb935bacd21 100644 --- a/atom/common/api/lib/shell.coffee +++ b/atom/common/api/lib/shell.coffee @@ -1,5 +1 @@ module.exports = process.atomBinding 'shell' - -if process.platform is 'win32' and process.type is 'renderer' - module.exports.showItemInFolder = (item) -> - require('remote').require('shell').showItemInFolder item diff --git a/atom/common/atom_version.h b/atom/common/atom_version.h index 5b1dc4ab44a6..b879da3066d4 100644 --- a/atom/common/atom_version.h +++ b/atom/common/atom_version.h @@ -7,7 +7,7 @@ #define ATOM_MAJOR_VERSION 0 #define ATOM_MINOR_VERSION 34 -#define ATOM_PATCH_VERSION 2 +#define ATOM_PATCH_VERSION 3 #define ATOM_VERSION_IS_RELEASE 1 diff --git a/atom/common/crash_reporter/crash_reporter_win.cc b/atom/common/crash_reporter/crash_reporter_win.cc index 78bd196f6cce..240c229ca4b6 100644 --- a/atom/common/crash_reporter/crash_reporter_win.cc +++ b/atom/common/crash_reporter/crash_reporter_win.cc @@ -80,7 +80,7 @@ struct ExceptionHandlerRecord { unsigned char thunk[12]; }; -void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { +bool RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { ExceptionHandlerRecord* record = reinterpret_cast(start); @@ -117,17 +117,17 @@ void RegisterNonABICompliantCodeRange(void* start, size_t size_in_bytes) { // Protect reserved page against modifications. DWORD old_protect; - CHECK(VirtualProtect( - start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect)); - CHECK(RtlAddFunctionTable( - &record->runtime_function, 1, reinterpret_cast(start))); + return VirtualProtect(start, sizeof(ExceptionHandlerRecord), + PAGE_EXECUTE_READ, &old_protect) && + RtlAddFunctionTable(&record->runtime_function, 1, + reinterpret_cast(start)); } void UnregisterNonABICompliantCodeRange(void* start) { ExceptionHandlerRecord* record = reinterpret_cast(start); - CHECK(RtlDeleteFunctionTable(&record->runtime_function)); + RtlDeleteFunctionTable(&record->runtime_function); } #endif // _WIN64 @@ -184,6 +184,7 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name, LOG(ERROR) << "Cannot initialize out-of-process crash handler"; #ifdef _WIN64 + bool registered = false; // Hook up V8 to breakpad. { // gin::Debug::SetCodeRangeCreatedCallback only runs the callback when @@ -192,9 +193,10 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name, size_t size = 0; v8::Isolate::GetCurrent()->GetCodeRange(&code_range, &size); if (code_range && size) - RegisterNonABICompliantCodeRange(code_range, size); + registered = RegisterNonABICompliantCodeRange(code_range, size); } - gin::Debug::SetCodeRangeDeletedCallback(UnregisterNonABICompliantCodeRange); + if (registered) + gin::Debug::SetCodeRangeDeletedCallback(UnregisterNonABICompliantCodeRange); #endif } diff --git a/atom/common/native_mate_converters/callback.cc b/atom/common/native_mate_converters/callback.cc index 3bcb748689fb..87faa3df3cd5 100644 --- a/atom/common/native_mate_converters/callback.cc +++ b/atom/common/native_mate_converters/callback.cc @@ -4,6 +4,8 @@ #include "atom/common/native_mate_converters/callback.h" +#include "atom/browser/atom_browser_main_parts.h" + namespace mate { namespace internal { @@ -54,6 +56,33 @@ v8::Local BindFunctionWith(v8::Isolate* isolate, } // namespace +SafeV8Function::SafeV8Function(v8::Isolate* isolate, v8::Local value) + : v8_function_(new RefCountedPersistent(isolate, value)), + weak_factory_(this) { + Init(); +} + +SafeV8Function::SafeV8Function(const SafeV8Function& other) + : v8_function_(other.v8_function_), + weak_factory_(this) { + Init(); +} + +v8::Local SafeV8Function::NewHandle() const { + return v8_function_->NewHandle(); +} + +void SafeV8Function::Init() { + if (Locker::IsBrowserProcess() && atom::AtomBrowserMainParts::Get()) + atom::AtomBrowserMainParts::Get()->RegisterDestructionCallback( + base::Bind(&SafeV8Function::FreeHandle, weak_factory_.GetWeakPtr())); +} + +void SafeV8Function::FreeHandle() { + Locker locker(v8_function_->isolate()); + v8_function_ = nullptr; +} + v8::Local CreateFunctionFromTranslater( v8::Isolate* isolate, const Translater& translater) { // The FunctionTemplate is cached. diff --git a/atom/common/native_mate_converters/callback.h b/atom/common/native_mate_converters/callback.h index 228fc0d3bb1d..5dd9d3cec9b1 100644 --- a/atom/common/native_mate_converters/callback.h +++ b/atom/common/native_mate_converters/callback.h @@ -10,6 +10,7 @@ #include "atom/common/api/locker.h" #include "base/bind.h" #include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "native_mate/function_template.h" #include "native_mate/scoped_persistent.h" #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h" @@ -18,7 +19,24 @@ namespace mate { namespace internal { -typedef scoped_refptr > SafeV8Function; +// Manages the V8 function with RAII, and automatically cleans the handle when +// JavaScript context is destroyed, even when the class is not destroyed. +class SafeV8Function { + public: + SafeV8Function(v8::Isolate* isolate, v8::Local value); + SafeV8Function(const SafeV8Function& other); + + bool is_alive() const { return v8_function_.get(); } + + v8::Local NewHandle() const; + + private: + void Init(); + void FreeHandle(); + + scoped_refptr> v8_function_; + base::WeakPtrFactory weak_factory_; +}; // Helper to invoke a V8 function with C++ parameters. template @@ -26,14 +44,17 @@ struct V8FunctionInvoker {}; template struct V8FunctionInvoker(ArgTypes...)> { - static v8::Local Go(v8::Isolate* isolate, SafeV8Function function, + static v8::Local Go(v8::Isolate* isolate, + const SafeV8Function& function, ArgTypes... raw) { Locker locker(isolate); v8::EscapableHandleScope handle_scope(isolate); + if (!function.is_alive()) + 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(); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -44,14 +65,17 @@ struct V8FunctionInvoker(ArgTypes...)> { template struct V8FunctionInvoker { - static void Go(v8::Isolate* isolate, SafeV8Function function, + static void Go(v8::Isolate* isolate, + const SafeV8Function& function, ArgTypes... raw) { Locker locker(isolate); v8::HandleScope handle_scope(isolate); + if (!function.is_alive()) + return; scoped_ptr script_scope( Locker::IsBrowserProcess() ? nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function->NewHandle(); + v8::Local holder = function.NewHandle(); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); std::vector> args = { ConvertToV8(isolate, raw)... }; @@ -61,17 +85,20 @@ struct V8FunctionInvoker { template struct V8FunctionInvoker { - static ReturnType Go(v8::Isolate* isolate, SafeV8Function function, + static ReturnType Go(v8::Isolate* isolate, + const SafeV8Function& function, ArgTypes... raw) { Locker locker(isolate); v8::HandleScope handle_scope(isolate); + ReturnType ret = ReturnType(); + if (!function.is_alive()) + return ret; scoped_ptr script_scope( Locker::IsBrowserProcess() ? nullptr : new blink::WebScopedRunV8Script(isolate)); - v8::Local holder = function->NewHandle(); + v8::Local holder = function.NewHandle(); v8::Local context = holder->CreationContext(); v8::Context::Scope context_scope(context); - ReturnType ret = ReturnType(); std::vector> args = { ConvertToV8(isolate, raw)... }; v8::Local result; auto maybe_result = @@ -119,9 +146,8 @@ struct Converter> { if (!val->IsFunction()) return false; - internal::SafeV8Function function( - new RefCountedPersistent(isolate, val)); - *out = base::Bind(&internal::V8FunctionInvoker::Go, isolate, function); + *out = base::Bind(&internal::V8FunctionInvoker::Go, + isolate, internal::SafeV8Function(isolate, val)); return true; } }; diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 10da202da70d..dbd0bd8d96ee 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -14,6 +14,7 @@ #include "atom/common/node_includes.h" #include "base/command_line.h" #include "base/base_paths.h" +#include "base/environment.h" #include "base/files/file_path.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" @@ -141,6 +142,14 @@ void NodeBindings::Initialize() { // Init node. // (we assume node::Init would not modify the parameters under embedded mode). node::Init(nullptr, nullptr, nullptr, nullptr); + +#if defined(OS_WIN) + // uv_init overrides error mode to suppress the default crash dialog, bring + // it back if user wants to show it. + scoped_ptr env(base::Environment::Create()); + if (env->HasVar("ELECTRON_DEFAULT_ERROR_MODE")) + SetErrorMode(0); +#endif } node::Environment* NodeBindings::CreateEnvironment( diff --git a/atom/common/platform_util_win.cc b/atom/common/platform_util_win.cc index 09ac5aca48f2..87f45e5cb2d4 100644 --- a/atom/common/platform_util_win.cc +++ b/atom/common/platform_util_win.cc @@ -5,7 +5,9 @@ #include "atom/common/platform_util.h" #include +#include #include +#include #include #include #include @@ -19,6 +21,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" #include "base/win/scoped_co_mem.h" +#include "base/win/scoped_com_initializer.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "url/gurl.h" @@ -42,11 +45,168 @@ bool ValidateShellCommandForScheme(const std::string& scheme) { return true; } +// Required COM implementation of IFileOperationProgressSink so we can +// precheck files before deletion to make sure they can be move to the +// Recycle Bin. +class DeleteFileProgressSink : public IFileOperationProgressSink { + public: + DeleteFileProgressSink(); + + private: + ULONG STDMETHODCALLTYPE AddRef(void); + ULONG STDMETHODCALLTYPE Release(void); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppvObj); + HRESULT STDMETHODCALLTYPE StartOperations(void); + HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT); + HRESULT STDMETHODCALLTYPE PreRenameItem( + DWORD, IShellItem*, LPCWSTR); + HRESULT STDMETHODCALLTYPE PostRenameItem( + DWORD, IShellItem*, LPCWSTR, HRESULT, IShellItem*); + HRESULT STDMETHODCALLTYPE PreMoveItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR); + HRESULT STDMETHODCALLTYPE PostMoveItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR, HRESULT, IShellItem*); + HRESULT STDMETHODCALLTYPE PreCopyItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR); + HRESULT STDMETHODCALLTYPE PostCopyItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR, HRESULT, IShellItem*); + HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD, IShellItem*); + HRESULT STDMETHODCALLTYPE PostDeleteItem( + DWORD, IShellItem*, HRESULT, IShellItem*); + HRESULT STDMETHODCALLTYPE PreNewItem( + DWORD, IShellItem*, LPCWSTR); + HRESULT STDMETHODCALLTYPE PostNewItem( + DWORD, IShellItem*, LPCWSTR, LPCWSTR, DWORD, HRESULT, IShellItem*); + HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT); + HRESULT STDMETHODCALLTYPE ResetTimer(void); + HRESULT STDMETHODCALLTYPE PauseTimer(void); + HRESULT STDMETHODCALLTYPE ResumeTimer(void); + + ULONG m_cRef; +}; + +DeleteFileProgressSink::DeleteFileProgressSink() { + m_cRef = 0; +} + +HRESULT DeleteFileProgressSink::PreDeleteItem(DWORD dwFlags, IShellItem*) { + if (!(dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE)) { + // TSF_DELETE_RECYCLE_IF_POSSIBLE will not be set for items that cannot be + // recycled. In this case, we abort the delete operation. This bubbles + // up and stops the Delete in IFileOperation. + return E_ABORT; + } + // Returns S_OK if successful, or an error value otherwise. In the case of an + // error value, the delete operation and all subsequent operations pending + // from the call to IFileOperation are canceled. + return S_OK; +} + +HRESULT DeleteFileProgressSink::QueryInterface(REFIID riid, LPVOID* ppvObj) { + // Always set out parameter to NULL, validating it first. + if (!ppvObj) + return E_INVALIDARG; + *ppvObj = nullptr; + if (riid == IID_IUnknown || riid == IID_IFileOperationProgressSink) { + // Increment the reference count and return the pointer. + *ppvObj = reinterpret_cast(this); + AddRef(); + return NOERROR; + } + return E_NOINTERFACE; +} + +ULONG DeleteFileProgressSink::AddRef() { + InterlockedIncrement(&m_cRef); + return m_cRef; +} + +ULONG DeleteFileProgressSink::Release() { + // Decrement the object's internal counter. + ULONG ulRefCount = InterlockedDecrement(&m_cRef); + if (0 == m_cRef) { + delete this; + } + return ulRefCount; +} + +HRESULT DeleteFileProgressSink::StartOperations() { + return S_OK; +} + +HRESULT DeleteFileProgressSink::FinishOperations(HRESULT) { + return S_OK; +} + +HRESULT DeleteFileProgressSink::PreRenameItem(DWORD, IShellItem*, LPCWSTR) { + return S_OK; +} + +HRESULT DeleteFileProgressSink::PostRenameItem( + DWORD, IShellItem*, __RPC__in_string LPCWSTR, HRESULT, IShellItem*) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PreMoveItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PostMoveItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR, HRESULT, IShellItem*) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PreCopyItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PostCopyItem( + DWORD, IShellItem*, IShellItem*, LPCWSTR, HRESULT, IShellItem*) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PostDeleteItem( + DWORD, IShellItem*, HRESULT, IShellItem*) { + return S_OK; +} + +HRESULT DeleteFileProgressSink::PreNewItem( + DWORD dwFlags, IShellItem*, LPCWSTR) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::PostNewItem( + DWORD, IShellItem*, LPCWSTR, LPCWSTR, DWORD, HRESULT, IShellItem*) { + return E_NOTIMPL; +} + +HRESULT DeleteFileProgressSink::UpdateProgress(UINT, UINT) { + return S_OK; +} + +HRESULT DeleteFileProgressSink::ResetTimer() { + return S_OK; +} + +HRESULT DeleteFileProgressSink::PauseTimer() { + return S_OK; +} + +HRESULT DeleteFileProgressSink::ResumeTimer() { + return S_OK; +} + } // namespace namespace platform_util { void ShowItemInFolder(const base::FilePath& full_path) { + base::win::ScopedCOMInitializer com_initializer; + if (!com_initializer.succeeded()) + return; + base::FilePath dir = full_path.DirName().AsEndingWithSeparator(); // ParseDisplayName will fail if the directory is "C:", it must be "C:\\". if (dir.empty()) @@ -170,32 +330,40 @@ bool OpenExternal(const GURL& url) { } bool MoveItemToTrash(const base::FilePath& path) { - // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, - // so we have to use wcscpy because wcscpy_s writes non-NULLs - // into the rest of the buffer. - wchar_t double_terminated_path[MAX_PATH + 1] = {0}; -#pragma warning(suppress:4996) // don't complain about wcscpy deprecation - wcscpy(double_terminated_path, path.value().c_str()); - - SHFILEOPSTRUCT file_operation = {0}; - file_operation.wFunc = FO_DELETE; - file_operation.pFrom = double_terminated_path; - file_operation.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOCONFIRMATION; - int err = SHFileOperation(&file_operation); - - // Since we're passing flags to the operation telling it to be silent, - // it's possible for the operation to be aborted/cancelled without err - // being set (although MSDN doesn't give any scenarios for how this can - // happen). See MSDN for SHFileOperation and SHFILEOPTSTRUCT. - if (file_operation.fAnyOperationsAborted) + base::win::ScopedCOMInitializer com_initializer; + if (!com_initializer.succeeded()) return false; - // Some versions of Windows return ERROR_FILE_NOT_FOUND (0x2) when deleting - // an empty directory and some return 0x402 when they should be returning - // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. Windows 7 - // can return DE_INVALIDFILES (0x7C) for nonexistent directories. - return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402 || - err == 0x7C); + base::win::ScopedComPtr pfo; + if (FAILED(pfo.CreateInstance(CLSID_FileOperation))) + return false; + + // Elevation prompt enabled for UAC protected files. This overrides the + // SILENT, NO_UI and NOERRORUI flags. + if (FAILED(pfo->SetOperationFlags(FOF_NO_UI | + FOF_ALLOWUNDO | + FOF_NOERRORUI | + FOF_SILENT | + FOFX_SHOWELEVATIONPROMPT | + FOFX_RECYCLEONDELETE))) + return false; + + // Create an IShellItem from the supplied source path. + base::win::ScopedComPtr delete_item; + if (FAILED(SHCreateItemFromParsingName(path.value().c_str(), + NULL, + IID_PPV_ARGS(delete_item.Receive())))) + return false; + + base::win::ScopedComPtr delete_sink( + new DeleteFileProgressSink); + if (!delete_sink) + return false; + + // Processes the queued command DeleteItem. This will trigger + // the DeleteFileProgressSink to check for Recycle Bin. + return SUCCEEDED(pfo->DeleteItem(delete_item.get(), delete_sink.get())) && + SUCCEEDED(pfo->PerformOperations()); } void Beep() { diff --git a/docs/api/app.md b/docs/api/app.md index 00aade7c54ab..fdb9f9980592 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -208,6 +208,15 @@ This method guarantees that all `beforeunload` and `unload` event handlers are correctly executed. It is possible that a window cancels the quitting by returning `false` in the `beforeunload` event handler. +### `app.exit(exitCode)` + +* `exitCode` Integer + +Exits immediately with `exitCode`. + +All windows will be closed immediately without asking user and the `before-quit` +and `will-quit` events will not be emitted. + ### `app.getAppPath()` Returns the current application directory. diff --git a/docs/tutorial/devtools-extension.md b/docs/tutorial/devtools-extension.md index 20ba7031d8ad..8a2144be6966 100644 --- a/docs/tutorial/devtools-extension.md +++ b/docs/tutorial/devtools-extension.md @@ -8,6 +8,8 @@ the `BrowserWindow.addDevToolsExtension` API to load them. The loaded extensions will be remembered so you don't need to call the API every time when creating a window. +** NOTE: React DevTools does not work, follow the issue here https://github.com/atom/electron/issues/915 ** + For example, to use the [React DevTools Extension](https://github.com/facebook/react-devtools) , first you need to download its source code: diff --git a/spec/api-crash-reporter-spec.coffee b/spec/api-crash-reporter-spec.coffee index 9aedb0c8ea1e..618adaaf9dec 100644 --- a/spec/api-crash-reporter-spec.coffee +++ b/spec/api-crash-reporter-spec.coffee @@ -53,5 +53,6 @@ describe 'crash-reporter module', -> protocol: 'file' pathname: path.join fixtures, 'api', 'crash.html' search: "?port=#{port}" - crashReporter.start {'submitUrl': 'http://127.0.0.1:' + port} + if process.platform is 'darwin' + crashReporter.start {'submitUrl': 'http://127.0.0.1:' + port} w.loadUrl url diff --git a/vendor/brightray b/vendor/brightray index 49a86c123f4c..98b3eb04d85a 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 49a86c123f4cc43f4dca886ded612104a8a1fec6 +Subproject commit 98b3eb04d85a8a70b8d9f29477437061c2e77a83