1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/app_modal_dialogs/js_modal_dialog.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/utf_string_conversions.h" 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_shutdown.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h" 1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h" 12dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h" 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/text/text_elider.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Control maximum sizes of various texts passed to us from javascript. 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#ifdef OS_LINUX 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Two-dimensional eliding. Reformat the text of the message dialog 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// inserting line breaks because otherwise a single long line can overflow 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// the message dialog (and crash/hang the GTK, depending on the version). 2421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenconst int kMessageTextMaxRows = 32; 2521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenconst int kMessageTextMaxCols = 132; 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxRows = 24; 27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxCols = 132; 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxTextSize(const string16& in_string, string16* out_string) { 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui::ElideRectangleString(in_string, kMessageTextMaxRows, 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen kMessageTextMaxCols, false, out_string); 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxPromptSize(const string16& in_string, string16* out_string) { 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui::ElideRectangleString(in_string, kDefaultPromptMaxRows, 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen kDefaultPromptMaxCols, false, out_string); 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#else 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// One-dimensional eliding. Trust the window system to break the string 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// appropriately, but limit its overall length to something reasonable. 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kMessageTextMaxSize = 3000; 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kDefaultPromptMaxSize = 2000; 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxTextSize(const string16& in_string, string16* out_string) { 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui::ElideString(in_string, kMessageTextMaxSize, out_string); 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid EnforceMaxPromptSize(const string16& in_string, string16* out_string) { 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui::ElideString(in_string, kDefaultPromptMaxSize, out_string); 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochJavaScriptAppModalDialog::JavaScriptAppModalDialog( 52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick JavaScriptAppModalDialogDelegate* delegate, 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& title, 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int dialog_flags, 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& message_text, 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& default_prompt_text, 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool display_suppress_checkbox, 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool is_before_unload_dialog, 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch IPC::Message* reply_msg) 60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : AppModalDialog(delegate->AsTabContents(), title), 61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick delegate_(delegate), 62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick extension_host_(delegate->AsExtensionHost()), 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch dialog_flags_(dialog_flags), 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch display_suppress_checkbox_(display_suppress_checkbox), 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_before_unload_dialog_(is_before_unload_dialog), 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch reply_msg_(reply_msg) { 6721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen string16 elided_text; 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen EnforceMaxTextSize(WideToUTF16(message_text), &elided_text); 6921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen message_text_ = UTF16ToWide(elided_text); 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen EnforceMaxPromptSize(WideToUTF16Hack(default_prompt_text), 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen &default_prompt_text_); 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK((tab_contents_ != NULL) != (extension_host_ != NULL)); 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch InitNotifications(); 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochJavaScriptAppModalDialog::~JavaScriptAppModalDialog() { 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 803345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickNativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() { 813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick gfx::NativeWindow parent_window = tab_contents_ ? 823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick tab_contents_->GetMessageBoxRootWindow() : 833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick extension_host_->GetMessageBoxRootWindow(); 843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this, 853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick parent_window); 863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::Observe(NotificationType type, 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationSource& source, 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationDetails& details) { 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (skip_this_dialog_) 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (NotificationType::EXTENSION_HOST_DESTROYED == type && 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Details<ExtensionHost>(extension_host_) != details) 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we reach here, we know the notification is relevant to us, either 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // because we're only observing applicable sources or because we passed the 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // check above. Both of those indicate that we should ignore this dialog. 101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // Also clear the delegate, since it's now invalid. 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch skip_this_dialog_ = true; 103731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick delegate_ = NULL; 1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (native_dialog_) 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CloseModalDialog(); 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::InitNotifications() { 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Make sure we get relevant navigation notifications so we know when our 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // parent contents will disappear or navigate to a different page. 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (tab_contents_) { 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<NavigationController>(&tab_contents_->controller())); 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<TabContents>(tab_contents_)); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (extension_host_) { 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // EXTENSION_HOST_DESTROYED uses the Profile as its source, but we care 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // about the ExtensionHost (which is passed in the details). 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED, 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NotificationService::AllSources()); 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 126731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) { 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we are shutting down and this is an onbeforeunload dialog, cancel the 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // shutdown. 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_before_unload_dialog_) 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser_shutdown::SetTryingToQuit(false); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // will receive its activation messages before this dialog receives 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // WM_DESTROY. The parent frame would then try to activate any modal dialogs 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // that were still open in the ModalDialogQueue, which would send activation 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // back to this one. The framework should be improved to handle this, so this 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // is a temporary workaround. 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CompleteDialog(); 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 14021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen NotifyDelegate(false, L"", suppress_js_messages); 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::OnAccept(const std::wstring& prompt_text, 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool suppress_js_messages) { 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CompleteDialog(); 14621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen NotifyDelegate(true, prompt_text, suppress_js_messages); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid JavaScriptAppModalDialog::OnClose() { 15021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen NotifyDelegate(false, L"", false); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 15321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid JavaScriptAppModalDialog::NotifyDelegate(bool success, 1549d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block const std::wstring& prompt_text, 1559d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block bool suppress_js_messages) { 1569d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block if (skip_this_dialog_) 1579d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block return; 1589d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block 1599d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block delegate_->OnMessageBoxClosed(reply_msg_, success, prompt_text); 1609d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block if (suppress_js_messages) 1619d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block delegate_->SetSuppressMessageBoxes(true); 1629d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block 16321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // On Views, we can end up coming through this code path twice :(. 16421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // See crbug.com/63732. 1659d6f8a4fbbdbaf61802f8e41de8afb692a0e3408Steve Block skip_this_dialog_ = true; 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 167