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 "ui/views/controls/webview/web_dialog_view.h"
6
7#include <vector>
8
9#include "base/strings/utf_string_conversions.h"
10#include "content/public/browser/browser_context.h"
11#include "content/public/browser/native_web_keyboard_event.h"
12#include "content/public/browser/notification_details.h"
13#include "content/public/browser/notification_source.h"
14#include "content/public/browser/notification_types.h"
15#include "content/public/browser/render_frame_host.h"
16#include "content/public/browser/web_contents.h"
17#include "ui/events/event.h"
18#include "ui/events/keycodes/keyboard_codes.h"
19#include "ui/views/controls/webview/webview.h"
20#include "ui/views/layout/fill_layout.h"
21#include "ui/views/widget/native_widget_private.h"
22#include "ui/views/widget/root_view.h"
23#include "ui/views/widget/widget.h"
24#include "ui/web_dialogs/web_dialog_delegate.h"
25#include "ui/web_dialogs/web_dialog_ui.h"
26
27using content::NativeWebKeyboardEvent;
28using content::WebContents;
29using content::WebUIMessageHandler;
30using ui::WebDialogDelegate;
31using ui::WebDialogUI;
32using ui::WebDialogWebContentsDelegate;
33
34namespace views {
35
36////////////////////////////////////////////////////////////////////////////////
37// WebDialogView, public:
38
39WebDialogView::WebDialogView(
40    content::BrowserContext* context,
41    WebDialogDelegate* delegate,
42    WebContentsHandler* handler)
43    : ClientView(NULL, NULL),
44      WebDialogWebContentsDelegate(context, handler),
45      delegate_(delegate),
46      web_view_(new views::WebView(context)),
47      is_attempting_close_dialog_(false),
48      before_unload_fired_(false),
49      closed_via_webui_(false),
50      close_contents_called_(false) {
51  web_view_->set_allow_accelerators(true);
52  AddChildView(web_view_);
53  set_contents_view(web_view_);
54  SetLayoutManager(new views::FillLayout);
55  // Pressing the ESC key will close the dialog.
56  AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
57}
58
59WebDialogView::~WebDialogView() {
60}
61
62content::WebContents* WebDialogView::web_contents() {
63  return web_view_->web_contents();
64}
65
66////////////////////////////////////////////////////////////////////////////////
67// WebDialogView, views::View implementation:
68
69gfx::Size WebDialogView::GetPreferredSize() const {
70  gfx::Size out;
71  if (delegate_)
72    delegate_->GetDialogSize(&out);
73  return out;
74}
75
76gfx::Size WebDialogView::GetMinimumSize() const {
77  gfx::Size out;
78  if (delegate_)
79    delegate_->GetMinimumDialogSize(&out);
80  return out;
81}
82
83bool WebDialogView::AcceleratorPressed(const ui::Accelerator& accelerator) {
84  // Pressing ESC closes the dialog.
85  DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
86  if (GetWidget())
87    GetWidget()->Close();
88  return true;
89}
90
91void WebDialogView::ViewHierarchyChanged(
92    const ViewHierarchyChangedDetails& details) {
93  if (details.is_add && GetWidget())
94    InitDialog();
95}
96
97bool WebDialogView::CanClose() {
98  // Don't close UI if |delegate_| does not allow users to close it by
99  // clicking on "x" button or pressing Esc shortcut key on hosting dialog.
100  if (!delegate_->CanCloseDialog() && !close_contents_called_)
101    return false;
102
103  // If CloseContents() is called before CanClose(), which is called by
104  // RenderViewHostImpl::ClosePageIgnoringUnloadEvents, it indicates
105  // beforeunload event should not be fired during closing.
106  if ((is_attempting_close_dialog_ && before_unload_fired_) ||
107      close_contents_called_) {
108    is_attempting_close_dialog_ = false;
109    before_unload_fired_ = false;
110    return true;
111  }
112
113  if (!is_attempting_close_dialog_) {
114    // Fire beforeunload event when user attempts to close the dialog.
115    is_attempting_close_dialog_ = true;
116    web_view_->web_contents()->DispatchBeforeUnload(false);
117  }
118  return false;
119}
120
121////////////////////////////////////////////////////////////////////////////////
122// WebDialogView, views::WidgetDelegate implementation:
123
124bool WebDialogView::CanResize() const {
125  if (delegate_)
126    return delegate_->CanResizeDialog();
127  return true;
128}
129
130ui::ModalType WebDialogView::GetModalType() const {
131  return GetDialogModalType();
132}
133
134base::string16 WebDialogView::GetWindowTitle() const {
135  if (delegate_)
136    return delegate_->GetDialogTitle();
137  return base::string16();
138}
139
140std::string WebDialogView::GetWindowName() const {
141  if (delegate_)
142    return delegate_->GetDialogName();
143  return std::string();
144}
145
146void WebDialogView::WindowClosing() {
147  // If we still have a delegate that means we haven't notified it of the
148  // dialog closing. This happens if the user clicks the Close button on the
149  // dialog.
150  if (delegate_)
151    OnDialogClosed("");
152}
153
154views::View* WebDialogView::GetContentsView() {
155  return this;
156}
157
158views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) {
159  return this;
160}
161
162views::View* WebDialogView::GetInitiallyFocusedView() {
163  return web_view_;
164}
165
166bool WebDialogView::ShouldShowWindowTitle() const {
167  return ShouldShowDialogTitle();
168}
169
170views::Widget* WebDialogView::GetWidget() {
171  return View::GetWidget();
172}
173
174const views::Widget* WebDialogView::GetWidget() const {
175  return View::GetWidget();
176}
177
178////////////////////////////////////////////////////////////////////////////////
179// WebDialogDelegate implementation:
180
181ui::ModalType WebDialogView::GetDialogModalType() const {
182  if (delegate_)
183    return delegate_->GetDialogModalType();
184  return ui::MODAL_TYPE_NONE;
185}
186
187base::string16 WebDialogView::GetDialogTitle() const {
188  return GetWindowTitle();
189}
190
191GURL WebDialogView::GetDialogContentURL() const {
192  if (delegate_)
193    return delegate_->GetDialogContentURL();
194  return GURL();
195}
196
197void WebDialogView::GetWebUIMessageHandlers(
198    std::vector<WebUIMessageHandler*>* handlers) const {
199  if (delegate_)
200    delegate_->GetWebUIMessageHandlers(handlers);
201}
202
203void WebDialogView::GetDialogSize(gfx::Size* size) const {
204  if (delegate_)
205    delegate_->GetDialogSize(size);
206}
207
208void WebDialogView::GetMinimumDialogSize(gfx::Size* size) const {
209  if (delegate_)
210    delegate_->GetMinimumDialogSize(size);
211}
212
213std::string WebDialogView::GetDialogArgs() const {
214  if (delegate_)
215    return delegate_->GetDialogArgs();
216  return std::string();
217}
218
219void WebDialogView::OnDialogShown(content::WebUI* webui,
220                                  content::RenderViewHost* render_view_host) {
221  if (delegate_)
222    delegate_->OnDialogShown(webui, render_view_host);
223}
224
225void WebDialogView::OnDialogClosed(const std::string& json_retval) {
226  Detach();
227  if (delegate_) {
228    // Store the dialog content area size.
229    delegate_->StoreDialogSize(GetContentsBounds().size());
230  }
231
232  if (GetWidget())
233    GetWidget()->Close();
234
235  if (delegate_) {
236    delegate_->OnDialogClosed(json_retval);
237    delegate_ = NULL;  // We will not communicate further with the delegate.
238  }
239}
240
241void WebDialogView::OnDialogCloseFromWebUI(const std::string& json_retval) {
242  closed_via_webui_ = true;
243  dialog_close_retval_ = json_retval;
244  if (GetWidget())
245    GetWidget()->Close();
246}
247
248void WebDialogView::OnCloseContents(WebContents* source,
249                                    bool* out_close_dialog) {
250  if (delegate_)
251    delegate_->OnCloseContents(source, out_close_dialog);
252}
253
254bool WebDialogView::ShouldShowDialogTitle() const {
255  if (delegate_)
256    return delegate_->ShouldShowDialogTitle();
257  return true;
258}
259
260bool WebDialogView::HandleContextMenu(
261    const content::ContextMenuParams& params) {
262  if (delegate_)
263    return delegate_->HandleContextMenu(params);
264  return WebDialogWebContentsDelegate::HandleContextMenu(params);
265}
266
267////////////////////////////////////////////////////////////////////////////////
268// content::WebContentsDelegate implementation:
269
270void WebDialogView::MoveContents(WebContents* source, const gfx::Rect& pos) {
271  // The contained web page wishes to resize itself. We let it do this because
272  // if it's a dialog we know about, we trust it not to be mean to the user.
273  GetWidget()->SetBounds(pos);
274}
275
276// A simplified version of BrowserView::HandleKeyboardEvent().
277// We don't handle global keyboard shortcuts here, but that's fine since
278// they're all browser-specific. (This may change in the future.)
279void WebDialogView::HandleKeyboardEvent(content::WebContents* source,
280                                        const NativeWebKeyboardEvent& event) {
281  if (!event.os_event)
282    return;
283
284  GetWidget()->native_widget_private()->RepostNativeEvent(event.os_event);
285}
286
287void WebDialogView::CloseContents(WebContents* source) {
288  close_contents_called_ = true;
289  bool close_dialog = false;
290  OnCloseContents(source, &close_dialog);
291  if (close_dialog)
292    OnDialogClosed(closed_via_webui_ ? dialog_close_retval_ : std::string());
293}
294
295content::WebContents* WebDialogView::OpenURLFromTab(
296    content::WebContents* source,
297    const content::OpenURLParams& params) {
298  content::WebContents* new_contents = NULL;
299  if (delegate_ &&
300      delegate_->HandleOpenURLFromTab(source, params, &new_contents)) {
301    return new_contents;
302  }
303  return WebDialogWebContentsDelegate::OpenURLFromTab(source, params);
304}
305
306void WebDialogView::AddNewContents(content::WebContents* source,
307                                   content::WebContents* new_contents,
308                                   WindowOpenDisposition disposition,
309                                   const gfx::Rect& initial_pos,
310                                   bool user_gesture,
311                                   bool* was_blocked) {
312  if (delegate_ && delegate_->HandleAddNewContents(
313          source, new_contents, disposition, initial_pos, user_gesture)) {
314    return;
315  }
316  WebDialogWebContentsDelegate::AddNewContents(
317      source, new_contents, disposition, initial_pos, user_gesture,
318      was_blocked);
319}
320
321void WebDialogView::LoadingStateChanged(content::WebContents* source,
322    bool to_different_document) {
323  if (delegate_)
324    delegate_->OnLoadingStateChanged(source);
325}
326
327void WebDialogView::BeforeUnloadFired(content::WebContents* tab,
328                                      bool proceed,
329                                      bool* proceed_to_fire_unload) {
330  before_unload_fired_ = true;
331  *proceed_to_fire_unload = proceed;
332}
333
334////////////////////////////////////////////////////////////////////////////////
335// WebDialogView, private:
336
337void WebDialogView::InitDialog() {
338  content::WebContents* web_contents = web_view_->GetWebContents();
339  if (web_contents->GetDelegate() == this)
340    return;
341
342  web_contents->SetDelegate(this);
343
344  // Set the delegate. This must be done before loading the page. See
345  // the comment above WebDialogUI in its header file for why.
346  WebDialogUI::SetDelegate(web_contents, this);
347
348  web_view_->LoadInitialURL(GetDialogContentURL());
349}
350
351}  // namespace views
352