Browse Source

Add 'will-prevent-unload' event.

22222 8 years ago
parent
commit
4044548f3e

+ 10 - 0
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<content::BluetoothChooser> 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.

+ 4 - 0
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<WebContents>,
   std::unique_ptr<content::BluetoothChooser> 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<WebContents>,
   v8::Global<v8::Value> devtools_web_contents_;
   v8::Global<v8::Value> debugger_;
 
+  std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
   std::unique_ptr<WebViewGuestDelegate> guest_delegate_;
 
   // The host webcontents that may contain this webcontents.

+ 8 - 3
atom/browser/atom_javascript_dialog_manager.cc

@@ -7,6 +7,7 @@
 #include <string>
 #include <vector>
 
+#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(

+ 7 - 0
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

+ 0 - 10
atom/browser/common_web_contents_delegate.cc

@@ -9,7 +9,6 @@
 #include <vector>
 
 #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,

+ 0 - 4
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<WebDialogHelper> web_dialog_helper_;
-  std::unique_ptr<AtomJavaScriptDialogManager> dialog_manager_;
   scoped_refptr<DevToolsFileSystemIndexer> devtools_file_system_indexer_;
 
   // Make sure BrowserContext is alwasys destroyed after WebContents.

+ 30 - 0
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:

+ 25 - 0
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
 

+ 4 - 0
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')}`