Home | History | Annotate | Download | only in webview
      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