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 "content/public/browser/web_contents_view.h"
     14 #include "ipc/ipc_message.h"
     15 #include "ui/base/accessibility/accessibility_types.h"
     16 #include "ui/base/accessibility/accessible_view_state.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     : wcv_holder_(new NativeViewHost),
     33       web_contents_(NULL),
     34       embed_fullscreen_widget_mode_enabled_(false),
     35       is_embedding_fullscreen_widget_(false),
     36       browser_context_(browser_context),
     37       allow_accelerators_(false) {
     38   AddChildView(wcv_holder_);
     39   NativeViewAccessibility::RegisterWebView(this);
     40 }
     41 
     42 WebView::~WebView() {
     43   NativeViewAccessibility::UnregisterWebView(this);
     44 }
     45 
     46 content::WebContents* WebView::GetWebContents() {
     47   CreateWebContentsWithSiteInstance(NULL);
     48   return web_contents_;
     49 }
     50 
     51 void WebView::CreateWebContentsWithSiteInstance(
     52     content::SiteInstance* site_instance) {
     53   if (!web_contents_) {
     54     wc_owner_.reset(CreateWebContents(browser_context_, site_instance));
     55     web_contents_ = wc_owner_.get();
     56     web_contents_->SetDelegate(this);
     57     AttachWebContents();
     58   }
     59 }
     60 
     61 void WebView::SetWebContents(content::WebContents* web_contents) {
     62   if (web_contents == web_contents_)
     63     return;
     64   DetachWebContents();
     65   if (wc_owner_ != web_contents)
     66     wc_owner_.reset();
     67   web_contents_ = web_contents;
     68   if (embed_fullscreen_widget_mode_enabled_) {
     69     is_embedding_fullscreen_widget_ =
     70         web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
     71   } else {
     72     is_embedding_fullscreen_widget_ = false;
     73   }
     74   AttachWebContents();
     75 }
     76 
     77 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
     78   bool should_be_embedded = enable;
     79   if (!embed_fullscreen_widget_mode_enabled_ && enable) {
     80     DCHECK(!is_embedding_fullscreen_widget_);
     81     embed_fullscreen_widget_mode_enabled_ = true;
     82     should_be_embedded =
     83         web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
     84   } else if (embed_fullscreen_widget_mode_enabled_ && !enable) {
     85     embed_fullscreen_widget_mode_enabled_ = false;
     86   }
     87   if (should_be_embedded != is_embedding_fullscreen_widget_)
     88     ReattachForFullscreenChange(should_be_embedded);
     89 }
     90 
     91 void WebView::LoadInitialURL(const GURL& url) {
     92   GetWebContents()->GetController().LoadURL(
     93       url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
     94       std::string());
     95 }
     96 
     97 void WebView::SetFastResize(bool fast_resize) {
     98   wcv_holder_->set_fast_resize(fast_resize);
     99 }
    100 
    101 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
    102   FocusManager* focus_manager = GetFocusManager();
    103   if (focus_manager)
    104     focus_manager->SetFocusedView(this);
    105 }
    106 
    107 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
    108   preferred_size_ = preferred_size;
    109   PreferredSizeChanged();
    110 }
    111 
    112 ////////////////////////////////////////////////////////////////////////////////
    113 // WebView, View overrides:
    114 
    115 const char* WebView::GetClassName() const {
    116   return kViewClassName;
    117 }
    118 
    119 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
    120   wcv_holder_->SetSize(bounds().size());
    121 }
    122 
    123 void WebView::ViewHierarchyChanged(
    124     const ViewHierarchyChangedDetails& details) {
    125   if (details.is_add)
    126     AttachWebContents();
    127 }
    128 
    129 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
    130   if (allow_accelerators_)
    131     return FocusManager::IsTabTraversalKeyEvent(event);
    132 
    133   // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
    134   // TabContents.
    135   // We'll first give the page a chance to process the key events.  If it does
    136   // not process them, they'll be returned to us and we'll treat them as
    137   // accelerators then.
    138   return web_contents_ && !web_contents_->IsCrashed();
    139 }
    140 
    141 bool WebView::IsFocusable() const {
    142   // We need to be focusable when our contents is not a view hierarchy, as
    143   // clicking on the contents needs to focus us.
    144   return !!web_contents_;
    145 }
    146 
    147 void WebView::OnFocus() {
    148   if (!web_contents_)
    149     return;
    150   if (is_embedding_fullscreen_widget_) {
    151     content::RenderWidgetHostView* const current_fs_view =
    152         web_contents_->GetFullscreenRenderWidgetHostView();
    153     if (current_fs_view)
    154       current_fs_view->Focus();
    155   } else {
    156     web_contents_->GetView()->Focus();
    157   }
    158 }
    159 
    160 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
    161   if (web_contents_)
    162     web_contents_->FocusThroughTabTraversal(reverse);
    163 }
    164 
    165 void WebView::GetAccessibleState(ui::AccessibleViewState* state) {
    166   state->role = ui::AccessibilityTypes::ROLE_GROUPING;
    167 }
    168 
    169 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
    170   if (web_contents_) {
    171     content::RenderWidgetHostView* host_view =
    172         web_contents_->GetRenderWidgetHostView();
    173     if (host_view)
    174       return host_view->GetNativeViewAccessible();
    175   }
    176   return View::GetNativeViewAccessible();
    177 }
    178 
    179 gfx::Size WebView::GetPreferredSize() {
    180   if (preferred_size_ == gfx::Size())
    181     return View::GetPreferredSize();
    182   else
    183     return preferred_size_;
    184 }
    185 
    186 ////////////////////////////////////////////////////////////////////////////////
    187 // WebView, content::WebContentsDelegate implementation:
    188 
    189 void WebView::WebContentsFocused(content::WebContents* web_contents) {
    190   DCHECK(wc_owner_.get());
    191   // The WebView is only the delegate of WebContentses it creates itself.
    192   OnWebContentsFocused(web_contents_);
    193 }
    194 
    195 bool WebView::EmbedsFullscreenWidget() const {
    196   DCHECK(wc_owner_.get());
    197   return embed_fullscreen_widget_mode_enabled_;
    198 }
    199 
    200 ////////////////////////////////////////////////////////////////////////////////
    201 // WebView, content::WebContentsObserver implementation:
    202 
    203 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
    204                                     content::RenderViewHost* new_host) {
    205   FocusManager* const focus_manager = GetFocusManager();
    206   if (focus_manager && focus_manager->GetFocusedView() == this)
    207     OnFocus();
    208 }
    209 
    210 void WebView::WebContentsDestroyed(content::WebContents* web_contents) {
    211   // We watch for destruction of WebContents that we host but do not own. If we
    212   // own a WebContents that is being destroyed, we're doing the destroying, so
    213   // we don't want to recursively tear it down while it's being torn down.
    214   if (!wc_owner_.get())
    215     SetWebContents(NULL);
    216 }
    217 
    218 void WebView::DidShowFullscreenWidget(int routing_id) {
    219   if (embed_fullscreen_widget_mode_enabled_)
    220     ReattachForFullscreenChange(true);
    221 }
    222 
    223 void WebView::DidDestroyFullscreenWidget(int routing_id) {
    224   if (embed_fullscreen_widget_mode_enabled_)
    225     ReattachForFullscreenChange(false);
    226 }
    227 
    228 ////////////////////////////////////////////////////////////////////////////////
    229 // WebView, private:
    230 
    231 void WebView::AttachWebContents() {
    232   // Prevents attachment if the WebView isn't already in a Widget, or it's
    233   // already attached.
    234   if (!GetWidget() || !web_contents_)
    235     return;
    236 
    237   const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
    238       web_contents_->GetFullscreenRenderWidgetHostView()->GetNativeView() :
    239       web_contents_->GetView()->GetNativeView();
    240   if (wcv_holder_->native_view() == view_to_attach)
    241     return;
    242   wcv_holder_->Attach(view_to_attach);
    243 
    244   // The view will not be focused automatically when it is attached, so we need
    245   // to pass on focus to it if the FocusManager thinks the view is focused. Note
    246   // that not every Widget has a focus manager.
    247   FocusManager* const focus_manager = GetFocusManager();
    248   if (focus_manager && focus_manager->GetFocusedView() == this)
    249     OnFocus();
    250 
    251   WebContentsObserver::Observe(web_contents_);
    252 
    253 #if defined(OS_WIN) && defined(USE_AURA)
    254   if (!is_embedding_fullscreen_widget_) {
    255     web_contents_->SetParentNativeViewAccessible(
    256         parent()->GetNativeViewAccessible());
    257   }
    258 #endif
    259 }
    260 
    261 void WebView::DetachWebContents() {
    262   if (web_contents_) {
    263     wcv_holder_->Detach();
    264 #if defined(OS_WIN)
    265     if (!is_embedding_fullscreen_widget_) {
    266 #if !defined(USE_AURA)
    267       // TODO(beng): This should either not be necessary, or be done implicitly
    268       // by NativeViewHostWin on Detach(). As it stands, this is needed so that
    269       // the of the detached contents knows to tell the renderer it's been
    270       // hidden.
    271       //
    272       // Moving this out of here would also mean we wouldn't be potentially
    273       // calling member functions on a half-destroyed WebContents.
    274       ShowWindow(web_contents_->GetView()->GetNativeView(), SW_HIDE);
    275 #else
    276       web_contents_->SetParentNativeViewAccessible(NULL);
    277 #endif
    278     }
    279 #endif
    280   }
    281   WebContentsObserver::Observe(NULL);
    282 }
    283 
    284 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
    285   DetachWebContents();
    286   is_embedding_fullscreen_widget_ = enter_fullscreen &&
    287       web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
    288   AttachWebContents();
    289 }
    290 
    291 content::WebContents* WebView::CreateWebContents(
    292       content::BrowserContext* browser_context,
    293       content::SiteInstance* site_instance) {
    294   content::WebContents* contents = NULL;
    295   if (ViewsDelegate::views_delegate) {
    296     contents = ViewsDelegate::views_delegate->CreateWebContents(
    297         browser_context, site_instance);
    298   }
    299 
    300   if (!contents) {
    301     content::WebContents::CreateParams create_params(
    302         browser_context, site_instance);
    303     return content::WebContents::Create(create_params);
    304   }
    305 
    306   return contents;
    307 }
    308 
    309 }  // namespace views
    310