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