Home | History | Annotate | Download | only in app_modal_dialogs
      1 // Copyright (c) 2012 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/javascript_app_modal_dialog.h"
      6 
      7 #include "chrome/browser/browser_shutdown.h"
      8 #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
      9 #include "content/public/browser/web_contents.h"
     10 #include "ui/gfx/text_elider.h"
     11 
     12 #if defined(USE_AURA)
     13 #include "ui/aura/window.h"
     14 #include "ui/aura/window_event_dispatcher.h"
     15 #endif
     16 
     17 using content::JavaScriptDialogManager;
     18 using content::WebContents;
     19 
     20 namespace {
     21 
     22 // Control maximum sizes of various texts passed to us from javascript.
     23 #if defined(OS_POSIX) && !defined(OS_MACOSX)
     24 // Two-dimensional eliding.  Reformat the text of the message dialog
     25 // inserting line breaks because otherwise a single long line can overflow
     26 // the message dialog (and crash/hang the GTK, depending on the version).
     27 const int kMessageTextMaxRows = 32;
     28 const int kMessageTextMaxCols = 132;
     29 const int kDefaultPromptMaxRows = 24;
     30 const int kDefaultPromptMaxCols = 132;
     31 void EnforceMaxTextSize(const base::string16& in_string,
     32                         base::string16* out_string) {
     33   gfx::ElideRectangleString(in_string, kMessageTextMaxRows,
     34                            kMessageTextMaxCols, false, out_string);
     35 }
     36 void EnforceMaxPromptSize(const base::string16& in_string,
     37                           base::string16* out_string) {
     38   gfx::ElideRectangleString(in_string, kDefaultPromptMaxRows,
     39                            kDefaultPromptMaxCols, false, out_string);
     40 }
     41 #else
     42 // One-dimensional eliding.  Trust the window system to break the string
     43 // appropriately, but limit its overall length to something reasonable.
     44 const int kMessageTextMaxSize = 3000;
     45 const int kDefaultPromptMaxSize = 2000;
     46 void EnforceMaxTextSize(const base::string16& in_string,
     47                         base::string16* out_string) {
     48   gfx::ElideString(in_string, kMessageTextMaxSize, out_string);
     49 }
     50 void EnforceMaxPromptSize(const base::string16& in_string,
     51                           base::string16* out_string) {
     52   gfx::ElideString(in_string, kDefaultPromptMaxSize, out_string);
     53 }
     54 #endif
     55 
     56 }  // namespace
     57 
     58 ChromeJavaScriptDialogExtraData::ChromeJavaScriptDialogExtraData()
     59     : suppress_javascript_messages_(false) {
     60 }
     61 
     62 JavaScriptAppModalDialog::JavaScriptAppModalDialog(
     63     WebContents* web_contents,
     64     ExtraDataMap* extra_data_map,
     65     const base::string16& title,
     66     content::JavaScriptMessageType javascript_message_type,
     67     const base::string16& message_text,
     68     const base::string16& default_prompt_text,
     69     bool display_suppress_checkbox,
     70     bool is_before_unload_dialog,
     71     bool is_reload,
     72     const JavaScriptDialogManager::DialogClosedCallback& callback)
     73     : AppModalDialog(web_contents, title),
     74       extra_data_map_(extra_data_map),
     75       javascript_message_type_(javascript_message_type),
     76       display_suppress_checkbox_(display_suppress_checkbox),
     77       is_before_unload_dialog_(is_before_unload_dialog),
     78       is_reload_(is_reload),
     79       callback_(callback),
     80       use_override_prompt_text_(false) {
     81   EnforceMaxTextSize(message_text, &message_text_);
     82   EnforceMaxPromptSize(default_prompt_text, &default_prompt_text_);
     83 }
     84 
     85 JavaScriptAppModalDialog::~JavaScriptAppModalDialog() {
     86 }
     87 
     88 NativeAppModalDialog* JavaScriptAppModalDialog::CreateNativeDialog() {
     89   gfx::NativeWindow parent_window = web_contents()->GetTopLevelNativeWindow();
     90 
     91 #if defined(USE_AURA)
     92   if (!parent_window->GetRootWindow()) {
     93     // When we are part of a WebContents that isn't actually being displayed on
     94     // the screen, we can't actually attach to it.
     95     parent_window = NULL;
     96   }
     97 #endif  // defined(USE_AURA)
     98 
     99   return NativeAppModalDialog::CreateNativeJavaScriptPrompt(this,
    100                                                             parent_window);
    101 }
    102 
    103 bool JavaScriptAppModalDialog::IsJavaScriptModalDialog() {
    104   return true;
    105 }
    106 
    107 void JavaScriptAppModalDialog::Invalidate() {
    108   if (!IsValid())
    109     return;
    110 
    111   AppModalDialog::Invalidate();
    112   if (!callback_.is_null()) {
    113     callback_.Run(false, base::string16());
    114     callback_.Reset();
    115   }
    116   if (native_dialog())
    117     CloseModalDialog();
    118 }
    119 
    120 void JavaScriptAppModalDialog::OnCancel(bool suppress_js_messages) {
    121   // We need to do this before WM_DESTROY (WindowClosing()) as any parent frame
    122   // will receive its activation messages before this dialog receives
    123   // WM_DESTROY. The parent frame would then try to activate any modal dialogs
    124   // that were still open in the ModalDialogQueue, which would send activation
    125   // back to this one. The framework should be improved to handle this, so this
    126   // is a temporary workaround.
    127   CompleteDialog();
    128 
    129   NotifyDelegate(false, base::string16(), suppress_js_messages);
    130 }
    131 
    132 void JavaScriptAppModalDialog::OnAccept(const base::string16& prompt_text,
    133                                         bool suppress_js_messages) {
    134   base::string16 prompt_text_to_use = prompt_text;
    135   // This is only for testing.
    136   if (use_override_prompt_text_)
    137     prompt_text_to_use = override_prompt_text_;
    138 
    139   CompleteDialog();
    140   NotifyDelegate(true, prompt_text_to_use, suppress_js_messages);
    141 }
    142 
    143 void JavaScriptAppModalDialog::OnClose() {
    144   NotifyDelegate(false, base::string16(), false);
    145 }
    146 
    147 void JavaScriptAppModalDialog::SetOverridePromptText(
    148     const base::string16& override_prompt_text) {
    149   override_prompt_text_ = override_prompt_text;
    150   use_override_prompt_text_ = true;
    151 }
    152 
    153 void JavaScriptAppModalDialog::NotifyDelegate(bool success,
    154                                               const base::string16& user_input,
    155                                               bool suppress_js_messages) {
    156   if (!IsValid())
    157     return;
    158 
    159   if (!callback_.is_null()) {
    160     callback_.Run(success, user_input);
    161     callback_.Reset();
    162   }
    163 
    164   // The callback_ above may delete web_contents_, thus removing the extra
    165   // data from the map owned by ChromeJavaScriptDialogManager. Make sure
    166   // to only use the data if still present. http://crbug.com/236476
    167   ExtraDataMap::iterator extra_data = extra_data_map_->find(web_contents());
    168   if (extra_data != extra_data_map_->end()) {
    169     extra_data->second.last_javascript_message_dismissal_ =
    170         base::TimeTicks::Now();
    171     extra_data->second.suppress_javascript_messages_ = suppress_js_messages;
    172   }
    173 
    174   // On Views, we can end up coming through this code path twice :(.
    175   // See crbug.com/63732.
    176   AppModalDialog::Invalidate();
    177 }
    178