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