Add 'will-prevent-unload' event.

This commit is contained in:
22222 2017-04-27 19:28:48 -05:00
parent 0c3f80c334
commit 4044548f3e
9 changed files with 88 additions and 17 deletions

View file

@ -13,6 +13,7 @@
#include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_client.h"
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.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/child_web_contents_tracker.h"
#include "atom/browser/lib/bluetooth_chooser.h" #include "atom/browser/lib/bluetooth_chooser.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
@ -683,6 +684,15 @@ std::unique_ptr<content::BluetoothChooser> WebContents::RunBluetoothChooser(
return std::move(bluetooth_chooser); 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) { void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
// Do nothing, we override this method just to avoid compilation error since // Do nothing, we override this method just to avoid compilation error since
// there are two virtual functions named BeforeUnloadFired. // there are two virtual functions named BeforeUnloadFired.

View file

@ -41,6 +41,7 @@ namespace atom {
struct SetSizeParams; struct SetSizeParams;
class AtomBrowserContext; class AtomBrowserContext;
class AtomJavaScriptDialogManager;
class WebContentsZoomController; class WebContentsZoomController;
class WebViewGuestDelegate; class WebViewGuestDelegate;
@ -296,6 +297,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser( std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
content::RenderFrameHost* frame, content::RenderFrameHost* frame,
const content::BluetoothChooser::EventHandler& handler) override; const content::BluetoothChooser::EventHandler& handler) override;
content::JavaScriptDialogManager* GetJavaScriptDialogManager(
content::WebContents* source) override;
// content::WebContentsObserver: // content::WebContentsObserver:
void BeforeUnloadFired(const base::TimeTicks& proceed_time) override; void BeforeUnloadFired(const base::TimeTicks& proceed_time) override;
@ -388,6 +391,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
v8::Global<v8::Value> devtools_web_contents_; v8::Global<v8::Value> devtools_web_contents_;
v8::Global<v8::Value> debugger_; v8::Global<v8::Value> debugger_;
std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
std::unique_ptr<WebViewGuestDelegate> guest_delegate_; std::unique_ptr<WebViewGuestDelegate> guest_delegate_;
// The host webcontents that may contain this webcontents. // The host webcontents that may contain this webcontents.

View file

@ -7,6 +7,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/browser/ui/message_box.h" #include "atom/browser/ui/message_box.h"
#include "base/bind.h" #include "base/bind.h"
@ -17,6 +18,10 @@ using content::JavaScriptDialogType;
namespace atom { namespace atom {
AtomJavaScriptDialogManager::AtomJavaScriptDialogManager(
api::WebContents* api_web_contents)
: api_web_contents_(api_web_contents) {}
void AtomJavaScriptDialogManager::RunJavaScriptDialog( void AtomJavaScriptDialogManager::RunJavaScriptDialog(
content::WebContents* web_contents, content::WebContents* web_contents,
const GURL& origin_url, const GURL& origin_url,
@ -25,7 +30,6 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog(
const base::string16& default_prompt_text, const base::string16& default_prompt_text,
const DialogClosedCallback& callback, const DialogClosedCallback& callback,
bool* did_suppress_message) { bool* did_suppress_message) {
if (dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT && if (dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT &&
dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) { dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) {
callback.Run(false, base::string16()); callback.Run(false, base::string16());
@ -49,8 +53,9 @@ void AtomJavaScriptDialogManager::RunBeforeUnloadDialog(
content::WebContents* web_contents, content::WebContents* web_contents,
bool is_reload, bool is_reload,
const DialogClosedCallback& callback) { const DialogClosedCallback& callback) {
// FIXME(zcbenz): the |message_text| is removed, figure out what should we do. bool default_prevented = api_web_contents_->Emit("will-prevent-unload");
callback.Run(false, base::ASCIIToUTF16("This should not be displayed")); callback.Run(default_prevented, base::string16());
return;
} }
void AtomJavaScriptDialogManager::CancelDialogs( void AtomJavaScriptDialogManager::CancelDialogs(

View file

@ -11,8 +11,14 @@
namespace atom { namespace atom {
namespace api {
class WebContents;
}
class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager {
public: public:
explicit AtomJavaScriptDialogManager(api::WebContents* api_web_contents);
// content::JavaScriptDialogManager implementations. // content::JavaScriptDialogManager implementations.
void RunJavaScriptDialog( void RunJavaScriptDialog(
content::WebContents* web_contents, content::WebContents* web_contents,
@ -33,6 +39,7 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager {
static void OnMessageBoxCallback(const DialogClosedCallback& callback, static void OnMessageBoxCallback(const DialogClosedCallback& callback,
int code, int code,
bool checkbox_checked); bool checkbox_checked);
api::WebContents* api_web_contents_;
}; };
} // namespace atom } // namespace atom

View file

@ -9,7 +9,6 @@
#include <vector> #include <vector>
#include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_javascript_dialog_manager.h"
#include "atom/browser/native_window.h" #include "atom/browser/native_window.h"
#include "atom/browser/ui/file_dialog.h" #include "atom/browser/ui/file_dialog.h"
#include "atom/browser/web_dialog_helper.h" #include "atom/browser/web_dialog_helper.h"
@ -230,15 +229,6 @@ bool CommonWebContentsDelegate::CanOverscrollContent() const {
return false; 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::ColorChooser* CommonWebContentsDelegate::OpenColorChooser(
content::WebContents* web_contents, content::WebContents* web_contents,
SkColor color, SkColor color,

View file

@ -20,7 +20,6 @@ using brightray::DevToolsFileSystemIndexer;
namespace atom { namespace atom {
class AtomBrowserContext; class AtomBrowserContext;
class AtomJavaScriptDialogManager;
class NativeWindow; class NativeWindow;
class WebDialogHelper; class WebDialogHelper;
@ -62,8 +61,6 @@ class CommonWebContentsDelegate
content::WebContents* source, content::WebContents* source,
const content::OpenURLParams& params) override; const content::OpenURLParams& params) override;
bool CanOverscrollContent() const override; bool CanOverscrollContent() const override;
content::JavaScriptDialogManager* GetJavaScriptDialogManager(
content::WebContents* source) override;
content::ColorChooser* OpenColorChooser( content::ColorChooser* OpenColorChooser(
content::WebContents* web_contents, content::WebContents* web_contents,
SkColor color, SkColor color,
@ -147,7 +144,6 @@ class CommonWebContentsDelegate
bool native_fullscreen_; bool native_fullscreen_;
std::unique_ptr<WebDialogHelper> web_dialog_helper_; std::unique_ptr<WebDialogHelper> web_dialog_helper_;
std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
scoped_refptr<DevToolsFileSystemIndexer> devtools_file_system_indexer_; scoped_refptr<DevToolsFileSystemIndexer> devtools_file_system_indexer_;
// Make sure BrowserContext is alwasys destroyed after WebContents. // Make sure BrowserContext is alwasys destroyed after WebContents.

View file

@ -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 navigation outside of the page. Examples of this occurring are when anchor links
are clicked or when the DOM `hashchange` event is triggered. 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' #### Event: 'crashed'
Returns: Returns:

View file

@ -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()', () => { describe('destroy()', () => {
let server let server

View file

@ -266,6 +266,10 @@ ipcMain.on('prevent-next-will-attach-webview', (event) => {
event.sender.once('will-attach-webview', event => event.preventDefault()) 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) => { ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => {
event.sender.once('will-attach-webview', (event, webPreferences, params) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => {
params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}` params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`