diff --git a/atom/browser/atom_javascript_dialog_manager.cc b/atom/browser/atom_javascript_dialog_manager.cc index c593b2bfba..1f0536b4c2 100644 --- a/atom/browser/atom_javascript_dialog_manager.cc +++ b/atom/browser/atom_javascript_dialog_manager.cc @@ -10,6 +10,7 @@ #include "atom/browser/api/atom_api_web_contents.h" #include "atom/browser/native_window.h" #include "atom/browser/ui/message_box.h" +#include "atom/browser/web_contents_preferences.h" #include "base/bind.h" #include "base/strings/utf_string_conversions.h" #include "ui/gfx/image/image_skia.h" @@ -18,6 +19,12 @@ using content::JavaScriptDialogType; namespace atom { +namespace { + +constexpr int kUserWantsNoMoreDialogs = -1; + +} // namespace + AtomJavaScriptDialogManager::AtomJavaScriptDialogManager( api::WebContents* api_web_contents) : api_web_contents_(api_web_contents) {} @@ -30,6 +37,11 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( const base::string16& default_prompt_text, const DialogClosedCallback& callback, bool* did_suppress_message) { + const std::string origin = origin_url.GetOrigin().spec(); + if (origin_counts_[origin] == kUserWantsNoMoreDialogs) { + return callback.Run(false, base::string16()); + } + if (dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_ALERT && dialog_type != JavaScriptDialogType::JAVASCRIPT_DIALOG_TYPE_CONFIRM) { callback.Run(false, base::string16()); @@ -41,12 +53,25 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( buttons.push_back("Cancel"); } - atom::ShowMessageBox(NativeWindow::FromWebContents(web_contents), - atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, buttons, -1, - 0, atom::MessageBoxOptions::MESSAGE_BOX_NONE, "", - base::UTF16ToUTF8(message_text), "", "", false, - gfx::ImageSkia(), - base::Bind(&OnMessageBoxCallback, callback)); + origin_counts_[origin]++; + + std::string checkbox_string; + if (origin_counts_[origin] > 1 && + WebContentsPreferences::IsPreferenceEnabled("safeDialogs", + web_contents)) { + if (!WebContentsPreferences::GetString("safeDialogsMessage", + &checkbox_string, web_contents)) { + checkbox_string = "Prevent this app from creating additional dialogs"; + } + } + atom::ShowMessageBox( + NativeWindow::FromWebContents(web_contents), + atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, buttons, -1, 0, + atom::MessageBoxOptions::MESSAGE_BOX_NONE, "", + base::UTF16ToUTF8(message_text), "", checkbox_string, + false, gfx::ImageSkia(), + base::Bind(&AtomJavaScriptDialogManager::OnMessageBoxCallback, + base::Unretained(this), callback, origin)); } void AtomJavaScriptDialogManager::RunBeforeUnloadDialog( @@ -63,11 +88,13 @@ void AtomJavaScriptDialogManager::CancelDialogs( bool reset_state) { } -// static void AtomJavaScriptDialogManager::OnMessageBoxCallback( const DialogClosedCallback& callback, + const std::string& origin, int code, bool checkbox_checked) { + if (checkbox_checked) + origin_counts_[origin] = kUserWantsNoMoreDialogs; callback.Run(code == 0, base::string16()); } diff --git a/atom/browser/atom_javascript_dialog_manager.h b/atom/browser/atom_javascript_dialog_manager.h index d5e6c54336..0f5e55403b 100644 --- a/atom/browser/atom_javascript_dialog_manager.h +++ b/atom/browser/atom_javascript_dialog_manager.h @@ -5,6 +5,7 @@ #ifndef ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_ #define ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_ +#include #include #include "content/public/browser/javascript_dialog_manager.h" @@ -36,10 +37,13 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { bool reset_state) override; private: - static void OnMessageBoxCallback(const DialogClosedCallback& callback, - int code, - bool checkbox_checked); + void OnMessageBoxCallback(const DialogClosedCallback& callback, + const std::string& origin, + int code, + bool checkbox_checked); + api::WebContents* api_web_contents_; + std::map origin_counts_; }; } // namespace atom diff --git a/atom/browser/web_contents_preferences.cc b/atom/browser/web_contents_preferences.cc index 2cbf07be86..7951c36b45 100644 --- a/atom/browser/web_contents_preferences.cc +++ b/atom/browser/web_contents_preferences.cc @@ -301,4 +301,13 @@ bool WebContentsPreferences::GetInteger(const std::string& attributeName, return false; } +bool WebContentsPreferences::GetString(const std::string& attribute_name, + std::string* string_value, + content::WebContents* web_contents) { + WebContentsPreferences* self = FromWebContents(web_contents); + if (!self) + return false; + return self->web_preferences()->GetString(attribute_name, string_value); +} + } // namespace atom diff --git a/atom/browser/web_contents_preferences.h b/atom/browser/web_contents_preferences.h index 94dd8dc598..5cc6bd97b2 100644 --- a/atom/browser/web_contents_preferences.h +++ b/atom/browser/web_contents_preferences.h @@ -40,6 +40,10 @@ class WebContentsPreferences static bool IsPreferenceEnabled(const std::string& attribute_name, content::WebContents* web_contents); + static bool GetString(const std::string& attribute_name, + std::string* string_value, + content::WebContents* web_contents); + // Modify the WebPreferences according to |web_contents|'s preferences. static void OverrideWebkitPrefs( content::WebContents* web_contents, content::WebPreferences* prefs); diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 2be2da5976..e0d46834b6 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -362,6 +362,12 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `additionArguments` String[] (optional) - A list of strings that will be appended to `process.argv` in the renderer process of this app. Useful for passing small bits of data down to renderer process preload scripts. + * `safeDialogs` Boolean (optional) - Whether to enable browser style + consecutive dialog protection. Default is `false`. + * `safeDialogsMessage` String (optional) - The message to display when + consecutive dialog protection is triggered. If not defined the default + message would be used, note that currently the default message is in + English and not localized. When setting minimum or maximum window size with `minWidth`/`maxWidth`/ `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index c0cd09dea4..633360db1e 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -438,39 +438,6 @@ ipcMain.on('ELECTRON_BROWSER_SEND_TO', function (event, sendToAll, webContentsId } }) -// Implements window.alert(message, title) -ipcMain.on('ELECTRON_BROWSER_WINDOW_ALERT', function (event, message, title) { - if (message == null) message = '' - if (title == null) title = '' - - const dialogProperties = { - message: `${message}`, - title: `${title}`, - buttons: ['OK'] - } - event.returnValue = event.sender.isOffscreen() - ? electron.dialog.showMessageBox(dialogProperties) - : electron.dialog.showMessageBox( - event.sender.getOwnerBrowserWindow(), dialogProperties) -}) - -// Implements window.confirm(message, title) -ipcMain.on('ELECTRON_BROWSER_WINDOW_CONFIRM', function (event, message, title) { - if (message == null) message = '' - if (title == null) title = '' - - const dialogProperties = { - message: `${message}`, - title: `${title}`, - buttons: ['OK', 'Cancel'], - cancelId: 1 - } - event.returnValue = !(event.sender.isOffscreen() - ? electron.dialog.showMessageBox(dialogProperties) - : electron.dialog.showMessageBox( - event.sender.getOwnerBrowserWindow(), dialogProperties)) -}) - // Implements window.close() ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) { const window = event.sender.getOwnerBrowserWindow() diff --git a/lib/renderer/window-setup.js b/lib/renderer/window-setup.js index b94eace5ae..ac329b4307 100644 --- a/lib/renderer/window-setup.js +++ b/lib/renderer/window-setup.js @@ -133,14 +133,6 @@ module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNative } } - window.alert = function (message, title) { - ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_ALERT', toString(message), toString(title)) - } - - window.confirm = function (message, title) { - return ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CONFIRM', toString(message), toString(title)) - } - // But we do not support prompt(). window.prompt = function () { throw new Error('prompt() is and will not be supported.') diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 871af98b4d..37030b1252 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -1088,10 +1088,6 @@ describe('chromium feature', () => { assert.throws(() => { window.alert({toString: null}) }, /Cannot convert object to primitive value/) - - assert.throws(() => { - window.alert('message', {toString: 3}) - }, /Cannot convert object to primitive value/) }) }) @@ -1100,10 +1096,6 @@ describe('chromium feature', () => { assert.throws(() => { window.confirm({toString: null}, 'title') }, /Cannot convert object to primitive value/) - - assert.throws(() => { - window.confirm('message', {toString: 3}) - }, /Cannot convert object to primitive value/) }) })