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