Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dialog (alert/confirm) blocking as a user switch after the first dialog #11613

Merged
merged 10 commits into from Mar 6, 2018
41 changes: 34 additions & 7 deletions atom/browser/atom_javascript_dialog_manager.cc
Expand Up @@ -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"
Expand All @@ -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) {}
Expand All @@ -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());
Expand All @@ -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(
Expand All @@ -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());
}

Expand Down
10 changes: 7 additions & 3 deletions atom/browser/atom_javascript_dialog_manager.h
Expand Up @@ -5,6 +5,7 @@
#ifndef ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_
#define ATOM_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_

#include <map>
#include <string>

#include "content/public/browser/javascript_dialog_manager.h"
Expand Down Expand Up @@ -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<std::string, int> origin_counts_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe unordered_map instead?

};

} // namespace atom
Expand Down
9 changes: 9 additions & 0 deletions atom/browser/web_contents_preferences.cc
Expand Up @@ -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
4 changes: 4 additions & 0 deletions atom/browser/web_contents_preferences.h
Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions docs/api/browser-window.md
Expand Up @@ -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
Expand Down
33 changes: 0 additions & 33 deletions lib/browser/rpc-server.js
Expand Up @@ -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()
Expand Down
8 changes: 0 additions & 8 deletions lib/renderer/window-setup.js
Expand Up @@ -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.')
Expand Down