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 <utility>
8 #include <vector>
9
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/native_web_keyboard_event.h"
13 #include "content/public/browser/notification_details.h"
14 #include "content/public/browser/notification_source.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/web_contents.h"
18 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
19 #include "ui/events/event.h"
20 #include "ui/events/keycodes/keyboard_codes.h"
21 #include "ui/views/controls/webview/webview.h"
22 #include "ui/views/layout/fill_layout.h"
23 #include "ui/views/widget/native_widget_private.h"
24 #include "ui/views/widget/root_view.h"
25 #include "ui/views/widget/widget.h"
26 #include "ui/views/window/dialog_delegate.h"
27 #include "ui/web_dialogs/web_dialog_delegate.h"
28 #include "ui/web_dialogs/web_dialog_ui.h"
29
30 using content::NativeWebKeyboardEvent;
31 using content::WebContents;
32 using content::WebUIMessageHandler;
33 using ui::WebDialogDelegate;
34 using ui::WebDialogUIBase;
35 using ui::WebDialogWebContentsDelegate;
36
37 namespace views {
38
ObservableWebView(content::BrowserContext * browser_context,WebDialogDelegate * delegate)39 ObservableWebView::ObservableWebView(content::BrowserContext* browser_context,
40 WebDialogDelegate* delegate)
41 : WebView(browser_context), delegate_(delegate) {}
42
43 ObservableWebView::~ObservableWebView() = default;
44
DidFinishLoad(content::RenderFrameHost * render_frame_host,const GURL & validated_url)45 void ObservableWebView::DidFinishLoad(
46 content::RenderFrameHost* render_frame_host,
47 const GURL& validated_url) {
48 // Only listen to the main frame.
49 if (render_frame_host->GetParent())
50 return;
51
52 if (delegate_)
53 delegate_->OnWebContentsFinishedLoad();
54 }
55
ResourceLoadComplete(content::RenderFrameHost * render_frame_host,const content::GlobalRequestID & request_id,const blink::mojom::ResourceLoadInfo & resource_load_info)56 void ObservableWebView::ResourceLoadComplete(
57 content::RenderFrameHost* render_frame_host,
58 const content::GlobalRequestID& request_id,
59 const blink::mojom::ResourceLoadInfo& resource_load_info) {
60 // Only listen to the main frame.
61 if (render_frame_host->GetParent())
62 return;
63
64 if (delegate_)
65 delegate_->OnMainFrameResourceLoadComplete(resource_load_info);
66 }
67
ResetDelegate()68 void ObservableWebView::ResetDelegate() {
69 delegate_ = nullptr;
70 }
71
72 ////////////////////////////////////////////////////////////////////////////////
73 // WebDialogView, public:
74
WebDialogView(content::BrowserContext * context,WebDialogDelegate * delegate,std::unique_ptr<WebContentsHandler> handler,bool use_dialog_frame)75 WebDialogView::WebDialogView(content::BrowserContext* context,
76 WebDialogDelegate* delegate,
77 std::unique_ptr<WebContentsHandler> handler,
78 bool use_dialog_frame)
79 : ClientView(nullptr, nullptr),
80 WebDialogWebContentsDelegate(context, std::move(handler)),
81 delegate_(delegate),
82 web_view_(new ObservableWebView(context, delegate)),
83 use_dialog_frame_(use_dialog_frame) {
84 web_view_->set_allow_accelerators(true);
85 AddChildView(web_view_);
86 set_contents_view(web_view_);
87 SetLayoutManager(std::make_unique<views::FillLayout>());
88 // Pressing the Escape key will close the dialog.
89 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
90
91 if (delegate_) {
92 for (const auto& accelerator : delegate_->GetAccelerators())
93 AddAccelerator(accelerator);
94 }
95 }
96
97 WebDialogView::~WebDialogView() = default;
98
web_contents()99 content::WebContents* WebDialogView::web_contents() {
100 return web_view_->web_contents();
101 }
102
103 ////////////////////////////////////////////////////////////////////////////////
104 // WebDialogView, views::View implementation:
105
CalculatePreferredSize() const106 gfx::Size WebDialogView::CalculatePreferredSize() const {
107 gfx::Size out;
108 if (delegate_)
109 delegate_->GetDialogSize(&out);
110 return out;
111 }
112
GetMinimumSize() const113 gfx::Size WebDialogView::GetMinimumSize() const {
114 gfx::Size out;
115 if (delegate_)
116 delegate_->GetMinimumDialogSize(&out);
117 return out;
118 }
119
AcceleratorPressed(const ui::Accelerator & accelerator)120 bool WebDialogView::AcceleratorPressed(const ui::Accelerator& accelerator) {
121 if (delegate_ && delegate_->AcceleratorPressed(accelerator))
122 return true;
123
124 DCHECK_EQ(ui::VKEY_ESCAPE, accelerator.key_code());
125 if (delegate_ && !delegate_->ShouldCloseDialogOnEscape())
126 return false;
127
128 // Pressing Escape closes the dialog.
129 if (GetWidget()) {
130 // Contents must be closed first, or the dialog will not close.
131 CloseContents(web_view_->web_contents());
132 GetWidget()->CloseWithReason(views::Widget::ClosedReason::kEscKeyPressed);
133 }
134 return true;
135 }
136
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)137 void WebDialogView::ViewHierarchyChanged(
138 const ViewHierarchyChangedDetails& details) {
139 if (details.is_add && GetWidget())
140 InitDialog();
141 }
142
CanClose()143 bool WebDialogView::CanClose() {
144 // Don't close UI if |delegate_| does not allow users to close it by
145 // clicking on "x" button or pressing Escape shortcut key on hosting
146 // dialog.
147 if (!delegate_->CanCloseDialog() && !close_contents_called_)
148 return false;
149
150 // If CloseContents() is called before CanClose(), which is called by
151 // RenderViewHostImpl::ClosePageIgnoringUnloadEvents, it indicates
152 // beforeunload event should not be fired during closing.
153 if ((is_attempting_close_dialog_ && before_unload_fired_) ||
154 close_contents_called_) {
155 is_attempting_close_dialog_ = false;
156 before_unload_fired_ = false;
157 return true;
158 }
159
160 if (!is_attempting_close_dialog_) {
161 // Fire beforeunload event when user attempts to close the dialog.
162 is_attempting_close_dialog_ = true;
163 web_view_->web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
164 }
165 return false;
166 }
167
168 ////////////////////////////////////////////////////////////////////////////////
169 // WebDialogView, views::WidgetDelegate implementation:
170
OnCloseRequested(Widget::ClosedReason close_reason)171 bool WebDialogView::OnCloseRequested(Widget::ClosedReason close_reason) {
172 return !delegate_ || delegate_->OnDialogCloseRequested();
173 }
174
CanResize() const175 bool WebDialogView::CanResize() const {
176 if (delegate_)
177 return delegate_->CanResizeDialog();
178 return true;
179 }
180
GetModalType() const181 ui::ModalType WebDialogView::GetModalType() const {
182 return GetDialogModalType();
183 }
184
GetWindowTitle() const185 base::string16 WebDialogView::GetWindowTitle() const {
186 if (delegate_)
187 return delegate_->GetDialogTitle();
188 return base::string16();
189 }
190
GetAccessibleWindowTitle() const191 base::string16 WebDialogView::GetAccessibleWindowTitle() const {
192 if (delegate_)
193 return delegate_->GetAccessibleDialogTitle();
194 return GetWindowTitle();
195 }
196
GetWindowName() const197 std::string WebDialogView::GetWindowName() const {
198 if (delegate_)
199 return delegate_->GetDialogName();
200 return std::string();
201 }
202
WindowClosing()203 void WebDialogView::WindowClosing() {
204 // If we still have a delegate that means we haven't notified it of the
205 // dialog closing. This happens if the user clicks the Close button on the
206 // dialog.
207 if (delegate_)
208 OnDialogClosed("");
209 }
210
GetContentsView()211 views::View* WebDialogView::GetContentsView() {
212 return this;
213 }
214
CreateClientView(views::Widget * widget)215 views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) {
216 return this;
217 }
218
CreateNonClientFrameView(Widget * widget)219 NonClientFrameView* WebDialogView::CreateNonClientFrameView(Widget* widget) {
220 if (use_dialog_frame_)
221 return DialogDelegate::CreateDialogFrameView(widget);
222 return WidgetDelegate::CreateNonClientFrameView(widget);
223 }
224
GetInitiallyFocusedView()225 views::View* WebDialogView::GetInitiallyFocusedView() {
226 return web_view_;
227 }
228
ShouldShowWindowTitle() const229 bool WebDialogView::ShouldShowWindowTitle() const {
230 return ShouldShowDialogTitle();
231 }
232
GetWidget()233 views::Widget* WebDialogView::GetWidget() {
234 return View::GetWidget();
235 }
236
GetWidget() const237 const views::Widget* WebDialogView::GetWidget() const {
238 return View::GetWidget();
239 }
240
241 ////////////////////////////////////////////////////////////////////////////////
242 // WebDialogDelegate implementation:
243
GetDialogModalType() const244 ui::ModalType WebDialogView::GetDialogModalType() const {
245 if (delegate_)
246 return delegate_->GetDialogModalType();
247 return ui::MODAL_TYPE_NONE;
248 }
249
GetDialogTitle() const250 base::string16 WebDialogView::GetDialogTitle() const {
251 return GetWindowTitle();
252 }
253
GetDialogContentURL() const254 GURL WebDialogView::GetDialogContentURL() const {
255 if (delegate_)
256 return delegate_->GetDialogContentURL();
257 return GURL();
258 }
259
GetWebUIMessageHandlers(std::vector<WebUIMessageHandler * > * handlers) const260 void WebDialogView::GetWebUIMessageHandlers(
261 std::vector<WebUIMessageHandler*>* handlers) const {
262 if (delegate_)
263 delegate_->GetWebUIMessageHandlers(handlers);
264 }
265
GetDialogSize(gfx::Size * size) const266 void WebDialogView::GetDialogSize(gfx::Size* size) const {
267 if (delegate_)
268 delegate_->GetDialogSize(size);
269 }
270
GetMinimumDialogSize(gfx::Size * size) const271 void WebDialogView::GetMinimumDialogSize(gfx::Size* size) const {
272 if (delegate_)
273 delegate_->GetMinimumDialogSize(size);
274 }
275
GetDialogArgs() const276 std::string WebDialogView::GetDialogArgs() const {
277 if (delegate_)
278 return delegate_->GetDialogArgs();
279 return std::string();
280 }
281
OnDialogShown(content::WebUI * webui)282 void WebDialogView::OnDialogShown(content::WebUI* webui) {
283 if (delegate_)
284 delegate_->OnDialogShown(webui);
285 }
286
OnDialogClosed(const std::string & json_retval)287 void WebDialogView::OnDialogClosed(const std::string& json_retval) {
288 Detach();
289 if (delegate_) {
290 // Store the dialog content area size.
291 delegate_->StoreDialogSize(GetContentsBounds().size());
292 }
293
294 if (GetWidget())
295 GetWidget()->Close();
296
297 if (delegate_) {
298 delegate_->OnDialogClosed(json_retval);
299 delegate_ = nullptr; // We will not communicate further with the delegate.
300 // Clear the copy of the delegate in |web_view_| too.
301 web_view_->ResetDelegate();
302 }
303 }
304
OnDialogCloseFromWebUI(const std::string & json_retval)305 void WebDialogView::OnDialogCloseFromWebUI(const std::string& json_retval) {
306 closed_via_webui_ = true;
307 dialog_close_retval_ = json_retval;
308 if (GetWidget())
309 GetWidget()->Close();
310 }
311
OnCloseContents(WebContents * source,bool * out_close_dialog)312 void WebDialogView::OnCloseContents(WebContents* source,
313 bool* out_close_dialog) {
314 DCHECK(out_close_dialog);
315 if (delegate_)
316 delegate_->OnCloseContents(source, out_close_dialog);
317 }
318
ShouldShowDialogTitle() const319 bool WebDialogView::ShouldShowDialogTitle() const {
320 if (delegate_)
321 return delegate_->ShouldShowDialogTitle();
322 return true;
323 }
324
ShouldCenterDialogTitleText() const325 bool WebDialogView::ShouldCenterDialogTitleText() const {
326 if (delegate_)
327 return delegate_->ShouldCenterDialogTitleText();
328 return false;
329 }
330
ShouldShowCloseButton() const331 bool WebDialogView::ShouldShowCloseButton() const {
332 if (delegate_)
333 return delegate_->ShouldShowCloseButton();
334 return true;
335 }
336
HandleContextMenu(content::RenderFrameHost * render_frame_host,const content::ContextMenuParams & params)337 bool WebDialogView::HandleContextMenu(
338 content::RenderFrameHost* render_frame_host,
339 const content::ContextMenuParams& params) {
340 if (delegate_)
341 return delegate_->HandleContextMenu(render_frame_host, params);
342 return WebDialogWebContentsDelegate::HandleContextMenu(render_frame_host,
343 params);
344 }
345
346 ////////////////////////////////////////////////////////////////////////////////
347 // content::WebContentsDelegate implementation:
348
SetContentsBounds(WebContents * source,const gfx::Rect & bounds)349 void WebDialogView::SetContentsBounds(WebContents* source,
350 const gfx::Rect& bounds) {
351 // The contained web page wishes to resize itself. We let it do this because
352 // if it's a dialog we know about, we trust it not to be mean to the user.
353 GetWidget()->SetBounds(bounds);
354 }
355
356 // A simplified version of BrowserView::HandleKeyboardEvent().
357 // We don't handle global keyboard shortcuts here, but that's fine since
358 // they're all browser-specific. (This may change in the future.)
HandleKeyboardEvent(content::WebContents * source,const NativeWebKeyboardEvent & event)359 bool WebDialogView::HandleKeyboardEvent(content::WebContents* source,
360 const NativeWebKeyboardEvent& event) {
361 if (!event.os_event)
362 return false;
363
364 return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
365 event, GetFocusManager());
366 }
367
CloseContents(WebContents * source)368 void WebDialogView::CloseContents(WebContents* source) {
369 close_contents_called_ = true;
370 bool close_dialog = false;
371 OnCloseContents(source, &close_dialog);
372 if (close_dialog)
373 OnDialogClosed(closed_via_webui_ ? dialog_close_retval_ : std::string());
374 }
375
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)376 content::WebContents* WebDialogView::OpenURLFromTab(
377 content::WebContents* source,
378 const content::OpenURLParams& params) {
379 content::WebContents* new_contents = nullptr;
380 if (delegate_ &&
381 delegate_->HandleOpenURLFromTab(source, params, &new_contents)) {
382 return new_contents;
383 }
384 return WebDialogWebContentsDelegate::OpenURLFromTab(source, params);
385 }
386
AddNewContents(content::WebContents * source,std::unique_ptr<content::WebContents> new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_rect,bool user_gesture,bool * was_blocked)387 void WebDialogView::AddNewContents(
388 content::WebContents* source,
389 std::unique_ptr<content::WebContents> new_contents,
390 WindowOpenDisposition disposition,
391 const gfx::Rect& initial_rect,
392 bool user_gesture,
393 bool* was_blocked) {
394 WebDialogWebContentsDelegate::AddNewContents(source, std::move(new_contents),
395 disposition, initial_rect,
396 user_gesture, was_blocked);
397 }
398
LoadingStateChanged(content::WebContents * source,bool to_different_document)399 void WebDialogView::LoadingStateChanged(content::WebContents* source,
400 bool to_different_document) {
401 if (delegate_)
402 delegate_->OnLoadingStateChanged(source);
403 }
404
BeforeUnloadFired(content::WebContents * tab,bool proceed,bool * proceed_to_fire_unload)405 void WebDialogView::BeforeUnloadFired(content::WebContents* tab,
406 bool proceed,
407 bool* proceed_to_fire_unload) {
408 before_unload_fired_ = true;
409 *proceed_to_fire_unload = proceed;
410 }
411
IsWebContentsCreationOverridden(content::SiteInstance * source_site_instance,content::mojom::WindowContainerType window_container_type,const GURL & opener_url,const std::string & frame_name,const GURL & target_url)412 bool WebDialogView::IsWebContentsCreationOverridden(
413 content::SiteInstance* source_site_instance,
414 content::mojom::WindowContainerType window_container_type,
415 const GURL& opener_url,
416 const std::string& frame_name,
417 const GURL& target_url) {
418 if (delegate_)
419 return delegate_->HandleShouldOverrideWebContentsCreation();
420 return false;
421 }
422
423 ////////////////////////////////////////////////////////////////////////////////
424 // WebDialogView, private:
425
InitDialog()426 void WebDialogView::InitDialog() {
427 content::WebContents* web_contents = web_view_->GetWebContents();
428 if (web_contents->GetDelegate() == this)
429 return;
430
431 web_contents->SetDelegate(this);
432
433 // Set the delegate. This must be done before loading the page. See
434 // the comment above WebDialogUI in its header file for why.
435 WebDialogUIBase::SetDelegate(web_contents, this);
436
437 if (!disable_url_load_for_test_)
438 web_view_->LoadInitialURL(GetDialogContentURL());
439 }
440
441 } // namespace views
442