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 "chrome/browser/ui/views/extensions/extension_view_views.h"
6
7#include "chrome/browser/extensions/extension_view_host.h"
8#include "chrome/browser/ui/browser.h"
9#include "chrome/browser/ui/views/extensions/extension_popup.h"
10#include "content/public/browser/content_browser_client.h"
11#include "content/public/browser/render_view_host.h"
12#include "content/public/browser/render_widget_host_view.h"
13#include "content/public/browser/web_contents.h"
14#include "extensions/browser/extension_host.h"
15#include "extensions/common/view_type.h"
16#include "ui/events/event.h"
17#include "ui/views/widget/widget.h"
18
19#if defined(USE_AURA)
20#include "ui/base/cursor/cursor.h"
21#endif
22
23ExtensionViewViews::ExtensionViewViews(extensions::ExtensionHost* host,
24                                       Browser* browser)
25    : host_(host),
26      browser_(browser),
27      initialized_(false),
28      container_(NULL),
29      is_clipped_(false) {
30  // This view needs to be focusable so it can act as the focused view for the
31  // focus manager. This is required to have SkipDefaultKeyEventProcessing
32  // called so the tab key events are forwarded to the renderer.
33  SetFocusable(true);
34}
35
36ExtensionViewViews::~ExtensionViewViews() {
37  if (parent())
38    parent()->RemoveChildView(this);
39  CleanUp();
40}
41
42gfx::Size ExtensionViewViews::GetMinimumSize() const {
43  // If the minimum size has never been set, returns the preferred size (same
44  // behavior as views::View).
45  return (minimum_size_ == gfx::Size()) ? GetPreferredSize() : minimum_size_;
46}
47
48void ExtensionViewViews::SetVisible(bool is_visible) {
49  if (is_visible != visible()) {
50    NativeViewHost::SetVisible(is_visible);
51
52    // Also tell RenderWidgetHostView the new visibility. Despite its name, it
53    // is not part of the View hierarchy and does not know about the change
54    // unless we tell it.
55    content::RenderWidgetHostView* host_view = render_view_host()->GetView();
56    if (host_view) {
57      if (is_visible)
58        host_view->Show();
59      else
60        host_view->Hide();
61    }
62  }
63}
64
65gfx::NativeCursor ExtensionViewViews::GetCursor(const ui::MouseEvent& event) {
66  return gfx::kNullCursor;
67}
68
69void ExtensionViewViews::ViewHierarchyChanged(
70    const ViewHierarchyChangedDetails& details) {
71  NativeViewHost::ViewHierarchyChanged(details);
72  if (details.is_add && GetWidget() && !initialized_)
73    CreateWidgetHostView();
74}
75
76void ExtensionViewViews::SetIsClipped(bool is_clipped) {
77  if (is_clipped_ != is_clipped) {
78    is_clipped_ = is_clipped;
79    if (visible())
80      ShowIfCompletelyLoaded();
81  }
82}
83
84void ExtensionViewViews::Init() {
85  // Initialization continues in ViewHierarchyChanged().
86}
87
88Browser* ExtensionViewViews::GetBrowser() {
89  return browser_;
90}
91
92gfx::NativeView ExtensionViewViews::GetNativeView() {
93  return native_view();
94}
95
96void ExtensionViewViews::ResizeDueToAutoResize(const gfx::Size& new_size) {
97  // Don't actually do anything with this information until we have been shown.
98  // Size changes will not be honored by lower layers while we are hidden.
99  if (!visible()) {
100    pending_preferred_size_ = new_size;
101    return;
102  }
103
104  if (new_size != GetPreferredSize())
105    SetPreferredSize(new_size);
106}
107
108void ExtensionViewViews::RenderViewCreated() {
109  extensions::ViewType host_type = host_->extension_host_type();
110  if (host_type == extensions::VIEW_TYPE_EXTENSION_POPUP) {
111    render_view_host()->EnableAutoResize(
112        gfx::Size(ExtensionPopup::kMinWidth, ExtensionPopup::kMinHeight),
113        gfx::Size(ExtensionPopup::kMaxWidth, ExtensionPopup::kMaxHeight));
114  }
115}
116
117void ExtensionViewViews::HandleKeyboardEvent(
118    content::WebContents* source,
119    const content::NativeWebKeyboardEvent& event) {
120  if (browser_) {
121    // Handle lower priority browser shortcuts such as Ctrl-f.
122    browser_->HandleKeyboardEvent(source, event);
123    return;
124  }
125
126  unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
127                                                        GetFocusManager());
128}
129
130void ExtensionViewViews::DidStopLoading() {
131  ShowIfCompletelyLoaded();
132}
133
134bool ExtensionViewViews::SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) {
135  // Let the tab key event be processed by the renderer (instead of moving the
136  // focus to the next focusable view). Also handle Backspace, since otherwise
137  // (on Windows at least), pressing Backspace, when focus is on a text field
138  // within the ExtensionViewViews, will navigate the page back instead of
139  // erasing a character.
140  return (e.key_code() == ui::VKEY_TAB || e.key_code() == ui::VKEY_BACK);
141}
142
143void ExtensionViewViews::OnBoundsChanged(const gfx::Rect& previous_bounds) {
144  // Propagate the new size to RenderWidgetHostView.
145  // We can't send size zero because RenderWidget DCHECKs that.
146  if (render_view_host()->GetView() && !bounds().IsEmpty())
147    render_view_host()->GetView()->SetSize(size());
148}
149
150void ExtensionViewViews::PreferredSizeChanged() {
151  View::PreferredSizeChanged();
152  if (container_)
153    container_->OnExtensionSizeChanged(this);
154}
155
156void ExtensionViewViews::OnFocus() {
157  host()->host_contents()->Focus();
158}
159
160void ExtensionViewViews::CreateWidgetHostView() {
161  DCHECK(!initialized_);
162  initialized_ = true;
163  Attach(host_->host_contents()->GetNativeView());
164  host_->CreateRenderViewSoon();
165  SetVisible(false);
166}
167
168void ExtensionViewViews::ShowIfCompletelyLoaded() {
169  if (visible() || is_clipped_)
170    return;
171
172  // We wait to show the ExtensionViewViews until it has loaded, and the view
173  // has actually been created. These can happen in different orders.
174  if (host_->did_stop_loading()) {
175    SetVisible(true);
176    ResizeDueToAutoResize(pending_preferred_size_);
177  }
178}
179
180void ExtensionViewViews::CleanUp() {
181  if (!initialized_)
182    return;
183  if (native_view())
184    Detach();
185  initialized_ = false;
186}
187
188namespace extensions {
189
190// static
191scoped_ptr<ExtensionView> ExtensionViewHost::CreateExtensionView(
192    ExtensionViewHost* host,
193    Browser* browser) {
194  scoped_ptr<ExtensionViewViews> view(new ExtensionViewViews(host, browser));
195  // We own |view_|, so don't auto delete when it's removed from the view
196  // hierarchy.
197  view->set_owned_by_client();
198  return view.PassAs<ExtensionView>();
199}
200
201}  // namespace extensions
202