ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / ui / views / controls / webview / webview.cc
blobbe33f1d58df31645fbb7b6bf00338af70442b028
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_process_host.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 "ipc/ipc_message.h"
15 #include "ui/accessibility/ax_enums.h"
16 #include "ui/accessibility/ax_view_state.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/events/event.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"
23 namespace views {
25 // static
26 const char WebView::kViewClassName[] = "WebView";
28 ////////////////////////////////////////////////////////////////////////////////
29 // WebView, public:
31 WebView::WebView(content::BrowserContext* browser_context)
32 : holder_(new NativeViewHost()),
33 observing_render_process_host_(nullptr),
34 embed_fullscreen_widget_mode_enabled_(false),
35 is_embedding_fullscreen_widget_(false),
36 browser_context_(browser_context),
37 allow_accelerators_(false) {
38 AddChildView(holder_); // Takes ownership of |holder_|.
41 WebView::~WebView() {
42 SetWebContents(NULL); // Make sure all necessary tear-down takes place.
45 content::WebContents* WebView::GetWebContents() {
46 if (!web_contents()) {
47 wc_owner_.reset(CreateWebContents(browser_context_));
48 wc_owner_->SetDelegate(this);
49 SetWebContents(wc_owner_.get());
51 return web_contents();
54 void WebView::SetWebContents(content::WebContents* replacement) {
55 if (replacement == web_contents())
56 return;
57 DetachWebContents();
58 WebContentsObserver::Observe(replacement);
59 if (observing_render_process_host_) {
60 observing_render_process_host_->RemoveObserver(this);
61 observing_render_process_host_ = nullptr;
63 if (web_contents() && web_contents()->GetRenderProcessHost()) {
64 observing_render_process_host_ = web_contents()->GetRenderProcessHost();
65 observing_render_process_host_->AddObserver(this);
67 // web_contents() now returns |replacement| from here onwards.
68 SetFocusable(!!web_contents());
69 if (wc_owner_ != replacement)
70 wc_owner_.reset();
71 if (embed_fullscreen_widget_mode_enabled_) {
72 is_embedding_fullscreen_widget_ =
73 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
74 } else {
75 DCHECK(!is_embedding_fullscreen_widget_);
77 AttachWebContents();
78 NotifyMaybeTextInputClientAndAccessibilityChanged();
81 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
82 DCHECK(!web_contents())
83 << "Cannot change mode while a WebContents is attached.";
84 embed_fullscreen_widget_mode_enabled_ = enable;
87 void WebView::LoadInitialURL(const GURL& url) {
88 GetWebContents()->GetController().LoadURL(
89 url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
90 std::string());
93 void WebView::SetFastResize(bool fast_resize) {
94 holder_->set_fast_resize(fast_resize);
97 void WebView::SetResizeBackgroundColor(SkColor resize_background_color) {
98 holder_->set_resize_background_color(resize_background_color);
101 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
102 FocusManager* focus_manager = GetFocusManager();
103 if (focus_manager)
104 focus_manager->SetFocusedView(this);
107 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
108 preferred_size_ = preferred_size;
109 PreferredSizeChanged();
112 ////////////////////////////////////////////////////////////////////////////////
113 // WebView, View overrides:
115 const char* WebView::GetClassName() const {
116 return kViewClassName;
119 ui::TextInputClient* WebView::GetTextInputClient() {
120 // This function delegates the text input handling to the underlying
121 // content::RenderWidgetHostView. So when the underlying RWHV is destroyed or
122 // replaced with another one, we have to notify the FocusManager through
123 // FocusManager::OnTextInputClientChanged() that the focused TextInputClient
124 // needs to be updated.
125 if (switches::IsTextInputFocusManagerEnabled() &&
126 web_contents() && !web_contents()->IsBeingDestroyed()) {
127 const content::RenderViewHost* host = web_contents()->GetRenderViewHost();
128 content::RenderWidgetHostView* host_view =
129 is_embedding_fullscreen_widget_ ?
130 web_contents()->GetFullscreenRenderWidgetHostView() :
131 web_contents()->GetRenderWidgetHostView();
132 if (host && host->IsRenderViewLive() && host_view)
133 return host_view->GetTextInputClient();
135 return NULL;
138 scoped_ptr<content::WebContents> WebView::SwapWebContents(
139 scoped_ptr<content::WebContents> new_web_contents) {
140 if (wc_owner_)
141 wc_owner_->SetDelegate(NULL);
142 scoped_ptr<content::WebContents> old_web_contents(wc_owner_.Pass());
143 wc_owner_ = new_web_contents.Pass();
144 if (wc_owner_)
145 wc_owner_->SetDelegate(this);
146 SetWebContents(wc_owner_.get());
147 return old_web_contents.Pass();
150 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
151 // In most cases, the holder is simply sized to fill this WebView's bounds.
152 // Only WebContentses that are in fullscreen mode and being screen-captured
153 // will engage the special layout/sizing behavior.
154 gfx::Rect holder_bounds(bounds().size());
155 if (!embed_fullscreen_widget_mode_enabled_ ||
156 !web_contents() ||
157 web_contents()->GetCapturerCount() == 0 ||
158 web_contents()->GetPreferredSize().IsEmpty() ||
159 !(is_embedding_fullscreen_widget_ ||
160 (web_contents()->GetDelegate() &&
161 web_contents()->GetDelegate()->
162 IsFullscreenForTabOrPending(web_contents())))) {
163 holder_->SetBoundsRect(holder_bounds);
164 return;
167 // Size the holder to the capture video resolution and center it. If this
168 // WebView is not large enough to contain the holder at the preferred size,
169 // scale down to fit (preserving aspect ratio).
170 const gfx::Size capture_size = web_contents()->GetPreferredSize();
171 if (capture_size.width() <= holder_bounds.width() &&
172 capture_size.height() <= holder_bounds.height()) {
173 // No scaling, just centering.
174 holder_bounds.ClampToCenteredSize(capture_size);
175 } else {
176 // Scale down, preserving aspect ratio, and center.
177 // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
178 // looks like others have written this code elsewhere. Let's considate
179 // into a shared function ui/gfx/geometry or around there.
180 const int64 x = static_cast<int64>(capture_size.width()) *
181 holder_bounds.height();
182 const int64 y = static_cast<int64>(capture_size.height()) *
183 holder_bounds.width();
184 if (y < x) {
185 holder_bounds.ClampToCenteredSize(gfx::Size(
186 holder_bounds.width(), static_cast<int>(y / capture_size.width())));
187 } else {
188 holder_bounds.ClampToCenteredSize(gfx::Size(
189 static_cast<int>(x / capture_size.height()), holder_bounds.height()));
193 holder_->SetBoundsRect(holder_bounds);
196 void WebView::ViewHierarchyChanged(
197 const ViewHierarchyChangedDetails& details) {
198 if (details.is_add)
199 AttachWebContents();
202 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
203 if (allow_accelerators_)
204 return FocusManager::IsTabTraversalKeyEvent(event);
206 // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
207 // TabContents.
208 // We'll first give the page a chance to process the key events. If it does
209 // not process them, they'll be returned to us and we'll treat them as
210 // accelerators then.
211 return web_contents() && !web_contents()->IsCrashed();
214 bool WebView::OnMousePressed(const ui::MouseEvent& event) {
215 // A left-click within WebView is a request to focus. The area within the
216 // native view child is excluded since it will be handling mouse pressed
217 // events itself (http://crbug.com/436192).
218 if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location())) {
219 gfx::Point location_in_holder = event.location();
220 ConvertPointToTarget(this, holder_, &location_in_holder);
221 if (!holder_->HitTestPoint(location_in_holder)) {
222 RequestFocus();
223 return true;
226 return View::OnMousePressed(event);
229 void WebView::OnFocus() {
230 if (web_contents())
231 web_contents()->Focus();
234 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
235 if (web_contents())
236 web_contents()->FocusThroughTabTraversal(reverse);
239 void WebView::GetAccessibleState(ui::AXViewState* state) {
240 state->role = ui::AX_ROLE_WEB_VIEW;
243 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
244 if (web_contents()) {
245 content::RenderWidgetHostView* host_view =
246 web_contents()->GetRenderWidgetHostView();
247 if (host_view)
248 return host_view->GetNativeViewAccessible();
250 return View::GetNativeViewAccessible();
253 gfx::Size WebView::GetPreferredSize() const {
254 if (preferred_size_ == gfx::Size())
255 return View::GetPreferredSize();
256 else
257 return preferred_size_;
260 ////////////////////////////////////////////////////////////////////////////////
261 // WebView, content::RenderProcessHostObserver implementation:
263 void WebView::RenderProcessExited(content::RenderProcessHost* host,
264 base::TerminationStatus status,
265 int exit_code) {
266 NotifyMaybeTextInputClientAndAccessibilityChanged();
269 void WebView::RenderProcessHostDestroyed(content::RenderProcessHost* host) {
270 DCHECK_EQ(host, observing_render_process_host_);
271 observing_render_process_host_->RemoveObserver(this);
272 observing_render_process_host_ = nullptr;
275 ////////////////////////////////////////////////////////////////////////////////
276 // WebView, content::WebContentsDelegate implementation:
278 void WebView::WebContentsFocused(content::WebContents* web_contents) {
279 DCHECK(wc_owner_.get());
280 // The WebView is only the delegate of WebContentses it creates itself.
281 OnWebContentsFocused(wc_owner_.get());
284 bool WebView::EmbedsFullscreenWidget() const {
285 DCHECK(wc_owner_.get());
286 return embed_fullscreen_widget_mode_enabled_;
289 ////////////////////////////////////////////////////////////////////////////////
290 // WebView, content::WebContentsObserver implementation:
292 void WebView::RenderViewReady() {
293 NotifyMaybeTextInputClientAndAccessibilityChanged();
296 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
297 NotifyMaybeTextInputClientAndAccessibilityChanged();
300 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
301 content::RenderViewHost* new_host) {
302 FocusManager* const focus_manager = GetFocusManager();
303 if (focus_manager && focus_manager->GetFocusedView() == this)
304 OnFocus();
305 NotifyMaybeTextInputClientAndAccessibilityChanged();
308 void WebView::WebContentsDestroyed() {
309 if (observing_render_process_host_) {
310 observing_render_process_host_->RemoveObserver(this);
311 observing_render_process_host_ = nullptr;
313 NotifyMaybeTextInputClientAndAccessibilityChanged();
316 void WebView::DidShowFullscreenWidget(int routing_id) {
317 if (embed_fullscreen_widget_mode_enabled_)
318 ReattachForFullscreenChange(true);
321 void WebView::DidDestroyFullscreenWidget(int routing_id) {
322 if (embed_fullscreen_widget_mode_enabled_)
323 ReattachForFullscreenChange(false);
326 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
327 if (embed_fullscreen_widget_mode_enabled_)
328 ReattachForFullscreenChange(entered_fullscreen);
331 void WebView::DidAttachInterstitialPage() {
332 NotifyMaybeTextInputClientAndAccessibilityChanged();
335 void WebView::DidDetachInterstitialPage() {
336 NotifyMaybeTextInputClientAndAccessibilityChanged();
339 ////////////////////////////////////////////////////////////////////////////////
340 // WebView, private:
342 void WebView::AttachWebContents() {
343 // Prevents attachment if the WebView isn't already in a Widget, or it's
344 // already attached.
345 if (!GetWidget() || !web_contents())
346 return;
348 const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
349 web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
350 web_contents()->GetNativeView();
351 OnBoundsChanged(bounds());
352 if (holder_->native_view() == view_to_attach)
353 return;
355 holder_->Attach(view_to_attach);
357 // The view will not be focused automatically when it is attached, so we need
358 // to pass on focus to it if the FocusManager thinks the view is focused. Note
359 // that not every Widget has a focus manager.
360 FocusManager* const focus_manager = GetFocusManager();
361 if (focus_manager && focus_manager->GetFocusedView() == this)
362 OnFocus();
364 #if defined(OS_WIN)
365 if (!is_embedding_fullscreen_widget_) {
366 web_contents()->SetParentNativeViewAccessible(
367 parent()->GetNativeViewAccessible());
369 #endif
372 void WebView::DetachWebContents() {
373 if (web_contents()) {
374 holder_->Detach();
375 #if defined(OS_WIN)
376 if (!is_embedding_fullscreen_widget_)
377 web_contents()->SetParentNativeViewAccessible(NULL);
378 #endif
382 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
383 DCHECK(embed_fullscreen_widget_mode_enabled_);
384 const bool web_contents_has_separate_fs_widget =
385 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
386 if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
387 // Shutting down or starting up the embedding of the separate fullscreen
388 // widget. Need to detach and re-attach to a different native view.
389 DetachWebContents();
390 is_embedding_fullscreen_widget_ =
391 enter_fullscreen && web_contents_has_separate_fs_widget;
392 AttachWebContents();
393 } else {
394 // Entering or exiting "non-Flash" fullscreen mode, where the native view is
395 // the same. So, do not change attachment.
396 OnBoundsChanged(bounds());
398 NotifyMaybeTextInputClientAndAccessibilityChanged();
401 void WebView::NotifyMaybeTextInputClientAndAccessibilityChanged() {
402 // Update the TextInputClient as needed; see GetTextInputClient().
403 FocusManager* const focus_manager = GetFocusManager();
404 if (focus_manager)
405 focus_manager->OnTextInputClientChanged(this);
407 #if defined(OS_CHROMEOS)
408 if (web_contents())
409 NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, false);
410 #endif // defined OS_CHROMEOS
413 content::WebContents* WebView::CreateWebContents(
414 content::BrowserContext* browser_context) {
415 content::WebContents* contents = NULL;
416 if (ViewsDelegate::views_delegate) {
417 contents = ViewsDelegate::views_delegate->CreateWebContents(
418 browser_context, NULL);
421 if (!contents) {
422 content::WebContents::CreateParams create_params(
423 browser_context, NULL);
424 return content::WebContents::Create(create_params);
427 return contents;
430 } // namespace views