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.
5 #include "ui/views/controls/webview/webview.h"
7 #include "content/public/browser/browser_accessibility_state.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/navigation_controller.h"
10 #include "content/public/browser/render_view_host.h"
11 #include "content/public/browser/render_widget_host_view.h"
12 #include "content/public/browser/web_contents.h"
13 #include "content/public/browser/web_contents_view.h"
14 #include "ipc/ipc_message.h"
15 #include "ui/accessibility/ax_enums.h"
16 #include "ui/accessibility/ax_view_state.h"
17 #include "ui/events/event.h"
18 #include "ui/views/accessibility/native_view_accessibility.h"
19 #include "ui/views/controls/native/native_view_host.h"
20 #include "ui/views/focus/focus_manager.h"
21 #include "ui/views/views_delegate.h"
26 const char WebView::kViewClassName[] = "WebView";
28 ////////////////////////////////////////////////////////////////////////////////
31 WebView::WebView(content::BrowserContext* browser_context)
32 : holder_(new NativeViewHost()),
33 embed_fullscreen_widget_mode_enabled_(false),
34 is_embedding_fullscreen_widget_(false),
35 browser_context_(browser_context),
36 allow_accelerators_(false) {
37 AddChildView(holder_); // Takes ownership of |holder_|.
38 NativeViewAccessibility::RegisterWebView(this);
42 SetWebContents(NULL); // Make sure all necessary tear-down takes place.
43 NativeViewAccessibility::UnregisterWebView(this);
46 content::WebContents* WebView::GetWebContents() {
47 if (!web_contents()) {
48 wc_owner_.reset(CreateWebContents(browser_context_));
49 wc_owner_->SetDelegate(this);
50 SetWebContents(wc_owner_.get());
52 return web_contents();
55 void WebView::SetWebContents(content::WebContents* replacement) {
56 if (replacement == web_contents())
59 WebContentsObserver::Observe(replacement);
60 // web_contents() now returns |replacement| from here onwards.
61 if (wc_owner_ != replacement)
63 if (embed_fullscreen_widget_mode_enabled_) {
64 is_embedding_fullscreen_widget_ =
65 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
67 DCHECK(!is_embedding_fullscreen_widget_);
72 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
73 DCHECK(!web_contents())
74 << "Cannot change mode while a WebContents is attached.";
75 embed_fullscreen_widget_mode_enabled_ = enable;
78 void WebView::LoadInitialURL(const GURL& url) {
79 GetWebContents()->GetController().LoadURL(
80 url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
84 void WebView::SetFastResize(bool fast_resize) {
85 holder_->set_fast_resize(fast_resize);
88 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
89 FocusManager* focus_manager = GetFocusManager();
91 focus_manager->SetFocusedView(this);
94 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
95 preferred_size_ = preferred_size;
96 PreferredSizeChanged();
99 ////////////////////////////////////////////////////////////////////////////////
100 // WebView, View overrides:
102 const char* WebView::GetClassName() const {
103 return kViewClassName;
106 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
107 // In most cases, the holder is simply sized to fill this WebView's bounds.
108 // Only WebContentses that are in fullscreen mode and being screen-captured
109 // will engage the special layout/sizing behavior.
110 gfx::Rect holder_bounds(bounds().size());
111 if (!embed_fullscreen_widget_mode_enabled_ ||
113 web_contents()->GetCapturerCount() == 0 ||
114 web_contents()->GetPreferredSize().IsEmpty() ||
115 !(is_embedding_fullscreen_widget_ ||
116 (web_contents()->GetDelegate() &&
117 web_contents()->GetDelegate()->
118 IsFullscreenForTabOrPending(web_contents())))) {
119 holder_->SetBoundsRect(holder_bounds);
123 // Size the holder to the capture video resolution and center it. If this
124 // WebView is not large enough to contain the holder at the preferred size,
125 // scale down to fit (preserving aspect ratio).
126 const gfx::Size capture_size = web_contents()->GetPreferredSize();
127 if (capture_size.width() <= holder_bounds.width() &&
128 capture_size.height() <= holder_bounds.height()) {
129 // No scaling, just centering.
130 holder_bounds.ClampToCenteredSize(capture_size);
132 // Scale down, preserving aspect ratio, and center.
133 // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
134 // looks like others have written this code elsewhere. Let's considate
135 // into a shared function ui/gfx/geometry or around there.
136 const int64 x = static_cast<int64>(capture_size.width()) *
137 holder_bounds.height();
138 const int64 y = static_cast<int64>(capture_size.height()) *
139 holder_bounds.width();
141 holder_bounds.ClampToCenteredSize(gfx::Size(
142 holder_bounds.width(), static_cast<int>(y / capture_size.width())));
144 holder_bounds.ClampToCenteredSize(gfx::Size(
145 static_cast<int>(x / capture_size.height()), holder_bounds.height()));
149 holder_->SetBoundsRect(holder_bounds);
152 void WebView::ViewHierarchyChanged(
153 const ViewHierarchyChangedDetails& details) {
158 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
159 if (allow_accelerators_)
160 return FocusManager::IsTabTraversalKeyEvent(event);
162 // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
164 // We'll first give the page a chance to process the key events. If it does
165 // not process them, they'll be returned to us and we'll treat them as
166 // accelerators then.
167 return web_contents() && !web_contents()->IsCrashed();
170 bool WebView::IsFocusable() const {
171 // We need to be focusable when our contents is not a view hierarchy, as
172 // clicking on the contents needs to focus us.
173 return !!web_contents();
176 void WebView::OnFocus() {
179 if (is_embedding_fullscreen_widget_) {
180 content::RenderWidgetHostView* const current_fs_view =
181 web_contents()->GetFullscreenRenderWidgetHostView();
183 current_fs_view->Focus();
185 web_contents()->GetView()->Focus();
189 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
191 web_contents()->FocusThroughTabTraversal(reverse);
194 void WebView::GetAccessibleState(ui::AXViewState* state) {
195 state->role = ui::AX_ROLE_GROUP;
198 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
199 if (web_contents()) {
200 content::RenderWidgetHostView* host_view =
201 web_contents()->GetRenderWidgetHostView();
203 return host_view->GetNativeViewAccessible();
205 return View::GetNativeViewAccessible();
208 gfx::Size WebView::GetPreferredSize() {
209 if (preferred_size_ == gfx::Size())
210 return View::GetPreferredSize();
212 return preferred_size_;
215 ////////////////////////////////////////////////////////////////////////////////
216 // WebView, content::WebContentsDelegate implementation:
218 void WebView::WebContentsFocused(content::WebContents* web_contents) {
219 DCHECK(wc_owner_.get());
220 // The WebView is only the delegate of WebContentses it creates itself.
221 OnWebContentsFocused(wc_owner_.get());
224 bool WebView::EmbedsFullscreenWidget() const {
225 DCHECK(wc_owner_.get());
226 return embed_fullscreen_widget_mode_enabled_;
229 ////////////////////////////////////////////////////////////////////////////////
230 // WebView, content::WebContentsObserver implementation:
232 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
233 content::RenderViewHost* new_host) {
234 FocusManager* const focus_manager = GetFocusManager();
235 if (focus_manager && focus_manager->GetFocusedView() == this)
239 void WebView::DidShowFullscreenWidget(int routing_id) {
240 if (embed_fullscreen_widget_mode_enabled_)
241 ReattachForFullscreenChange(true);
244 void WebView::DidDestroyFullscreenWidget(int routing_id) {
245 if (embed_fullscreen_widget_mode_enabled_)
246 ReattachForFullscreenChange(false);
249 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
250 if (embed_fullscreen_widget_mode_enabled_)
251 ReattachForFullscreenChange(entered_fullscreen);
254 ////////////////////////////////////////////////////////////////////////////////
257 void WebView::AttachWebContents() {
258 // Prevents attachment if the WebView isn't already in a Widget, or it's
260 if (!GetWidget() || !web_contents())
263 const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
264 web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
265 web_contents()->GetView()->GetNativeView();
266 OnBoundsChanged(bounds());
267 if (holder_->native_view() == view_to_attach)
269 holder_->Attach(view_to_attach);
271 // The view will not be focused automatically when it is attached, so we need
272 // to pass on focus to it if the FocusManager thinks the view is focused. Note
273 // that not every Widget has a focus manager.
274 FocusManager* const focus_manager = GetFocusManager();
275 if (focus_manager && focus_manager->GetFocusedView() == this)
279 if (!is_embedding_fullscreen_widget_) {
280 web_contents()->SetParentNativeViewAccessible(
281 parent()->GetNativeViewAccessible());
286 void WebView::DetachWebContents() {
287 if (web_contents()) {
290 if (!is_embedding_fullscreen_widget_)
291 web_contents()->SetParentNativeViewAccessible(NULL);
296 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
297 DCHECK(embed_fullscreen_widget_mode_enabled_);
298 const bool web_contents_has_separate_fs_widget =
299 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
300 if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
301 // Shutting down or starting up the embedding of the separate fullscreen
302 // widget. Need to detach and re-attach to a different native view.
304 is_embedding_fullscreen_widget_ =
305 enter_fullscreen && web_contents_has_separate_fs_widget;
308 // Entering or exiting "non-Flash" fullscreen mode, where the native view is
309 // the same. So, do not change attachment.
310 OnBoundsChanged(bounds());
314 content::WebContents* WebView::CreateWebContents(
315 content::BrowserContext* browser_context) {
316 content::WebContents* contents = NULL;
317 if (ViewsDelegate::views_delegate) {
318 contents = ViewsDelegate::views_delegate->CreateWebContents(
319 browser_context, NULL);
323 content::WebContents::CreateParams create_params(
324 browser_context, NULL);
325 return content::WebContents::Create(create_params);