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/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() 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::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
    364