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