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/extensions/extension_view.h"
6
7#include "chrome/browser/extensions/extension_host.h"
8#include "chrome/browser/ui/views/extensions/extension_popup.h"
9#include "content/browser/renderer_host/render_view_host.h"
10#include "content/browser/renderer_host/render_widget_host_view.h"
11#include "views/widget/widget.h"
12
13#if defined(OS_WIN)
14#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
15#elif defined(TOUCH_UI)
16#include "chrome/browser/renderer_host/render_widget_host_view_views.h"
17#elif defined(OS_LINUX)
18#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
19#endif
20
21ExtensionView::ExtensionView(ExtensionHost* host, Browser* browser)
22    : host_(host),
23      browser_(browser),
24      initialized_(false),
25      container_(NULL),
26      is_clipped_(false) {
27  host_->set_view(this);
28
29  // This view needs to be focusable so it can act as the focused view for the
30  // focus manager. This is required to have SkipDefaultKeyEventProcessing
31  // called so the tab key events are forwarded to the renderer.
32  SetFocusable(true);
33}
34
35ExtensionView::~ExtensionView() {
36  if (parent())
37    parent()->RemoveChildView(this);
38  CleanUp();
39}
40
41const Extension* ExtensionView::extension() const {
42  return host_->extension();
43}
44
45RenderViewHost* ExtensionView::render_view_host() const {
46  return host_->render_view_host();
47}
48
49void ExtensionView::DidStopLoading() {
50  ShowIfCompletelyLoaded();
51}
52
53void ExtensionView::SetIsClipped(bool is_clipped) {
54  if (is_clipped_ != is_clipped) {
55    is_clipped_ = is_clipped;
56    if (IsVisible())
57      ShowIfCompletelyLoaded();
58  }
59}
60
61void ExtensionView::SetVisible(bool is_visible) {
62  if (is_visible != IsVisible()) {
63    NativeViewHost::SetVisible(is_visible);
64
65    // Also tell RenderWidgetHostView the new visibility. Despite its name, it
66    // is not part of the View hierarchy and does not know about the change
67    // unless we tell it.
68    if (render_view_host()->view()) {
69      if (is_visible)
70        render_view_host()->view()->Show();
71      else
72        render_view_host()->view()->Hide();
73    }
74  }
75}
76
77void ExtensionView::CreateWidgetHostView() {
78  DCHECK(!initialized_);
79  initialized_ = true;
80  RenderWidgetHostView* view =
81      RenderWidgetHostView::CreateViewForWidget(render_view_host());
82
83  // TODO(mpcomplete): RWHV needs a cross-platform Init function.
84#if defined(OS_WIN)
85  // Create the HWND. Note:
86  // RenderWidgetHostHWND supports windowed plugins, but if we ever also
87  // wanted to support constrained windows with this, we would need an
88  // additional HWND to parent off of because windowed plugin HWNDs cannot
89  // exist in the same z-order as constrained windows.
90  RenderWidgetHostViewWin* view_win =
91      static_cast<RenderWidgetHostViewWin*>(view);
92  HWND hwnd = view_win->Create(GetWidget()->GetNativeView());
93  view_win->ShowWindow(SW_SHOW);
94  Attach(hwnd);
95#elif defined(TOUCH_UI)
96  RenderWidgetHostViewViews* view_views =
97      static_cast<RenderWidgetHostViewViews*>(view);
98  view_views->InitAsChild();
99  AttachToView(view_views);
100#elif defined(OS_LINUX)
101  RenderWidgetHostViewGtk* view_gtk =
102      static_cast<RenderWidgetHostViewGtk*>(view);
103  view_gtk->InitAsChild();
104  Attach(view_gtk->GetNativeView());
105#else
106  NOTIMPLEMENTED();
107#endif
108
109  host_->CreateRenderViewSoon(view);
110  SetVisible(false);
111}
112
113void ExtensionView::ShowIfCompletelyLoaded() {
114  if (IsVisible() || is_clipped_)
115    return;
116
117  // We wait to show the ExtensionView until it has loaded, and the view has
118  // actually been created. These can happen in different orders.
119  if (host_->did_stop_loading()) {
120    SetVisible(true);
121    UpdatePreferredSize(pending_preferred_size_);
122  }
123}
124
125void ExtensionView::CleanUp() {
126  if (!initialized_)
127    return;
128  if (native_view())
129    Detach();
130  initialized_ = false;
131}
132
133void ExtensionView::SetBackground(const SkBitmap& background) {
134  if (render_view_host()->IsRenderViewLive() && render_view_host()->view()) {
135    render_view_host()->view()->SetBackground(background);
136  } else {
137    pending_background_ = background;
138  }
139  ShowIfCompletelyLoaded();
140}
141
142void ExtensionView::UpdatePreferredSize(const gfx::Size& new_size) {
143  // Don't actually do anything with this information until we have been shown.
144  // Size changes will not be honored by lower layers while we are hidden.
145  if (!IsVisible()) {
146    pending_preferred_size_ = new_size;
147    return;
148  }
149
150  gfx::Size preferred_size = GetPreferredSize();
151  if (new_size != preferred_size)
152    SetPreferredSize(new_size);
153}
154
155void ExtensionView::ViewHierarchyChanged(bool is_add,
156                                         views::View *parent,
157                                         views::View *child) {
158  NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
159  if (is_add && GetWidget() && !initialized_)
160    CreateWidgetHostView();
161}
162
163void ExtensionView::PreferredSizeChanged() {
164  View::PreferredSizeChanged();
165  if (container_)
166    container_->OnExtensionPreferredSizeChanged(this);
167}
168
169bool ExtensionView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) {
170  // Let the tab key event be processed by the renderer (instead of moving the
171  // focus to the next focusable view).
172  return (e.key_code() == ui::VKEY_TAB);
173}
174
175void ExtensionView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
176  // Propagate the new size to RenderWidgetHostView.
177  // We can't send size zero because RenderWidget DCHECKs that.
178  if (render_view_host()->view() && !bounds().IsEmpty())
179    render_view_host()->view()->SetSize(size());
180}
181
182void ExtensionView::HandleMouseMove() {
183  if (container_)
184    container_->OnExtensionMouseMove(this);
185}
186
187void ExtensionView::HandleMouseLeave() {
188  if (container_)
189    container_->OnExtensionMouseLeave(this);
190}
191
192void ExtensionView::RenderViewCreated() {
193  if (!pending_background_.empty() && render_view_host()->view()) {
194    render_view_host()->view()->SetBackground(pending_background_);
195    pending_background_.reset();
196  }
197
198  // Tell the renderer not to draw scroll bars in popups unless the
199  // popups are at the maximum allowed size.
200  gfx::Size largest_popup_size(ExtensionPopup::kMaxWidth,
201                               ExtensionPopup::kMaxHeight);
202  host_->DisableScrollbarsForSmallWindows(largest_popup_size);
203}
204