Home | History | Annotate | Download | only in app_modal_dialogs
      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/app_modal_dialogs/js_modal_dialog.h"
      6 
      7 #include "base/string_util.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/browser_shutdown.h"
     10 #include "chrome/browser/extensions/extension_host.h"
     11 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
     12 #include "content/browser/tab_contents/tab_contents.h"
     13 #include "content/common/notification_service.h"
     14 #include "content/common/notification_type.h"
     15 #include "ui/base/text/text_elider.h"
     16 
     17 namespace {
     18 
     19 // Control maximum sizes of various texts passed to us from javascript.
     20 #ifdef OS_LINUX
     21 // Two-dimensional eliding.  Reformat the text of the message dialog
     22 // inserting line breaks because otherwise a single long line can overflow
     23 // the message dialog (and crash/hang the GTK, depending on the version).
     24 const int kMessageTextMaxRows = 32;
     25 const int kMessageTextMaxCols = 132;
     26 const int kDefaultPromptMaxRows = 24;
     27 const int kDefaultPromptMaxCols = 132;
     28 void EnforceMaxTextSize(const string16& in_string, string16* out_string) {
     29   ui::ElideRectangleString(in_string, kMessageTextMaxRows,
     30                            kMessageTextMaxCols, false, out_string);
     31 }
     32 void EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
     33   ui::ElideRectangleString(in_string, kDefaultPromptMaxRows,
     34                            kDefaultPromptMaxCols, false, out_string);
     35 }
     36 #else
     37 // One-dimensional eliding.  Trust the window system to break the string
     38 // appropriately, but limit its overall length to something reasonable.
     39 const int kMessageTextMaxSize = 3000;
     40 const int kDefaultPromptMaxSize = 2000;
     41 void EnforceMaxTextSize(const string16& in_string, string16* out_string) {
     42   ui::ElideString(in_string, kMessageTextMaxSize, out_string);
     43 }
     44 void EnforceMaxPromptSize(const string16& in_string, string16* out_string) {
     45   ui::ElideString(in_string, kDefaultPromptMaxSize, out_string);
     46 }
     47 #endif
     48 
     49 }  // namespace
     50 
     51 JavaScriptAppModalDialog::JavaScriptAppModalDialog(
     52     JavaScriptAppModalDialogDelegate* delegate,
     53     const std::wstring& title,
     54     int dialog_flags,
     55     const std::wstring& message_text,
     56     const std::wstring& default_prompt_text,
     57     bool display_suppress_checkbox,
     58     bool is_before_unload_dialog,
     59     IPC::Message* reply_msg)
     60     : AppModalDialog(delegate->AsTabContents(), title),
     61       delegate_(delegate),
     62       extension_host_(delegate->AsExtensionHost()),
     63       dialog_flags_(dialog_flags),
     64       display_suppress_checkbox_(display_suppress_checkbox),
     65       is_before_unload_dialog_(is_before_unload_dialog),
     66       reply_msg_(reply_msg) {
     67   string16 elided_text;
     68   EnforceMaxTextSize(WideToUTF16(message_text), &elided_text);
     69   message_text_ = UTF16ToWide(elided_text);
     70   EnforceMaxPromptSize(WideToUTF16Hack(default_prompt_text),
     71                        &default_prompt_text_);
     72 
     73   DCHECK((tab_contents_ != NULL) != (extension_host_ != NULL));
     74   InitNotifications();
     75 }
     76 
     77 JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
     78 }
     79 
     80 NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
     81   gfx::NativeWindow parent_window = tab_contents_ ?
     82       tab_contents_->GetMessageBoxRootWindow() :
     83       extension_host_->GetMessageBoxRootWindow();
     84   return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this,
     85                                                             parent_window);
     86 }
     87 
     88 void JavaScriptAppModalDialog::Observe(NotificationType type,
     89                                        const NotificationSource& source,
     90                                        const NotificationDetails& details) {
     91   if (skip_this_dialog_)
     92     return;
     93 
     94   if (NotificationType::EXTENSION_HOST_DESTROYED == type &&
     95       Details<ExtensionHost>(extension_host_) != details)
     96     return;
     97 
     98   // If we reach here, we know the notification is relevant to us, either
     99   // because we're only observing applicable sources or because we passed the
    100   // check above. Both of those indicate that we should ignore this dialog.
    101   // Also clear the delegate, since it's now invalid.
    102   skip_this_dialog_ = true;
    103   delegate_ = NULL;
    104   if (native_dialog_)
    105     CloseModalDialog();
    106 }
    107 
    108 void JavaScriptAppModalDialog::InitNotifications() {
    109   // Make sure we get relevant navigation notifications so we know when our
    110   // parent contents will disappear or navigate to a different page.
    111   if (tab_contents_) {
    112     registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
    113                    Source<NavigationController>(&tab_contents_->controller()));
    114     registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
    115                    Source<TabContents>(tab_contents_));
    116   } else if (extension_host_) {
    117     // EXTENSION_HOST_DESTROYED uses the Profile as its source, but we care
    118     // about the ExtensionHost (which is passed in the details).
    119     registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
    120                    NotificationService::AllSources());
    121   } else {
    122     NOTREACHED();
    123   }
    124 }
    125 
    126 void JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) {
    127   // If we are shutting down and this is an onbeforeunload dialog, cancel the
    128   // shutdown.
    129   if (is_before_unload_dialog_)
    130     browser_shutdown::SetTryingToQuit(false);
    131 
    132   // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
    133   // will receive its activation messages before this dialog receives
    134   // WM_DESTROY. The parent frame would then try to activate any modal dialogs
    135   // that were still open in the ModalDialogQueue, which would send activation
    136   // back to this one. The framework should be improved to handle this, so this
    137   // is a temporary workaround.
    138   CompleteDialog();
    139 
    140   NotifyDelegate(false, L"", suppress_js_messages);
    141 }
    142 
    143 void JavaScriptAppModalDialog::OnAccept(const std::wstring& prompt_text,
    144                                         bool suppress_js_messages) {
    145   CompleteDialog();
    146   NotifyDelegate(true, prompt_text, suppress_js_messages);
    147 }
    148 
    149 void JavaScriptAppModalDialog::OnClose() {
    150   NotifyDelegate(false, L"", false);
    151 }
    152 
    153 void JavaScriptAppModalDialog::NotifyDelegate(bool success,
    154                                               const std::wstring& prompt_text,
    155                                               bool suppress_js_messages) {
    156   if (skip_this_dialog_)
    157     return;
    158 
    159   delegate_->OnMessageBoxClosed(reply_msg_, success, prompt_text);
    160   if (suppress_js_messages)
    161     delegate_->SetSuppressMessageBoxes(true);
    162 
    163   // On Views, we can end up coming through this code path twice :(.
    164   // See crbug.com/63732.
    165   skip_this_dialog_ = true;
    166 }
    167