Home | History | Annotate | Download | only in tab_contents
      1 // Copyright (c) 2011 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 "chrome/browser/ui/views/tab_contents/tab_contents_view_views.h"
      6 
      7 #include <windows.h>
      8 
      9 #include <vector>
     10 
     11 #include "base/time.h"
     12 #include "chrome/browser/ui/views/sad_tab_view.h"
     13 #include "chrome/browser/ui/views/tab_contents/native_tab_contents_view.h"
     14 #include "chrome/browser/ui/views/tab_contents/render_view_context_menu_views.h"
     15 #include "content/browser/renderer_host/render_process_host.h"
     16 #include "content/browser/renderer_host/render_view_host.h"
     17 #include "content/browser/renderer_host/render_view_host_factory.h"
     18 #include "content/browser/renderer_host/render_widget_host_view.h"
     19 #include "content/browser/tab_contents/interstitial_page.h"
     20 #include "content/browser/tab_contents/tab_contents.h"
     21 #include "content/browser/tab_contents/tab_contents_delegate.h"
     22 #include "views/focus/focus_manager.h"
     23 #include "views/focus/view_storage.h"
     24 #include "views/screen.h"
     25 #include "views/widget/native_widget.h"
     26 #include "views/widget/root_view.h"
     27 #include "views/widget/widget.h"
     28 
     29 using WebKit::WebDragOperation;
     30 using WebKit::WebDragOperationNone;
     31 using WebKit::WebDragOperationsMask;
     32 using WebKit::WebInputEvent;
     33 
     34 // static
     35 TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
     36   return new TabContentsViewViews(tab_contents);
     37 }
     38 
     39 TabContentsViewViews::TabContentsViewViews(TabContents* tab_contents)
     40     : TabContentsView(tab_contents),
     41       ALLOW_THIS_IN_INITIALIZER_LIST(native_tab_contents_view_(
     42           NativeTabContentsView::CreateNativeTabContentsView(this))),
     43       close_tab_after_drag_ends_(false),
     44       sad_tab_(NULL) {
     45   last_focused_view_storage_id_ =
     46       views::ViewStorage::GetInstance()->CreateStorageID();
     47 }
     48 
     49 TabContentsViewViews::~TabContentsViewViews() {
     50   // Makes sure to remove any stored view we may still have in the ViewStorage.
     51   //
     52   // It is possible the view went away before us, so we only do this if the
     53   // view is registered.
     54   views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
     55   if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
     56     view_storage->RemoveView(last_focused_view_storage_id_);
     57 }
     58 
     59 void TabContentsViewViews::Unparent() {
     60   CHECK(native_tab_contents_view_.get());
     61   native_tab_contents_view_->Unparent();
     62 }
     63 
     64 void TabContentsViewViews::CreateView(const gfx::Size& initial_size) {
     65   native_tab_contents_view_->InitNativeTabContentsView();
     66 }
     67 
     68 RenderWidgetHostView* TabContentsViewViews::CreateViewForWidget(
     69     RenderWidgetHost* render_widget_host) {
     70   if (render_widget_host->view()) {
     71     // During testing, the view will already be set up in most cases to the
     72     // test view, so we don't want to clobber it with a real one. To verify that
     73     // this actually is happening (and somebody isn't accidentally creating the
     74     // view twice), we check for the RVH Factory, which will be set when we're
     75     // making special ones (which go along with the special views).
     76     DCHECK(RenderViewHostFactory::has_factory());
     77     return render_widget_host->view();
     78   }
     79 
     80   // If we were showing sad tab, remove it now.
     81   if (sad_tab_) {
     82     GetWidget()->SetContentsView(new views::View());
     83     sad_tab_ = NULL;
     84   }
     85 
     86   return native_tab_contents_view_->CreateRenderWidgetHostView(
     87       render_widget_host);
     88 }
     89 
     90 gfx::NativeView TabContentsViewViews::GetNativeView() const {
     91   return GetWidget()->GetNativeView();
     92 }
     93 
     94 gfx::NativeView TabContentsViewViews::GetContentNativeView() const {
     95   RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
     96   return rwhv ? rwhv->GetNativeView() : NULL;
     97 }
     98 
     99 gfx::NativeWindow TabContentsViewViews::GetTopLevelNativeWindow() const {
    100   return native_tab_contents_view_->GetTopLevelNativeWindow();
    101 }
    102 
    103 void TabContentsViewViews::GetContainerBounds(gfx::Rect* out) const {
    104   *out = GetWidget()->GetClientAreaScreenBounds();
    105 }
    106 
    107 void TabContentsViewViews::StartDragging(const WebDropData& drop_data,
    108                                          WebDragOperationsMask ops,
    109                                          const SkBitmap& image,
    110                                          const gfx::Point& image_offset) {
    111   native_tab_contents_view_->StartDragging(drop_data, ops, image, image_offset);
    112 }
    113 
    114 void TabContentsViewViews::SetPageTitle(const std::wstring& title) {
    115   native_tab_contents_view_->SetPageTitle(title);
    116 }
    117 
    118 void TabContentsViewViews::OnTabCrashed(base::TerminationStatus status,
    119                                         int /* error_code */) {
    120   // Force an invalidation to render sad tab.
    121   // Note that it's possible to get this message after the window was destroyed.
    122   if (::IsWindow(GetNativeView())) {
    123     SadTabView::Kind kind =
    124         status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ?
    125         SadTabView::KILLED : SadTabView::CRASHED;
    126     sad_tab_ = new SadTabView(tab_contents(), kind);
    127     GetWidget()->SetContentsView(sad_tab_);
    128     sad_tab_->SchedulePaint();
    129   }
    130 }
    131 
    132 void TabContentsViewViews::SizeContents(const gfx::Size& size) {
    133   GetWidget()->SetSize(size);
    134 }
    135 
    136 void TabContentsViewViews::Focus() {
    137   if (tab_contents()->interstitial_page()) {
    138     tab_contents()->interstitial_page()->Focus();
    139     return;
    140   }
    141 
    142   if (tab_contents()->is_crashed() && sad_tab_ != NULL) {
    143     sad_tab_->RequestFocus();
    144     return;
    145   }
    146 
    147   if (tab_contents()->constrained_window_count() > 0) {
    148     ConstrainedWindow* window = *tab_contents()->constrained_window_begin();
    149     DCHECK(window);
    150     window->FocusConstrainedWindow();
    151     return;
    152   }
    153 
    154   RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
    155   GetWidget()->GetFocusManager()->FocusNativeView(rwhv ? rwhv->GetNativeView()
    156                                                        : GetNativeView());
    157 }
    158 
    159 void TabContentsViewViews::SetInitialFocus() {
    160   if (tab_contents()->FocusLocationBarByDefault())
    161     tab_contents()->SetFocusToLocationBar(false);
    162   else
    163     Focus();
    164 }
    165 
    166 void TabContentsViewViews::StoreFocus() {
    167   views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
    168 
    169   if (view_storage->RetrieveView(last_focused_view_storage_id_) != NULL)
    170     view_storage->RemoveView(last_focused_view_storage_id_);
    171 
    172   views::FocusManager* focus_manager =
    173       views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
    174   if (focus_manager) {
    175     // |focus_manager| can be NULL if the tab has been detached but still
    176     // exists.
    177     views::View* focused_view = focus_manager->GetFocusedView();
    178     if (focused_view)
    179       view_storage->StoreView(last_focused_view_storage_id_, focused_view);
    180   }
    181 }
    182 
    183 void TabContentsViewViews::RestoreFocus() {
    184   views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
    185   views::View* last_focused_view =
    186       view_storage->RetrieveView(last_focused_view_storage_id_);
    187 
    188   if (!last_focused_view) {
    189     SetInitialFocus();
    190   } else {
    191     views::FocusManager* focus_manager =
    192         views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
    193 
    194     // If you hit this DCHECK, please report it to Jay (jcampan).
    195     DCHECK(focus_manager != NULL) << "No focus manager when restoring focus.";
    196 
    197     if (last_focused_view->IsFocusableInRootView() && focus_manager &&
    198         focus_manager->ContainsView(last_focused_view)) {
    199       last_focused_view->RequestFocus();
    200     } else {
    201       // The focused view may not belong to the same window hierarchy (e.g.
    202       // if the location bar was focused and the tab is dragged out), or it may
    203       // no longer be focusable (e.g. if the location bar was focused and then
    204       // we switched to fullscreen mode).  In that case we default to the
    205       // default focus.
    206       SetInitialFocus();
    207     }
    208     view_storage->RemoveView(last_focused_view_storage_id_);
    209   }
    210 }
    211 
    212 bool TabContentsViewViews::IsDoingDrag() const {
    213   return native_tab_contents_view_->IsDoingDrag();
    214 }
    215 
    216 void TabContentsViewViews::CancelDragAndCloseTab() {
    217   DCHECK(IsDoingDrag());
    218   // We can't close the tab while we're in the drag and
    219   // |drag_handler_->CancelDrag()| is async.  Instead, set a flag to cancel
    220   // the drag and when the drag nested message loop ends, close the tab.
    221   native_tab_contents_view_->CancelDrag();
    222   close_tab_after_drag_ends_ = true;
    223 }
    224 
    225 void TabContentsViewViews::GetViewBounds(gfx::Rect* out) const {
    226   *out = GetWidget()->GetWindowScreenBounds();
    227 }
    228 
    229 void TabContentsViewViews::UpdateDragCursor(WebDragOperation operation) {
    230   native_tab_contents_view_->SetDragCursor(operation);
    231 }
    232 
    233 void TabContentsViewViews::GotFocus() {
    234   if (tab_contents()->delegate())
    235     tab_contents()->delegate()->TabContentsFocused(tab_contents());
    236 }
    237 
    238 void TabContentsViewViews::TakeFocus(bool reverse) {
    239   if (!tab_contents()->delegate()->TakeFocus(reverse)) {
    240     views::FocusManager* focus_manager =
    241         views::FocusManager::GetFocusManagerForNativeView(GetNativeView());
    242 
    243     // We may not have a focus manager if the tab has been switched before this
    244     // message arrived.
    245     if (focus_manager)
    246       focus_manager->AdvanceFocus(reverse);
    247   }
    248 }
    249 
    250 views::Widget* TabContentsViewViews::GetWidget() {
    251   return native_tab_contents_view_->AsNativeWidget()->GetWidget();
    252 }
    253 
    254 const views::Widget* TabContentsViewViews::GetWidget() const {
    255   return native_tab_contents_view_->AsNativeWidget()->GetWidget();
    256 }
    257 
    258 void TabContentsViewViews::CloseTab() {
    259   tab_contents()->Close(tab_contents()->render_view_host());
    260 }
    261 
    262 void TabContentsViewViews::ShowContextMenu(const ContextMenuParams& params) {
    263   // Allow delegates to handle the context menu operation first.
    264   if (tab_contents()->delegate()->HandleContextMenu(params))
    265     return;
    266 
    267   context_menu_.reset(new RenderViewContextMenuViews(tab_contents(), params));
    268   context_menu_->Init();
    269 
    270   POINT screen_pt = { params.x, params.y };
    271   MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);
    272 
    273   // Enable recursive tasks on the message loop so we can get updates while
    274   // the context menu is being displayed.
    275   bool old_state = MessageLoop::current()->NestableTasksAllowed();
    276   MessageLoop::current()->SetNestableTasksAllowed(true);
    277   context_menu_->RunMenuAt(screen_pt.x, screen_pt.y);
    278   MessageLoop::current()->SetNestableTasksAllowed(old_state);
    279 }
    280 
    281 void TabContentsViewViews::ShowPopupMenu(const gfx::Rect& bounds,
    282                                          int item_height,
    283                                          double item_font_size,
    284                                          int selected_item,
    285                                          const std::vector<WebMenuItem>& items,
    286                                          bool right_aligned) {
    287   // External popup menus are only used on Mac.
    288   NOTREACHED();
    289 }
    290 
    291 ////////////////////////////////////////////////////////////////////////////////
    292 // TabContentsViewViews, internal::NativeTabContentsViewDelegate implementation:
    293 
    294 TabContents* TabContentsViewViews::GetTabContents() {
    295   return tab_contents();
    296 }
    297 
    298 bool TabContentsViewViews::IsShowingSadTab() const {
    299   return tab_contents()->is_crashed() && sad_tab_;
    300 }
    301 
    302 void TabContentsViewViews::OnNativeTabContentsViewShown() {
    303   tab_contents()->ShowContents();
    304 }
    305 
    306 void TabContentsViewViews::OnNativeTabContentsViewHidden() {
    307   tab_contents()->HideContents();
    308 }
    309 
    310 void TabContentsViewViews::OnNativeTabContentsViewSized(const gfx::Size& size) {
    311   if (tab_contents()->interstitial_page())
    312     tab_contents()->interstitial_page()->SetSize(size);
    313   RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
    314   if (rwhv)
    315     rwhv->SetSize(size);
    316 }
    317 
    318 void TabContentsViewViews::OnNativeTabContentsViewWheelZoom(int distance) {
    319   if (tab_contents()->delegate()) {
    320     bool zoom_in = distance > 0;
    321     tab_contents()->delegate()->ContentsZoomChange(zoom_in);
    322   }
    323 }
    324 
    325 void TabContentsViewViews::OnNativeTabContentsViewMouseDown() {
    326   // Make sure this TabContents is activated when it is clicked on.
    327   if (tab_contents()->delegate())
    328     tab_contents()->delegate()->ActivateContents(tab_contents());
    329 }
    330 
    331 void TabContentsViewViews::OnNativeTabContentsViewMouseMove() {
    332   // Let our delegate know that the mouse moved (useful for resetting status
    333   // bubble state).
    334   if (tab_contents()->delegate()) {
    335     tab_contents()->delegate()->ContentsMouseEvent(
    336         tab_contents(), views::Screen::GetCursorScreenPoint(), true);
    337   }
    338 }
    339 
    340 void TabContentsViewViews::OnNativeTabContentsViewDraggingEnded() {
    341   if (close_tab_after_drag_ends_) {
    342     close_tab_timer_.Start(base::TimeDelta::FromMilliseconds(0), this,
    343                            &TabContentsViewViews::CloseTab);
    344   }
    345   tab_contents()->SystemDragEnded();
    346 }
    347