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