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 "ipc/ipc_message.h"
14 #include "ui/accessibility/ax_enums.h"
15 #include "ui/accessibility/ax_view_state.h"
16 #include "ui/base/ui_base_switches_util.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_
);
70 NotifyMaybeTextInputClientChanged();
73 void WebView::SetEmbedFullscreenWidgetMode(bool enable
) {
74 DCHECK(!web_contents())
75 << "Cannot change mode while a WebContents is attached.";
76 embed_fullscreen_widget_mode_enabled_
= enable
;
79 void WebView::LoadInitialURL(const GURL
& url
) {
80 GetWebContents()->GetController().LoadURL(
81 url
, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
85 void WebView::SetFastResize(bool fast_resize
) {
86 holder_
->set_fast_resize(fast_resize
);
89 void WebView::OnWebContentsFocused(content::WebContents
* web_contents
) {
90 FocusManager
* focus_manager
= GetFocusManager();
92 focus_manager
->SetFocusedView(this);
95 void WebView::SetPreferredSize(const gfx::Size
& preferred_size
) {
96 preferred_size_
= preferred_size
;
97 PreferredSizeChanged();
100 ////////////////////////////////////////////////////////////////////////////////
101 // WebView, View overrides:
103 const char* WebView::GetClassName() const {
104 return kViewClassName
;
107 ui::TextInputClient
* WebView::GetTextInputClient() {
108 // This function delegates the text input handling to the underlying
109 // content::RenderWidgetHostView. So when the underlying RWHV is destroyed or
110 // replaced with another one, we have to notify the FocusManager through
111 // FocusManager::OnTextInputClientChanged() that the focused TextInputClient
112 // needs to be updated.
113 if (switches::IsTextInputFocusManagerEnabled() &&
114 web_contents() && !web_contents()->IsBeingDestroyed()) {
115 content::RenderWidgetHostView
* host_view
=
116 is_embedding_fullscreen_widget_
?
117 web_contents()->GetFullscreenRenderWidgetHostView() :
118 web_contents()->GetRenderWidgetHostView();
120 return host_view
->GetTextInputClient();
125 void WebView::OnBoundsChanged(const gfx::Rect
& previous_bounds
) {
126 // In most cases, the holder is simply sized to fill this WebView's bounds.
127 // Only WebContentses that are in fullscreen mode and being screen-captured
128 // will engage the special layout/sizing behavior.
129 gfx::Rect
holder_bounds(bounds().size());
130 if (!embed_fullscreen_widget_mode_enabled_
||
132 web_contents()->GetCapturerCount() == 0 ||
133 web_contents()->GetPreferredSize().IsEmpty() ||
134 !(is_embedding_fullscreen_widget_
||
135 (web_contents()->GetDelegate() &&
136 web_contents()->GetDelegate()->
137 IsFullscreenForTabOrPending(web_contents())))) {
138 holder_
->SetBoundsRect(holder_bounds
);
142 // Size the holder to the capture video resolution and center it. If this
143 // WebView is not large enough to contain the holder at the preferred size,
144 // scale down to fit (preserving aspect ratio).
145 const gfx::Size capture_size
= web_contents()->GetPreferredSize();
146 if (capture_size
.width() <= holder_bounds
.width() &&
147 capture_size
.height() <= holder_bounds
.height()) {
148 // No scaling, just centering.
149 holder_bounds
.ClampToCenteredSize(capture_size
);
151 // Scale down, preserving aspect ratio, and center.
152 // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
153 // looks like others have written this code elsewhere. Let's considate
154 // into a shared function ui/gfx/geometry or around there.
155 const int64 x
= static_cast<int64
>(capture_size
.width()) *
156 holder_bounds
.height();
157 const int64 y
= static_cast<int64
>(capture_size
.height()) *
158 holder_bounds
.width();
160 holder_bounds
.ClampToCenteredSize(gfx::Size(
161 holder_bounds
.width(), static_cast<int>(y
/ capture_size
.width())));
163 holder_bounds
.ClampToCenteredSize(gfx::Size(
164 static_cast<int>(x
/ capture_size
.height()), holder_bounds
.height()));
168 holder_
->SetBoundsRect(holder_bounds
);
171 void WebView::ViewHierarchyChanged(
172 const ViewHierarchyChangedDetails
& details
) {
177 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent
& event
) {
178 if (allow_accelerators_
)
179 return FocusManager::IsTabTraversalKeyEvent(event
);
181 // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
183 // We'll first give the page a chance to process the key events. If it does
184 // not process them, they'll be returned to us and we'll treat them as
185 // accelerators then.
186 return web_contents() && !web_contents()->IsCrashed();
189 bool WebView::IsFocusable() const {
190 // We need to be focusable when our contents is not a view hierarchy, as
191 // clicking on the contents needs to focus us.
192 return !!web_contents();
195 void WebView::OnFocus() {
197 web_contents()->Focus();
200 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse
) {
202 web_contents()->FocusThroughTabTraversal(reverse
);
205 void WebView::GetAccessibleState(ui::AXViewState
* state
) {
206 state
->role
= ui::AX_ROLE_GROUP
;
209 gfx::NativeViewAccessible
WebView::GetNativeViewAccessible() {
210 if (web_contents()) {
211 content::RenderWidgetHostView
* host_view
=
212 web_contents()->GetRenderWidgetHostView();
214 return host_view
->GetNativeViewAccessible();
216 return View::GetNativeViewAccessible();
219 gfx::Size
WebView::GetPreferredSize() const {
220 if (preferred_size_
== gfx::Size())
221 return View::GetPreferredSize();
223 return preferred_size_
;
226 ////////////////////////////////////////////////////////////////////////////////
227 // WebView, content::WebContentsDelegate implementation:
229 void WebView::WebContentsFocused(content::WebContents
* web_contents
) {
230 DCHECK(wc_owner_
.get());
231 // The WebView is only the delegate of WebContentses it creates itself.
232 OnWebContentsFocused(wc_owner_
.get());
235 bool WebView::EmbedsFullscreenWidget() const {
236 DCHECK(wc_owner_
.get());
237 return embed_fullscreen_widget_mode_enabled_
;
240 ////////////////////////////////////////////////////////////////////////////////
241 // WebView, content::WebContentsObserver implementation:
243 void WebView::RenderViewDeleted(content::RenderViewHost
* render_view_host
) {
244 NotifyMaybeTextInputClientChanged();
247 void WebView::RenderProcessGone(base::TerminationStatus status
) {
248 NotifyMaybeTextInputClientChanged();
251 void WebView::RenderViewHostChanged(content::RenderViewHost
* old_host
,
252 content::RenderViewHost
* new_host
) {
253 FocusManager
* const focus_manager
= GetFocusManager();
254 if (focus_manager
&& focus_manager
->GetFocusedView() == this)
256 NotifyMaybeTextInputClientChanged();
259 void WebView::DidShowFullscreenWidget(int routing_id
) {
260 if (embed_fullscreen_widget_mode_enabled_
)
261 ReattachForFullscreenChange(true);
264 void WebView::DidDestroyFullscreenWidget(int routing_id
) {
265 if (embed_fullscreen_widget_mode_enabled_
)
266 ReattachForFullscreenChange(false);
269 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen
) {
270 if (embed_fullscreen_widget_mode_enabled_
)
271 ReattachForFullscreenChange(entered_fullscreen
);
274 void WebView::DidAttachInterstitialPage() {
275 NotifyMaybeTextInputClientChanged();
278 void WebView::DidDetachInterstitialPage() {
279 NotifyMaybeTextInputClientChanged();
282 ////////////////////////////////////////////////////////////////////////////////
285 void WebView::AttachWebContents() {
286 // Prevents attachment if the WebView isn't already in a Widget, or it's
288 if (!GetWidget() || !web_contents())
291 const gfx::NativeView view_to_attach
= is_embedding_fullscreen_widget_
?
292 web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
293 web_contents()->GetNativeView();
294 OnBoundsChanged(bounds());
295 if (holder_
->native_view() == view_to_attach
)
297 holder_
->Attach(view_to_attach
);
299 // The view will not be focused automatically when it is attached, so we need
300 // to pass on focus to it if the FocusManager thinks the view is focused. Note
301 // that not every Widget has a focus manager.
302 FocusManager
* const focus_manager
= GetFocusManager();
303 if (focus_manager
&& focus_manager
->GetFocusedView() == this)
307 if (!is_embedding_fullscreen_widget_
) {
308 web_contents()->SetParentNativeViewAccessible(
309 parent()->GetNativeViewAccessible());
314 void WebView::DetachWebContents() {
315 if (web_contents()) {
318 if (!is_embedding_fullscreen_widget_
)
319 web_contents()->SetParentNativeViewAccessible(NULL
);
324 void WebView::ReattachForFullscreenChange(bool enter_fullscreen
) {
325 DCHECK(embed_fullscreen_widget_mode_enabled_
);
326 const bool web_contents_has_separate_fs_widget
=
327 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
328 if (is_embedding_fullscreen_widget_
|| web_contents_has_separate_fs_widget
) {
329 // Shutting down or starting up the embedding of the separate fullscreen
330 // widget. Need to detach and re-attach to a different native view.
332 is_embedding_fullscreen_widget_
=
333 enter_fullscreen
&& web_contents_has_separate_fs_widget
;
336 // Entering or exiting "non-Flash" fullscreen mode, where the native view is
337 // the same. So, do not change attachment.
338 OnBoundsChanged(bounds());
340 NotifyMaybeTextInputClientChanged();
343 void WebView::NotifyMaybeTextInputClientChanged() {
344 // Update the TextInputClient as needed; see GetTextInputClient().
345 FocusManager
* const focus_manager
= GetFocusManager();
347 focus_manager
->OnTextInputClientChanged(this);
350 content::WebContents
* WebView::CreateWebContents(
351 content::BrowserContext
* browser_context
) {
352 content::WebContents
* contents
= NULL
;
353 if (ViewsDelegate::views_delegate
) {
354 contents
= ViewsDelegate::views_delegate
->CreateWebContents(
355 browser_context
, NULL
);
359 content::WebContents::CreateParams
create_params(
360 browser_context
, NULL
);
361 return content::WebContents::Create(create_params
);