From 4044548f3e64ab127e02b58ee28cf5d69cf5af56 Mon Sep 17 00:00:00 2001 From: 22222 <22222@users.noreply.github.com> Date: Thu, 27 Apr 2017 19:28:48 -0500 Subject: [PATCH] Add 'will-prevent-unload' event. --- atom/browser/api/atom_api_web_contents.cc | 10 +++++++ atom/browser/api/atom_api_web_contents.h | 4 +++ .../browser/atom_javascript_dialog_manager.cc | 11 +++++-- atom/browser/atom_javascript_dialog_manager.h | 7 +++++ atom/browser/common_web_contents_delegate.cc | 10 ------- atom/browser/common_web_contents_delegate.h | 4 --- docs/api/web-contents.md | 30 +++++++++++++++++++ spec/api-web-contents-spec.js | 25 ++++++++++++++++ spec/static/main.js | 4 +++ 9 files changed, 88 insertions(+), 17 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index fedb5af844fa..43caf41a7af3 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -13,6 +13,7 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/atom_javascript_dialog_manager.h" #include "atom/browser/child_web_contents_tracker.h" #include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/native_window.h" @@ -683,6 +684,15 @@ std::unique_ptr WebContents::RunBluetoothChooser( return std::move(bluetooth_chooser); } +content::JavaScriptDialogManager* +WebContents::GetJavaScriptDialogManager( + content::WebContents* source) { + if (!dialog_manager_) + dialog_manager_.reset(new AtomJavaScriptDialogManager(this)); + + return dialog_manager_.get(); +} + void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) { // Do nothing, we override this method just to avoid compilation error since // there are two virtual functions named BeforeUnloadFired. diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 1f926dbd7a5a..3f7407e1ebb4 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -41,6 +41,7 @@ namespace atom { struct SetSizeParams; class AtomBrowserContext; +class AtomJavaScriptDialogManager; class WebContentsZoomController; class WebViewGuestDelegate; @@ -296,6 +297,8 @@ class WebContents : public mate::TrackableObject, std::unique_ptr RunBluetoothChooser( content::RenderFrameHost* frame, const content::BluetoothChooser::EventHandler& handler) override; + content::JavaScriptDialogManager* GetJavaScriptDialogManager( + content::WebContents* source) override; // content::WebContentsObserver: void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; @@ -388,6 +391,7 @@ class WebContents : public mate::TrackableObject, v8::Global devtools_web_contents_; v8::Global debugger_; + std::unique_ptr dialog_manager_; std::unique_ptr guest_delegate_; // The host webcontents that may contain this webcontents. diff --git a/atom/browser/atom_javascript_dialog_manager.cc b/atom/browser/atom_javascript_dialog_manager.cc index 79f82d99441c..c593b2bfba1d 100644 --- a/atom/browser/atom_javascript_dialog_manager.cc +++ b/atom/browser/atom_javascript_dialog_manager.cc @@ -7,6 +7,7 @@ #include #include +#include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/message_box.h" #include "base/bind.h" @@ -17,6 +18,10 @@ using content::JavaScriptDialogType; namespace atom { +AtomJavaScriptDialogManager::AtomJavaScriptDialogManager( + api::WebContents* api_web_contents) + : api_web_contents_(api_web_contents) {} + void AtomJavaScriptDialogManager::RunJavaScriptDialog( content::WebContents* web_contents, const GURL& origin_url, @@ -25,7 +30,6 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( const base::string16& default_prompt_text, const DialogClosedCallback& callback, bool* did_suppress_message) { - if (dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT && dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) { callback.Run(false, base::string16()); @@ -49,8 +53,9 @@ void AtomJavaScriptDialogManager::RunBeforeUnloadDialog( content::WebContents* web_contents, bool is_reload, const DialogClosedCallback& callback) { - // FIXME(zcbenz): the |message_text| is removed, figure out what should we do. - callback.Run(false, base::ASCIIToUTF16("This should not be displayed")); + bool default_prevented = api_web_contents_->Emit("will-prevent-unload"); + callback.Run(default_prevented, base::string16()); + return; } void AtomJavaScriptDialogManager::CancelDialogs( diff --git a/atom/browser/atom_javascript_dialog_manager.h b/atom/browser/atom_javascript_dialog_manager.h index 56b62afe8776..d5e6c543361c 100644 --- a/atom/browser/atom_javascript_dialog_manager.h +++ b/atom/browser/atom_javascript_dialog_manager.h @@ -11,8 +11,14 @@ namespace atom { +namespace api { +class WebContents; +} + class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { public: + explicit AtomJavaScriptDialogManager(api::WebContents* api_web_contents); + // content::JavaScriptDialogManager implementations. void RunJavaScriptDialog( content::WebContents* web_contents, @@ -33,6 +39,7 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { static void OnMessageBoxCallback(const DialogClosedCallback& callback, int code, bool checkbox_checked); + api::WebContents* api_web_contents_; }; } // namespace atom diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 06ac0089714c..b0430dafeb82 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -9,7 +9,6 @@ #include #include "atom/browser/atom_browser_context.h" -#include "atom/browser/atom_javascript_dialog_manager.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/file_dialog.h" #include "atom/browser/web_dialog_helper.h" @@ -230,15 +229,6 @@ bool CommonWebContentsDelegate::CanOverscrollContent() const { return false; } -content::JavaScriptDialogManager* -CommonWebContentsDelegate::GetJavaScriptDialogManager( - content::WebContents* source) { - if (!dialog_manager_) - dialog_manager_.reset(new AtomJavaScriptDialogManager); - - return dialog_manager_.get(); -} - content::ColorChooser* CommonWebContentsDelegate::OpenColorChooser( content::WebContents* web_contents, SkColor color, diff --git a/atom/browser/common_web_contents_delegate.h b/atom/browser/common_web_contents_delegate.h index d1d26314966e..fd468afd563d 100644 --- a/atom/browser/common_web_contents_delegate.h +++ b/atom/browser/common_web_contents_delegate.h @@ -20,7 +20,6 @@ using brightray::DevToolsFileSystemIndexer; namespace atom { class AtomBrowserContext; -class AtomJavaScriptDialogManager; class NativeWindow; class WebDialogHelper; @@ -62,8 +61,6 @@ class CommonWebContentsDelegate content::WebContents* source, const content::OpenURLParams& params) override; bool CanOverscrollContent() const override; - content::JavaScriptDialogManager* GetJavaScriptDialogManager( - content::WebContents* source) override; content::ColorChooser* OpenColorChooser( content::WebContents* web_contents, SkColor color, @@ -147,7 +144,6 @@ class CommonWebContentsDelegate bool native_fullscreen_; std::unique_ptr web_dialog_helper_; - std::unique_ptr dialog_manager_; scoped_refptr devtools_file_system_indexer_; // Make sure BrowserContext is alwasys destroyed after WebContents. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 98b1cbd2f241..d2d3567f141a 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -218,6 +218,36 @@ When in-page navigation happens, the page URL changes but does not cause navigation outside of the page. Examples of this occurring are when anchor links are clicked or when the DOM `hashchange` event is triggered. +#### Event: 'will-prevent-unload' + +Returns: + +* `event` Event + +Emitted when a `beforeunload` event handler is attempting to cancel a page unload. + +Calling `event.preventDefault()` will ignore the `beforeunload` event handler +and allow the page to be unloaded. + +```javascript +const {BrowserWindow} = require('electron') +let win = new BrowserWindow({width: 800, height: 600}) +win.webContents.on('will-prevent-unload', (event) => { + let choice = dialog.showMessageBox(mainWindow, { + type: 'question', + buttons: ['Leave', 'Stay'], + title: 'Do you want to leave this site?', + message: 'Changes you made may not be saved.', + defaultId: 0, + cancelId: 1 + }) + let leave = (choice === 0) + if (leave) { + event.preventDefault() + } +}) +``` + #### Event: 'crashed' Returns: diff --git a/spec/api-web-contents-spec.js b/spec/api-web-contents-spec.js index 7eac9d3e0e96..6dab70b9c38b 100644 --- a/spec/api-web-contents-spec.js +++ b/spec/api-web-contents-spec.js @@ -543,6 +543,31 @@ describe('webContents module', function () { }) }) + describe('will-prevent-unload event', function () { + it('does not emit if beforeunload returns undefined', function (done) { + w.once('closed', function () { + done() + }) + w.webContents.on('will-prevent-unload', function (e) { + assert.fail('should not have fired') + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-undefined.html')) + }) + + it('emits if beforeunload returns false', (done) => { + w.webContents.on('will-prevent-unload', () => { + done() + }) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')) + }) + + it('supports calling preventDefault on will-prevent-unload events', function (done) { + ipcRenderer.send('prevent-next-will-prevent-unload', w.webContents.id) + w.once('closed', () => done()) + w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-false.html')) + }) + }) + describe('destroy()', () => { let server diff --git a/spec/static/main.js b/spec/static/main.js index ba31b61ae07d..fcfd08b1aea3 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -266,6 +266,10 @@ ipcMain.on('prevent-next-will-attach-webview', (event) => { event.sender.once('will-attach-webview', event => event.preventDefault()) }) +ipcMain.on('prevent-next-will-prevent-unload', (event, id) => { + webContents.fromId(id).once('will-prevent-unload', event => event.preventDefault()) +}) + ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`