blob: 992f646a91987aca2f55ef97c0d2b410492e98c4 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/app_modal/javascript_app_modal_dialog.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/app_modal/javascript_dialog_manager.h"
#include "components/app_modal/javascript_native_dialog_factory.h"
#include "ui/gfx/text_elider.h"
namespace app_modal {
namespace {
// Control maximum sizes of various texts passed to us from javascript.
#if defined(OS_POSIX) && !defined(OS_MACOSX)
// Two-dimensional eliding. Reformat the text of the message dialog
// inserting line breaks because otherwise a single long line can overflow
// the message dialog (and crash/hang the GTK, depending on the version).
const int kMessageTextMaxRows = 32;
const int kMessageTextMaxCols = 132;
const int kDefaultPromptMaxRows = 24;
const int kDefaultPromptMaxCols = 132;
void EnforceMaxTextSize(const base::string16& in_string,
base::string16* out_string) {
gfx::ElideRectangleString(in_string, kMessageTextMaxRows,
kMessageTextMaxCols, false, out_string);
}
void EnforceMaxPromptSize(const base::string16& in_string,
base::string16* out_string) {
gfx::ElideRectangleString(in_string, kDefaultPromptMaxRows,
kDefaultPromptMaxCols, false, out_string);
}
#else
// One-dimensional eliding. Trust the window system to break the string
// appropriately, but limit its overall length to something reasonable.
const size_t kMessageTextMaxSize = 2000;
const size_t kDefaultPromptMaxSize = 2000;
void EnforceMaxTextSize(const base::string16& in_string,
base::string16* out_string) {
gfx::ElideString(in_string, kMessageTextMaxSize, out_string);
}
void EnforceMaxPromptSize(const base::string16& in_string,
base::string16* out_string) {
gfx::ElideString(in_string, kDefaultPromptMaxSize, out_string);
}
#endif
} // namespace
ChromeJavaScriptDialogExtraData::ChromeJavaScriptDialogExtraData()
: has_already_shown_a_dialog_(false),
suppress_javascript_messages_(false),
suppressed_dialog_count_(0) {}
JavaScriptAppModalDialog::JavaScriptAppModalDialog(
content::WebContents* web_contents,
ExtraDataMap* extra_data_map,
const base::string16& title,
content::JavaScriptMessageType javascript_message_type,
const base::string16& message_text,
const base::string16& default_prompt_text,
bool display_suppress_checkbox,
bool is_before_unload_dialog,
bool is_reload,
const content::JavaScriptDialogManager::DialogClosedCallback& callback)
: AppModalDialog(web_contents, title),
extra_data_map_(extra_data_map),
javascript_message_type_(javascript_message_type),
display_suppress_checkbox_(display_suppress_checkbox),
is_before_unload_dialog_(is_before_unload_dialog),
is_reload_(is_reload),
callback_(callback),
use_override_prompt_text_(false),
creation_time_(base::TimeTicks::Now()) {
EnforceMaxTextSize(message_text, &message_text_);
EnforceMaxPromptSize(default_prompt_text, &default_prompt_text_);
}
JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
}
NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
return JavaScriptDialogManager::GetInstance()
->native_dialog_factory()
->CreateNativeJavaScriptDialog(this);
}
bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() {
return true;
}
void JavaScriptAppModalDialog::Invalidate(bool suppress_callbacks) {
if (!IsValid())
return;
AppModalDialog::Invalidate(suppress_callbacks);
if (!suppress_callbacks)
CallDialogClosedCallback(false, base::string16());
if (native_dialog())
CloseModalDialog();
}
void JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) {
// We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
// will receive its activation messages before this dialog receives
// WM_DESTROY. The parent frame would then try to activate any modal dialogs
// that were still open in the ModalDialogQueue, which would send activation
// back to this one. The framework should be improved to handle this, so this
// is a temporary workaround.
CompleteDialog();
NotifyDelegate(false, base::string16(), suppress_js_messages);
}
void JavaScriptAppModalDialog::OnAccept(const base::string16& prompt_text,
bool suppress_js_messages) {
base::string16 prompt_text_to_use = prompt_text;
// This is only for testing.
if (use_override_prompt_text_)
prompt_text_to_use = override_prompt_text_;
CompleteDialog();
NotifyDelegate(true, prompt_text_to_use, suppress_js_messages);
}
void JavaScriptAppModalDialog::OnClose() {
NotifyDelegate(false, base::string16(), false);
}
void JavaScriptAppModalDialog::SetOverridePromptText(
const base::string16& override_prompt_text) {
override_prompt_text_ = override_prompt_text;
use_override_prompt_text_ = true;
}
void JavaScriptAppModalDialog::NotifyDelegate(bool success,
const base::string16& user_input,
bool suppress_js_messages) {
if (!IsValid())
return;
CallDialogClosedCallback(success, user_input);
// The close callback above may delete web_contents_, thus removing the extra
// data from the map owned by ::JavaScriptDialogManager. Make sure
// to only use the data if still present. http://crbug.com/236476
ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents());
if (extra_data != extra_data_map_->end()) {
extra_data->second.has_already_shown_a_dialog_ = true;
extra_data->second.suppress_javascript_messages_ = suppress_js_messages;
}
// On Views, we can end up coming through this code path twice :(.
// See crbug.com/63732.
AppModalDialog::Invalidate(false);
}
void JavaScriptAppModalDialog::CallDialogClosedCallback(bool success,
const base::string16& user_input) {
// TODO(joenotcharles): Both the callers of this function also check IsValid
// and call AppModalDialog::Invalidate, but in different orders. If the
// difference is not significant, more common code could be moved here.
UMA_HISTOGRAM_MEDIUM_TIMES(
"JSDialogs.FineTiming.TimeBetweenDialogCreatedAndSameDialogClosed",
base::TimeTicks::Now() - creation_time_);
if (!callback_.is_null()) {
callback_.Run(success, user_input);
callback_.Reset();
}
}
} // namespace app_modal