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 27 using content::NativeWebKeyboardEvent; 28 using content::WebContents; 29 using content::WebUIMessageHandler; 30 using ui::WebDialogDelegate; 31 using ui::WebDialogUI; 32 using ui::WebDialogWebContentsDelegate; 33 34 namespace views { 35 36 //////////////////////////////////////////////////////////////////////////////// 37 // WebDialogView, public: 38 39 WebDialogView::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 59 WebDialogView::~WebDialogView() { 60 } 61 62 content::WebContents* WebDialogView::web_contents() { 63 return web_view_->web_contents(); 64 } 65 66 //////////////////////////////////////////////////////////////////////////////// 67 // WebDialogView, views::View implementation: 68 69 gfx::Size WebDialogView::GetPreferredSize() const { 70 gfx::Size out; 71 if (delegate_) 72 delegate_->GetDialogSize(&out); 73 return out; 74 } 75 76 gfx::Size WebDialogView::GetMinimumSize() const { 77 gfx::Size out; 78 if (delegate_) 79 delegate_->GetMinimumDialogSize(&out); 80 return out; 81 } 82 83 bool 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 91 void WebDialogView::ViewHierarchyChanged( 92 const ViewHierarchyChangedDetails& details) { 93 if (details.is_add && GetWidget()) 94 InitDialog(); 95 } 96 97 bool 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 124 bool WebDialogView::CanResize() const { 125 if (delegate_) 126 return delegate_->CanResizeDialog(); 127 return true; 128 } 129 130 ui::ModalType WebDialogView::GetModalType() const { 131 return GetDialogModalType(); 132 } 133 134 base::string16 WebDialogView::GetWindowTitle() const { 135 if (delegate_) 136 return delegate_->GetDialogTitle(); 137 return base::string16(); 138 } 139 140 std::string WebDialogView::GetWindowName() const { 141 if (delegate_) 142 return delegate_->GetDialogName(); 143 return std::string(); 144 } 145 146 void 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 154 views::View* WebDialogView::GetContentsView() { 155 return this; 156 } 157 158 views::ClientView* WebDialogView::CreateClientView(views::Widget* widget) { 159 return this; 160 } 161 162 views::View* WebDialogView::GetInitiallyFocusedView() { 163 return web_view_; 164 } 165 166 bool WebDialogView::ShouldShowWindowTitle() const { 167 return ShouldShowDialogTitle(); 168 } 169 170 views::Widget* WebDialogView::GetWidget() { 171 return View::GetWidget(); 172 } 173 174 const views::Widget* WebDialogView::GetWidget() const { 175 return View::GetWidget(); 176 } 177 178 //////////////////////////////////////////////////////////////////////////////// 179 // WebDialogDelegate implementation: 180 181 ui::ModalType WebDialogView::GetDialogModalType() const { 182 if (delegate_) 183 return delegate_->GetDialogModalType(); 184 return ui::MODAL_TYPE_NONE; 185 } 186 187 base::string16 WebDialogView::GetDialogTitle() const { 188 return GetWindowTitle(); 189 } 190 191 GURL WebDialogView::GetDialogContentURL() const { 192 if (delegate_) 193 return delegate_->GetDialogContentURL(); 194 return GURL(); 195 } 196 197 void WebDialogView::GetWebUIMessageHandlers( 198 std::vector<WebUIMessageHandler*>* handlers) const { 199 if (delegate_) 200 delegate_->GetWebUIMessageHandlers(handlers); 201 } 202 203 void WebDialogView::GetDialogSize(gfx::Size* size) const { 204 if (delegate_) 205 delegate_->GetDialogSize(size); 206 } 207 208 void WebDialogView::GetMinimumDialogSize(gfx::Size* size) const { 209 if (delegate_) 210 delegate_->GetMinimumDialogSize(size); 211 } 212 213 std::string WebDialogView::GetDialogArgs() const { 214 if (delegate_) 215 return delegate_->GetDialogArgs(); 216 return std::string(); 217 } 218 219 void WebDialogView::OnDialogShown(content::WebUI* webui, 220 content::RenderViewHost* render_view_host) { 221 if (delegate_) 222 delegate_->OnDialogShown(webui, render_view_host); 223 } 224 225 void 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 241 void 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 248 void WebDialogView::OnCloseContents(WebContents* source, 249 bool* out_close_dialog) { 250 if (delegate_) 251 delegate_->OnCloseContents(source, out_close_dialog); 252 } 253 254 bool WebDialogView::ShouldShowDialogTitle() const { 255 if (delegate_) 256 return delegate_->ShouldShowDialogTitle(); 257 return true; 258 } 259 260 bool 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 270 void 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.) 279 void 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 287 void 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 295 content::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 306 void 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 321 void WebDialogView::LoadingStateChanged(content::WebContents* source, 322 bool to_different_document) { 323 if (delegate_) 324 delegate_->OnLoadingStateChanged(source); 325 } 326 327 void 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 337 void 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