Implement dialog (alert/confirm) blocking as a user switch after the first dialog
* This is to enable more browser-like behavior so that users who run third-party code will not be DOS'ed with alerts and confirms. This is already handled like this in most major browsers so this will greatly help these developers
This commit is contained in:
parent
a3d4d461a3
commit
795447f61a
7 changed files with 47 additions and 43 deletions
|
@ -10,6 +10,7 @@
|
||||||
#include "atom/browser/api/atom_api_web_contents.h"
|
#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 "atom/browser/web_contents_preferences.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "ui/gfx/image/image_skia.h"
|
#include "ui/gfx/image/image_skia.h"
|
||||||
|
@ -30,6 +31,13 @@ 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) {
|
||||||
|
const std::string origin = origin_url.GetOrigin().spec();
|
||||||
|
if (origin_counts_.find(origin) == origin_counts_.end()) {
|
||||||
|
origin_counts_[origin] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin_counts_[origin] == -1) return callback.Run(false, base::string16());;
|
||||||
|
|
||||||
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());
|
||||||
|
@ -41,12 +49,23 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog(
|
||||||
buttons.push_back("Cancel");
|
buttons.push_back("Cancel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::ShowMessageBox(NativeWindow::FromWebContents(web_contents),
|
||||||
atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, buttons, -1,
|
atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, buttons, -1,
|
||||||
0, atom::MessageBoxOptions::MESSAGE_BOX_NONE, "",
|
0, atom::MessageBoxOptions::MESSAGE_BOX_NONE, "",
|
||||||
base::UTF16ToUTF8(message_text), "", "", false,
|
base::UTF16ToUTF8(message_text), "", checkbox_string, false,
|
||||||
gfx::ImageSkia(),
|
gfx::ImageSkia(),
|
||||||
base::Bind(&OnMessageBoxCallback, callback));
|
base::Bind(&OnMessageBoxCallback, callback, origin,
|
||||||
|
&origin_counts_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtomJavaScriptDialogManager::RunBeforeUnloadDialog(
|
void AtomJavaScriptDialogManager::RunBeforeUnloadDialog(
|
||||||
|
@ -66,8 +85,13 @@ void AtomJavaScriptDialogManager::CancelDialogs(
|
||||||
// static
|
// static
|
||||||
void AtomJavaScriptDialogManager::OnMessageBoxCallback(
|
void AtomJavaScriptDialogManager::OnMessageBoxCallback(
|
||||||
const DialogClosedCallback& callback,
|
const DialogClosedCallback& callback,
|
||||||
|
const std::string& origin,
|
||||||
|
std::map<std::string, int>* origin_counts_,
|
||||||
int code,
|
int code,
|
||||||
bool checkbox_checked) {
|
bool checkbox_checked) {
|
||||||
|
if (checkbox_checked) {
|
||||||
|
(*origin_counts_)[origin] = -1;
|
||||||
|
}
|
||||||
callback.Run(code == 0, base::string16());
|
callback.Run(code == 0, base::string16());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_
|
#define ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "content/public/browser/javascript_dialog_manager.h"
|
#include "content/public/browser/javascript_dialog_manager.h"
|
||||||
|
|
||||||
|
@ -37,9 +38,12 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void OnMessageBoxCallback(const DialogClosedCallback& callback,
|
static void OnMessageBoxCallback(const DialogClosedCallback& callback,
|
||||||
|
const std::string& origin,
|
||||||
|
std::map<std::string, int>* origins_,
|
||||||
int code,
|
int code,
|
||||||
bool checkbox_checked);
|
bool checkbox_checked);
|
||||||
api::WebContents* api_web_contents_;
|
api::WebContents* api_web_contents_;
|
||||||
|
std::map<std::string, int> origin_counts_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -301,4 +301,13 @@ bool WebContentsPreferences::GetInteger(const std::string& attributeName,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebContentsPreferences::GetString(const std::string& attributeName,
|
||||||
|
std::string* stringValue,
|
||||||
|
content::WebContents* web_contents) {
|
||||||
|
WebContentsPreferences* self = FromWebContents(web_contents);
|
||||||
|
if (!self)
|
||||||
|
return false;
|
||||||
|
return self->web_preferences()->GetString(attributeName, stringValue);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -40,6 +40,10 @@ class WebContentsPreferences
|
||||||
static bool IsPreferenceEnabled(const std::string& attribute_name,
|
static bool IsPreferenceEnabled(const std::string& attribute_name,
|
||||||
content::WebContents* web_contents);
|
content::WebContents* web_contents);
|
||||||
|
|
||||||
|
static bool GetString(const std::string& attributeName,
|
||||||
|
std::string* stringValue,
|
||||||
|
content::WebContents* web_contents);
|
||||||
|
|
||||||
// Modify the WebPreferences according to |web_contents|'s preferences.
|
// Modify the WebPreferences according to |web_contents|'s preferences.
|
||||||
static void OverrideWebkitPrefs(
|
static void OverrideWebkitPrefs(
|
||||||
content::WebContents* web_contents, content::WebPreferences* prefs);
|
content::WebContents* web_contents, content::WebPreferences* prefs);
|
||||||
|
|
|
@ -362,6 +362,10 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
* `additionArguments` String[] (optional) - A list of strings that will be appended
|
* `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
|
to `process.argv` in the renderer process of this app. Useful for passing small
|
||||||
bits of data down to renderer process preload scripts.
|
bits of data down to renderer process preload scripts.
|
||||||
|
* `safeDialogs` Boolean (optional) - Whether to enable browser style
|
||||||
|
consecutive dialog protection.
|
||||||
|
* `safeDialogsMessage` String (optional) - The message to display when consecutive
|
||||||
|
dialog protection is triggered.
|
||||||
|
|
||||||
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
When setting minimum or maximum window size with `minWidth`/`maxWidth`/
|
||||||
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
`minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from
|
||||||
|
|
|
@ -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()
|
// Implements window.close()
|
||||||
ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
|
||||||
const window = event.sender.getOwnerBrowserWindow()
|
const window = event.sender.getOwnerBrowserWindow()
|
||||||
|
|
|
@ -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().
|
// But we do not support prompt().
|
||||||
window.prompt = function () {
|
window.prompt = function () {
|
||||||
throw new Error('prompt() is and will not be supported.')
|
throw new Error('prompt() is and will not be supported.')
|
||||||
|
|
Loading…
Reference in a new issue