Home | History | Annotate | Download | only in webview
      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 
     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 
     42 WebView::~WebView() {
     43   SetWebContents(NULL);  // Make sure all necessary tear-down takes place.
     44   NativeViewAccessibility::UnregisterWebView(this);
     45 }
     46 
     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 
     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 
     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 
     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 
     87 void WebView::SetFastResize(bool fast_resize) {
     88   holder_->set_fast_resize(fast_resize);
     89 }
     90 
     91 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
     92   FocusManager* focus_manager = GetFocusManager();
     93   if (focus_manager)
     94     focus_manager->SetFocusedView(this);
     95 }
     96 
     97 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
     98   preferred_size_ = preferred_size;
     99   PreferredSizeChanged();
    100 }
    101 
    102 ////////////////////////////////////////////////////////////////////////////////
    103 // WebView, View overrides:
    104 
    105 const char* WebView::GetClassName() const {
    106   return kViewClassName;
    107 }
    108 
    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 
    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 
    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 
    185 void WebView::ViewHierarchyChanged(
    186     const ViewHierarchyChangedDetails& details) {
    187   if (details.is_add)
    188     AttachWebContents();
    189 }
    190 
    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 
    203 void WebView::OnFocus() {
    204   if (web_contents())
    205     web_contents()->Focus();
    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() 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 
    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::RenderProcessGone(base::TerminationStatus status) {
    256   NotifyMaybeTextInputClientChanged();
    257 }
    258 
    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 
    267 void WebView::DidShowFullscreenWidget(int routing_id) {
    268   if (embed_fullscreen_widget_mode_enabled_)
    269     ReattachForFullscreenChange(true);
    270 }
    271 
    272 void WebView::DidDestroyFullscreenWidget(int routing_id) {
    273   if (embed_fullscreen_widget_mode_enabled_)
    274     ReattachForFullscreenChange(false);
    275 }
    276 
    277 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
    278   if (embed_fullscreen_widget_mode_enabled_)
    279     ReattachForFullscreenChange(entered_fullscreen);
    280 }
    281 
    282 void WebView::DidAttachInterstitialPage() {
    283   NotifyMaybeTextInputClientChanged();
    284 }
    285 
    286 void WebView::DidDetachInterstitialPage() {
    287   NotifyMaybeTextInputClientChanged();
    288 }
    289 
    290 ////////////////////////////////////////////////////////////////////////////////
    291 // WebView, private:
    292 
    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 
    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 
    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 
    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 
    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