Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / webview / webview.cc
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 "ui/views/controls/webview/webview.h"
6
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"
22
23 namespace views {
24
25 // static
26 const char WebView::kViewClassName[] = "WebView";
27
28 ////////////////////////////////////////////////////////////////////////////////
29 // WebView, public:
30
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);
39 }
40
41 WebView::~WebView() {
42   SetWebContents(NULL);  // Make sure all necessary tear-down takes place.
43   NativeViewAccessibility::UnregisterWebView(this);
44 }
45
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());
51   }
52   return web_contents();
53 }
54
55 void WebView::SetWebContents(content::WebContents* replacement) {
56   if (replacement == web_contents())
57     return;
58   DetachWebContents();
59   WebContentsObserver::Observe(replacement);
60   // web_contents() now returns |replacement| from here onwards.
61   if (wc_owner_ != replacement)
62     wc_owner_.reset();
63   if (embed_fullscreen_widget_mode_enabled_) {
64     is_embedding_fullscreen_widget_ =
65         web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
66   } else {
67     DCHECK(!is_embedding_fullscreen_widget_);
68   }
69   AttachWebContents();
70   NotifyMaybeTextInputClientChanged();
71 }
72
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;
77 }
78
79 void WebView::LoadInitialURL(const GURL& url) {
80   GetWebContents()->GetController().LoadURL(
81       url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
82       std::string());
83 }
84
85 void WebView::SetFastResize(bool fast_resize) {
86   holder_->set_fast_resize(fast_resize);
87 }
88
89 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
90   FocusManager* focus_manager = GetFocusManager();
91   if (focus_manager)
92     focus_manager->SetFocusedView(this);
93 }
94
95 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
96   preferred_size_ = preferred_size;
97   PreferredSizeChanged();
98 }
99
100 ////////////////////////////////////////////////////////////////////////////////
101 // WebView, View overrides:
102
103 const char* WebView::GetClassName() const {
104   return kViewClassName;
105 }
106
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();
119     if (host_view)
120       return host_view->GetTextInputClient();
121   }
122   return NULL;
123 }
124
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_ ||
131       !web_contents() ||
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);
139     return;
140   }
141
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);
150   } else {
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();
159     if (y < x) {
160       holder_bounds.ClampToCenteredSize(gfx::Size(
161           holder_bounds.width(), static_cast<int>(y / capture_size.width())));
162     } else {
163       holder_bounds.ClampToCenteredSize(gfx::Size(
164           static_cast<int>(x / capture_size.height()), holder_bounds.height()));
165     }
166   }
167
168   holder_->SetBoundsRect(holder_bounds);
169 }
170
171 void WebView::ViewHierarchyChanged(
172     const ViewHierarchyChangedDetails& details) {
173   if (details.is_add)
174     AttachWebContents();
175 }
176
177 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
178   if (allow_accelerators_)
179     return FocusManager::IsTabTraversalKeyEvent(event);
180
181   // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
182   // TabContents.
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();
187 }
188
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();
193 }
194
195 void WebView::OnFocus() {
196   if (!web_contents())
197     return;
198   if (is_embedding_fullscreen_widget_) {
199     content::RenderWidgetHostView* const current_fs_view =
200         web_contents()->GetFullscreenRenderWidgetHostView();
201     if (current_fs_view)
202       current_fs_view->Focus();
203   } else {
204     web_contents()->Focus();
205   }
206 }
207
208 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
209   if (web_contents())
210     web_contents()->FocusThroughTabTraversal(reverse);
211 }
212
213 void WebView::GetAccessibleState(ui::AXViewState* state) {
214   state->role = ui::AX_ROLE_GROUP;
215 }
216
217 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
218   if (web_contents()) {
219     content::RenderWidgetHostView* host_view =
220         web_contents()->GetRenderWidgetHostView();
221     if (host_view)
222       return host_view->GetNativeViewAccessible();
223   }
224   return View::GetNativeViewAccessible();
225 }
226
227 gfx::Size WebView::GetPreferredSize() {
228   if (preferred_size_ == gfx::Size())
229     return View::GetPreferredSize();
230   else
231     return preferred_size_;
232 }
233
234 ////////////////////////////////////////////////////////////////////////////////
235 // WebView, content::WebContentsDelegate implementation:
236
237 void WebView::WebContentsFocused(content::WebContents* web_contents) {
238   DCHECK(wc_owner_.get());
239   // The WebView is only the delegate of WebContentses it creates itself.
240   OnWebContentsFocused(wc_owner_.get());
241 }
242
243 bool WebView::EmbedsFullscreenWidget() const {
244   DCHECK(wc_owner_.get());
245   return embed_fullscreen_widget_mode_enabled_;
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // WebView, content::WebContentsObserver implementation:
250
251 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
252   NotifyMaybeTextInputClientChanged();
253 }
254
255 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
256                                     content::RenderViewHost* new_host) {
257   FocusManager* const focus_manager = GetFocusManager();
258   if (focus_manager && focus_manager->GetFocusedView() == this)
259     OnFocus();
260   NotifyMaybeTextInputClientChanged();
261 }
262
263 void WebView::DidShowFullscreenWidget(int routing_id) {
264   if (embed_fullscreen_widget_mode_enabled_)
265     ReattachForFullscreenChange(true);
266 }
267
268 void WebView::DidDestroyFullscreenWidget(int routing_id) {
269   if (embed_fullscreen_widget_mode_enabled_)
270     ReattachForFullscreenChange(false);
271 }
272
273 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
274   if (embed_fullscreen_widget_mode_enabled_)
275     ReattachForFullscreenChange(entered_fullscreen);
276 }
277
278 ////////////////////////////////////////////////////////////////////////////////
279 // WebView, private:
280
281 void WebView::AttachWebContents() {
282   // Prevents attachment if the WebView isn't already in a Widget, or it's
283   // already attached.
284   if (!GetWidget() || !web_contents())
285     return;
286
287   const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
288       web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
289       web_contents()->GetNativeView();
290   OnBoundsChanged(bounds());
291   if (holder_->native_view() == view_to_attach)
292     return;
293   holder_->Attach(view_to_attach);
294
295   // The view will not be focused automatically when it is attached, so we need
296   // to pass on focus to it if the FocusManager thinks the view is focused. Note
297   // that not every Widget has a focus manager.
298   FocusManager* const focus_manager = GetFocusManager();
299   if (focus_manager && focus_manager->GetFocusedView() == this)
300     OnFocus();
301
302 #if defined(OS_WIN)
303   if (!is_embedding_fullscreen_widget_) {
304     web_contents()->SetParentNativeViewAccessible(
305         parent()->GetNativeViewAccessible());
306   }
307 #endif
308 }
309
310 void WebView::DetachWebContents() {
311   if (web_contents()) {
312     holder_->Detach();
313 #if defined(OS_WIN)
314     if (!is_embedding_fullscreen_widget_)
315       web_contents()->SetParentNativeViewAccessible(NULL);
316 #endif
317   }
318 }
319
320 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
321   DCHECK(embed_fullscreen_widget_mode_enabled_);
322   const bool web_contents_has_separate_fs_widget =
323       web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
324   if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
325     // Shutting down or starting up the embedding of the separate fullscreen
326     // widget.  Need to detach and re-attach to a different native view.
327     DetachWebContents();
328     is_embedding_fullscreen_widget_ =
329         enter_fullscreen && web_contents_has_separate_fs_widget;
330     AttachWebContents();
331   } else {
332     // Entering or exiting "non-Flash" fullscreen mode, where the native view is
333     // the same.  So, do not change attachment.
334     OnBoundsChanged(bounds());
335   }
336   NotifyMaybeTextInputClientChanged();
337 }
338
339 void WebView::NotifyMaybeTextInputClientChanged() {
340   // Update the TextInputClient as needed; see GetTextInputClient().
341   FocusManager* const focus_manager = GetFocusManager();
342   if (focus_manager)
343     focus_manager->OnTextInputClientChanged(this);
344 }
345
346 content::WebContents* WebView::CreateWebContents(
347       content::BrowserContext* browser_context) {
348   content::WebContents* contents = NULL;
349   if (ViewsDelegate::views_delegate) {
350     contents = ViewsDelegate::views_delegate->CreateWebContents(
351         browser_context, NULL);
352   }
353
354   if (!contents) {
355     content::WebContents::CreateParams create_params(
356         browser_context, NULL);
357     return content::WebContents::Create(create_params);
358   }
359
360   return contents;
361 }
362
363 }  // namespace views