1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/gtk/js_modal_dialog_gtk.h" 6 7 #include <gtk/gtk.h> 8 9 #include "base/logging.h" 10 #include "base/utf_string_conversions.h" 11 #include "chrome/browser/ui/app_modal_dialogs/js_modal_dialog.h" 12 #include "chrome/browser/ui/gtk/gtk_util.h" 13 #include "grit/generated_resources.h" 14 #include "grit/locale_settings.h" 15 #include "ui/base/l10n/l10n_util.h" 16 #include "ui/base/message_box_flags.h" 17 18 namespace { 19 20 // We stash pointers to widgets on the gtk_dialog so we can refer to them 21 // after dialog creation. 22 const char kPromptTextId[] = "chrome_prompt_text"; 23 const char kSuppressCheckboxId[] = "chrome_suppress_checkbox"; 24 25 // If there's a text entry in the dialog, get the text from the first one and 26 // return it. 27 std::wstring GetPromptText(GtkDialog* dialog) { 28 GtkWidget* widget = static_cast<GtkWidget*>( 29 g_object_get_data(G_OBJECT(dialog), kPromptTextId)); 30 if (widget) 31 return UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(widget))); 32 return std::wstring(); 33 } 34 35 // If there's a toggle button in the dialog, return the toggled state. 36 // Otherwise, return false. 37 bool ShouldSuppressJSDialogs(GtkDialog* dialog) { 38 GtkWidget* widget = static_cast<GtkWidget*>( 39 g_object_get_data(G_OBJECT(dialog), kSuppressCheckboxId)); 40 if (widget) 41 return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); 42 return false; 43 } 44 45 } // namespace 46 47 //////////////////////////////////////////////////////////////////////////////// 48 // JSModalDialogGtk, public: 49 50 JSModalDialogGtk::JSModalDialogGtk(JavaScriptAppModalDialog* dialog, 51 gfx::NativeWindow parent_window) 52 : dialog_(dialog) { 53 GtkButtonsType buttons = GTK_BUTTONS_NONE; 54 GtkMessageType message_type = GTK_MESSAGE_OTHER; 55 56 // We add in the OK button manually later because we want to focus it 57 // explicitly. 58 switch (dialog_->dialog_flags()) { 59 case ui::MessageBoxFlags::kIsJavascriptAlert: 60 buttons = GTK_BUTTONS_NONE; 61 message_type = GTK_MESSAGE_WARNING; 62 break; 63 64 case ui::MessageBoxFlags::kIsJavascriptConfirm: 65 if (dialog_->is_before_unload_dialog()) { 66 // onbeforeunload also uses a confirm prompt, it just has custom 67 // buttons. We add the buttons using gtk_dialog_add_button below. 68 buttons = GTK_BUTTONS_NONE; 69 } else { 70 buttons = GTK_BUTTONS_CANCEL; 71 } 72 message_type = GTK_MESSAGE_QUESTION; 73 break; 74 75 case ui::MessageBoxFlags::kIsJavascriptPrompt: 76 buttons = GTK_BUTTONS_CANCEL; 77 message_type = GTK_MESSAGE_QUESTION; 78 break; 79 80 default: 81 NOTREACHED(); 82 } 83 84 // We want the alert to be app modal so put all the browser windows into the 85 // same window group. 86 gtk_util::MakeAppModalWindowGroup(); 87 88 gtk_dialog_ = gtk_message_dialog_new(parent_window, 89 GTK_DIALOG_MODAL, message_type, buttons, "%s", 90 WideToUTF8(dialog_->message_text()).c_str()); 91 gtk_util::ApplyMessageDialogQuirks(gtk_dialog_); 92 gtk_window_set_title(GTK_WINDOW(gtk_dialog_), 93 WideToUTF8(dialog_->title()).c_str()); 94 95 // Adjust content area as needed. Set up the prompt text entry or 96 // suppression check box. 97 if (ui::MessageBoxFlags::kIsJavascriptPrompt == dialog_->dialog_flags()) { 98 // TODO(tc): Replace with gtk_dialog_get_content_area() when using GTK 2.14+ 99 GtkWidget* contents_vbox = GTK_DIALOG(gtk_dialog_)->vbox; 100 GtkWidget* text_box = gtk_entry_new(); 101 gtk_entry_set_text(GTK_ENTRY(text_box), 102 WideToUTF8(dialog_->default_prompt_text()).c_str()); 103 gtk_box_pack_start(GTK_BOX(contents_vbox), text_box, TRUE, TRUE, 0); 104 g_object_set_data(G_OBJECT(gtk_dialog_), kPromptTextId, text_box); 105 gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE); 106 } 107 108 if (dialog_->display_suppress_checkbox()) { 109 GtkWidget* contents_vbox = GTK_DIALOG(gtk_dialog_)->vbox; 110 GtkWidget* check_box = gtk_check_button_new_with_label( 111 l10n_util::GetStringUTF8( 112 IDS_JAVASCRIPT_MESSAGEBOX_SUPPRESS_OPTION).c_str()); 113 gtk_box_pack_start(GTK_BOX(contents_vbox), check_box, TRUE, TRUE, 0); 114 g_object_set_data(G_OBJECT(gtk_dialog_), kSuppressCheckboxId, check_box); 115 } 116 117 // Adjust buttons/action area as needed. 118 if (dialog_->is_before_unload_dialog()) { 119 std::string button_text = l10n_util::GetStringUTF8( 120 IDS_BEFOREUNLOAD_MESSAGEBOX_OK_BUTTON_LABEL); 121 gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), button_text.c_str(), 122 GTK_RESPONSE_OK); 123 124 button_text = l10n_util::GetStringUTF8( 125 IDS_BEFOREUNLOAD_MESSAGEBOX_CANCEL_BUTTON_LABEL); 126 gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), button_text.c_str(), 127 GTK_RESPONSE_CANCEL); 128 } else { 129 // Add the OK button and focus it. 130 GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(gtk_dialog_), 131 GTK_STOCK_OK, GTK_RESPONSE_OK); 132 if (ui::MessageBoxFlags::kIsJavascriptPrompt != dialog_->dialog_flags()) 133 gtk_widget_grab_focus(ok_button); 134 } 135 136 gtk_dialog_set_default_response(GTK_DIALOG(gtk_dialog_), GTK_RESPONSE_OK); 137 g_signal_connect(gtk_dialog_, "response", G_CALLBACK(OnResponseThunk), this); 138 } 139 140 JSModalDialogGtk::~JSModalDialogGtk() { 141 } 142 143 //////////////////////////////////////////////////////////////////////////////// 144 // JSModalDialogGtk, NativeAppModalDialog implementation: 145 146 int JSModalDialogGtk::GetAppModalDialogButtons() const { 147 switch (dialog_->dialog_flags()) { 148 case ui::MessageBoxFlags::kIsJavascriptAlert: 149 return ui::MessageBoxFlags::DIALOGBUTTON_OK; 150 151 case ui::MessageBoxFlags::kIsJavascriptConfirm: 152 return ui::MessageBoxFlags::DIALOGBUTTON_OK | 153 ui::MessageBoxFlags::DIALOGBUTTON_CANCEL; 154 155 case ui::MessageBoxFlags::kIsJavascriptPrompt: 156 return ui::MessageBoxFlags::DIALOGBUTTON_OK; 157 158 default: 159 NOTREACHED(); 160 return 0; 161 } 162 } 163 164 void JSModalDialogGtk::ShowAppModalDialog() { 165 gtk_util::ShowDialogWithMinLocalizedWidth(GTK_WIDGET(gtk_dialog_), 166 IDS_ALERT_DIALOG_WIDTH_CHARS); 167 } 168 169 void JSModalDialogGtk::ActivateAppModalDialog() { 170 DCHECK(gtk_dialog_); 171 gtk_window_present(GTK_WINDOW(gtk_dialog_));} 172 173 void JSModalDialogGtk::CloseAppModalDialog() { 174 DCHECK(gtk_dialog_); 175 OnResponse(gtk_dialog_, GTK_RESPONSE_DELETE_EVENT); 176 } 177 178 void JSModalDialogGtk::AcceptAppModalDialog() { 179 OnResponse(gtk_dialog_, GTK_RESPONSE_OK); 180 } 181 182 void JSModalDialogGtk::CancelAppModalDialog() { 183 OnResponse(gtk_dialog_, GTK_RESPONSE_CANCEL); 184 } 185 186 //////////////////////////////////////////////////////////////////////////////// 187 // JSModalDialogGtk, private: 188 189 void JSModalDialogGtk::OnResponse(GtkWidget* dialog, int response_id) { 190 switch (response_id) { 191 case GTK_RESPONSE_OK: 192 // The first arg is the prompt text and the second is true if we want to 193 // suppress additional popups from the page. 194 dialog_->OnAccept(GetPromptText(GTK_DIALOG(dialog)), 195 ShouldSuppressJSDialogs(GTK_DIALOG(dialog))); 196 break; 197 198 case GTK_RESPONSE_CANCEL: 199 case GTK_RESPONSE_DELETE_EVENT: // User hit the X on the dialog. 200 dialog_->OnCancel(ShouldSuppressJSDialogs(GTK_DIALOG(dialog))); 201 break; 202 203 default: 204 NOTREACHED(); 205 } 206 gtk_widget_destroy(dialog); 207 208 // Now that the dialog is gone, we can put all the windows into separate 209 // window groups so other dialogs are no longer app modal. 210 gtk_util::AppModalDismissedUngroupWindows(); 211 delete this; 212 } 213 214 //////////////////////////////////////////////////////////////////////////////// 215 // NativeAppModalDialog, public: 216 217 // static 218 NativeAppModalDialog* NativeAppModalDialog::CreateNativeJavaScriptPrompt( 219 JavaScriptAppModalDialog* dialog, 220 gfx::NativeWindow parent_window) { 221 return new JSModalDialogGtk(dialog, parent_window); 222 } 223