• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/aura/window.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/events/event.h"
19 #include "ui/views/accessibility/native_view_accessibility.h"
20 #include "ui/views/controls/native/native_view_host.h"
21 #include "ui/views/focus/focus_manager.h"
22 #include "ui/views/views_delegate.h"
23 
24 namespace views {
25 
26 // static
27 const char WebView::kViewClassName[] = "WebView";
28 
29 ////////////////////////////////////////////////////////////////////////////////
30 // WebView, public:
31 
WebView(content::BrowserContext * browser_context)32 WebView::WebView(content::BrowserContext* browser_context)
33     : holder_(new NativeViewHost()),
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_|.
39   NativeViewAccessibility::RegisterWebView(this);
40 }
41 
~WebView()42 WebView::~WebView() {
43   SetWebContents(NULL);  // Make sure all necessary tear-down takes place.
44   NativeViewAccessibility::UnregisterWebView(this);
45 }
46 
GetWebContents()47 content::WebContents* WebView::GetWebContents() {
48   if (!web_contents()) {
49     wc_owner_.reset(CreateWebContents(browser_context_));
50     wc_owner_->SetDelegate(this);
51     SetWebContents(wc_owner_.get());
52   }
53   return web_contents();
54 }
55 
SetWebContents(content::WebContents * replacement)56 void WebView::SetWebContents(content::WebContents* replacement) {
57   if (replacement == web_contents())
58     return;
59   DetachWebContents();
60   WebContentsObserver::Observe(replacement);
61   // web_contents() now returns |replacement| from here onwards.
62   SetFocusable(!!web_contents());
63   if (wc_owner_ != replacement)
64     wc_owner_.reset();
65   if (embed_fullscreen_widget_mode_enabled_) {
66     is_embedding_fullscreen_widget_ =
67         web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
68   } else {
69     DCHECK(!is_embedding_fullscreen_widget_);
70   }
71   AttachWebContents();
72   NotifyMaybeTextInputClientChanged();
73 }
74 
SetEmbedFullscreenWidgetMode(bool enable)75 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
76   DCHECK(!web_contents())
77       << "Cannot change mode while a WebContents is attached.";
78   embed_fullscreen_widget_mode_enabled_ = enable;
79 }
80 
LoadInitialURL(const GURL & url)81 void WebView::LoadInitialURL(const GURL& url) {
82   GetWebContents()->GetController().LoadURL(
83       url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
84       std::string());
85 }
86 
SetFastResize(bool fast_resize)87 void WebView::SetFastResize(bool fast_resize) {
88   holder_->set_fast_resize(fast_resize);
89 }
90 
OnWebContentsFocused(content::WebContents * web_contents)91 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
92   FocusManager* focus_manager = GetFocusManager();
93   if (focus_manager)
94     focus_manager->SetFocusedView(this);
95 }
96 
SetPreferredSize(const gfx::Size & preferred_size)97 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
98   preferred_size_ = preferred_size;
99   PreferredSizeChanged();
100 }
101 
102 ////////////////////////////////////////////////////////////////////////////////
103 // WebView, View overrides:
104 
GetClassName() const105 const char* WebView::GetClassName() const {
106   return kViewClassName;
107 }
108 
GetTextInputClient()109 ui::TextInputClient* WebView::GetTextInputClient() {
110   // This function delegates the text input handling to the underlying
111   // content::RenderWidgetHostView.  So when the underlying RWHV is destroyed or
112   // replaced with another one, we have to notify the FocusManager through
113   // FocusManager::OnTextInputClientChanged() that the focused TextInputClient
114   // needs to be updated.
115   if (switches::IsTextInputFocusManagerEnabled() &&
116       web_contents() && !web_contents()->IsBeingDestroyed()) {
117     content::RenderWidgetHostView* host_view =
118         is_embedding_fullscreen_widget_ ?
119         web_contents()->GetFullscreenRenderWidgetHostView() :
120         web_contents()->GetRenderWidgetHostView();
121     if (host_view)
122       return host_view->GetTextInputClient();
123   }
124   return NULL;
125 }
126 
SwapWebContents(scoped_ptr<content::WebContents> new_web_contents)127 scoped_ptr<content::WebContents> WebView::SwapWebContents(
128     scoped_ptr<content::WebContents> new_web_contents) {
129   if (wc_owner_)
130     wc_owner_->SetDelegate(NULL);
131   scoped_ptr<content::WebContents> old_web_contents(wc_owner_.Pass());
132   wc_owner_ = new_web_contents.Pass();
133   if (wc_owner_)
134     wc_owner_->SetDelegate(this);
135   SetWebContents(wc_owner_.get());
136   return old_web_contents.Pass();
137 }
138 
OnBoundsChanged(const gfx::Rect & previous_bounds)139 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
140   // In most cases, the holder is simply sized to fill this WebView's bounds.
141   // Only WebContentses that are in fullscreen mode and being screen-captured
142   // will engage the special layout/sizing behavior.
143   gfx::Rect holder_bounds(bounds().size());
144   if (!embed_fullscreen_widget_mode_enabled_ ||
145       !web_contents() ||
146       web_contents()->GetCapturerCount() == 0 ||
147       web_contents()->GetPreferredSize().IsEmpty() ||
148       !(is_embedding_fullscreen_widget_ ||
149         (web_contents()->GetDelegate() &&
150          web_contents()->GetDelegate()->
151              IsFullscreenForTabOrPending(web_contents())))) {
152     holder_->SetBoundsRect(holder_bounds);
153     return;
154   }
155 
156   // Size the holder to the capture video resolution and center it.  If this
157   // WebView is not large enough to contain the holder at the preferred size,
158   // scale down to fit (preserving aspect ratio).
159   const gfx::Size capture_size = web_contents()->GetPreferredSize();
160   if (capture_size.width() <= holder_bounds.width() &&
161       capture_size.height() <= holder_bounds.height()) {
162     // No scaling, just centering.
163     holder_bounds.ClampToCenteredSize(capture_size);
164   } else {
165     // Scale down, preserving aspect ratio, and center.
166     // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
167     // looks like others have written this code elsewhere.  Let's considate
168     // into a shared function ui/gfx/geometry or around there.
169     const int64 x = static_cast<int64>(capture_size.width()) *
170         holder_bounds.height();
171     const int64 y = static_cast<int64>(capture_size.height()) *
172         holder_bounds.width();
173     if (y < x) {
174       holder_bounds.ClampToCenteredSize(gfx::Size(
175           holder_bounds.width(), static_cast<int>(y / capture_size.width())));
176     } else {
177       holder_bounds.ClampToCenteredSize(gfx::Size(
178           static_cast<int>(x / capture_size.height()), holder_bounds.height()));
179     }
180   }
181 
182   holder_->SetBoundsRect(holder_bounds);
183 }
184 
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)185 void WebView::ViewHierarchyChanged(
186     const ViewHierarchyChangedDetails& details) {
187   if (details.is_add)
188     AttachWebContents();
189 }
190 
SkipDefaultKeyEventProcessing(const ui::KeyEvent & event)191 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
192   if (allow_accelerators_)
193     return FocusManager::IsTabTraversalKeyEvent(event);
194 
195   // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
196   // TabContents.
197   // We'll first give the page a chance to process the key events.  If it does
198   // not process them, they'll be returned to us and we'll treat them as
199   // accelerators then.
200   return web_contents() && !web_contents()->IsCrashed();
201 }
202 
OnFocus()203 void WebView::OnFocus() {
204   if (web_contents())
205     web_contents()->Focus();
206 }
207 
AboutToRequestFocusFromTabTraversal(bool reverse)208 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
209   if (web_contents())
210     web_contents()->FocusThroughTabTraversal(reverse);
211 }
212 
GetAccessibleState(ui::AXViewState * state)213 void WebView::GetAccessibleState(ui::AXViewState* state) {
214   state->role = ui::AX_ROLE_GROUP;
215 }
216 
GetNativeViewAccessible()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 
GetPreferredSize() const227 gfx::Size WebView::GetPreferredSize() const {
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 
WebContentsFocused(content::WebContents * web_contents)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 
EmbedsFullscreenWidget() const243 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 
RenderViewDeleted(content::RenderViewHost * render_view_host)251 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
252   NotifyMaybeTextInputClientChanged();
253 }
254 
RenderProcessGone(base::TerminationStatus status)255 void WebView::RenderProcessGone(base::TerminationStatus status) {
256   NotifyMaybeTextInputClientChanged();
257 }
258 
RenderViewHostChanged(content::RenderViewHost * old_host,content::RenderViewHost * new_host)259 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
260                                     content::RenderViewHost* new_host) {
261   FocusManager* const focus_manager = GetFocusManager();
262   if (focus_manager && focus_manager->GetFocusedView() == this)
263     OnFocus();
264   NotifyMaybeTextInputClientChanged();
265 }
266 
DidShowFullscreenWidget(int routing_id)267 void WebView::DidShowFullscreenWidget(int routing_id) {
268   if (embed_fullscreen_widget_mode_enabled_)
269     ReattachForFullscreenChange(true);
270 }
271 
DidDestroyFullscreenWidget(int routing_id)272 void WebView::DidDestroyFullscreenWidget(int routing_id) {
273   if (embed_fullscreen_widget_mode_enabled_)
274     ReattachForFullscreenChange(false);
275 }
276 
DidToggleFullscreenModeForTab(bool entered_fullscreen)277 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
278   if (embed_fullscreen_widget_mode_enabled_)
279     ReattachForFullscreenChange(entered_fullscreen);
280 }
281 
DidAttachInterstitialPage()282 void WebView::DidAttachInterstitialPage() {
283   NotifyMaybeTextInputClientChanged();
284 }
285 
DidDetachInterstitialPage()286 void WebView::DidDetachInterstitialPage() {
287   NotifyMaybeTextInputClientChanged();
288 }
289 
290 ////////////////////////////////////////////////////////////////////////////////
291 // WebView, private:
292 
AttachWebContents()293 void WebView::AttachWebContents() {
294   // Prevents attachment if the WebView isn't already in a Widget, or it's
295   // already attached.
296   if (!GetWidget() || !web_contents())
297     return;
298 
299   const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
300       web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
301       web_contents()->GetNativeView();
302   OnBoundsChanged(bounds());
303   if (holder_->native_view() == view_to_attach)
304     return;
305 
306   // The WCV needs to be parented before making it visible.
307   holder_->Attach(view_to_attach);
308 
309   // Fullscreen widgets are not parented by a WebContentsView. Their visibility
310   // is controlled by content i.e. (RenderWidgetHost)
311   if (!is_embedding_fullscreen_widget_)
312     view_to_attach->Show();
313 
314   // The view will not be focused automatically when it is attached, so we need
315   // to pass on focus to it if the FocusManager thinks the view is focused. Note
316   // that not every Widget has a focus manager.
317   FocusManager* const focus_manager = GetFocusManager();
318   if (focus_manager && focus_manager->GetFocusedView() == this)
319     OnFocus();
320 
321 #if defined(OS_WIN)
322   if (!is_embedding_fullscreen_widget_) {
323     web_contents()->SetParentNativeViewAccessible(
324         parent()->GetNativeViewAccessible());
325   }
326 #endif
327 }
328 
DetachWebContents()329 void WebView::DetachWebContents() {
330   if (web_contents()) {
331     // Fullscreen widgets are not parented by a WebContentsView. Their
332     // visibility is controlled by content i.e. (RenderWidgetHost).
333     if (!is_embedding_fullscreen_widget_)
334       web_contents()->GetNativeView()->Hide();
335 
336     holder_->Detach();
337 #if defined(OS_WIN)
338     if (!is_embedding_fullscreen_widget_)
339       web_contents()->SetParentNativeViewAccessible(NULL);
340 #endif
341   }
342 }
343 
ReattachForFullscreenChange(bool enter_fullscreen)344 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
345   DCHECK(embed_fullscreen_widget_mode_enabled_);
346   const bool web_contents_has_separate_fs_widget =
347       web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
348   if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
349     // Shutting down or starting up the embedding of the separate fullscreen
350     // widget.  Need to detach and re-attach to a different native view.
351     DetachWebContents();
352     is_embedding_fullscreen_widget_ =
353         enter_fullscreen && web_contents_has_separate_fs_widget;
354     AttachWebContents();
355   } else {
356     // Entering or exiting "non-Flash" fullscreen mode, where the native view is
357     // the same.  So, do not change attachment.
358     OnBoundsChanged(bounds());
359   }
360   NotifyMaybeTextInputClientChanged();
361 }
362 
NotifyMaybeTextInputClientChanged()363 void WebView::NotifyMaybeTextInputClientChanged() {
364   // Update the TextInputClient as needed; see GetTextInputClient().
365   FocusManager* const focus_manager = GetFocusManager();
366   if (focus_manager)
367     focus_manager->OnTextInputClientChanged(this);
368 }
369 
CreateWebContents(content::BrowserContext * browser_context)370 content::WebContents* WebView::CreateWebContents(
371       content::BrowserContext* browser_context) {
372   content::WebContents* contents = NULL;
373   if (ViewsDelegate::views_delegate) {
374     contents = ViewsDelegate::views_delegate->CreateWebContents(
375         browser_context, NULL);
376   }
377 
378   if (!contents) {
379     content::WebContents::CreateParams create_params(
380         browser_context, NULL);
381     return content::WebContents::Create(create_params);
382   }
383 
384   return contents;
385 }
386 
387 }  // namespace views
388