| OLD | NEW |
| (Empty) |
| 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 SetFocusable(!!web_contents()); | |
| 62 if (wc_owner_ != replacement) | |
| 63 wc_owner_.reset(); | |
| 64 if (embed_fullscreen_widget_mode_enabled_) { | |
| 65 is_embedding_fullscreen_widget_ = | |
| 66 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); | |
| 67 } else { | |
| 68 DCHECK(!is_embedding_fullscreen_widget_); | |
| 69 } | |
| 70 AttachWebContents(); | |
| 71 NotifyMaybeTextInputClientChanged(); | |
| 72 } | |
| 73 | |
| 74 void WebView::SetEmbedFullscreenWidgetMode(bool enable) { | |
| 75 DCHECK(!web_contents()) | |
| 76 << "Cannot change mode while a WebContents is attached."; | |
| 77 embed_fullscreen_widget_mode_enabled_ = enable; | |
| 78 } | |
| 79 | |
| 80 void WebView::LoadInitialURL(const GURL& url) { | |
| 81 GetWebContents()->GetController().LoadURL( | |
| 82 url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
| 83 std::string()); | |
| 84 } | |
| 85 | |
| 86 void WebView::SetFastResize(bool fast_resize) { | |
| 87 holder_->set_fast_resize(fast_resize); | |
| 88 } | |
| 89 | |
| 90 void WebView::OnWebContentsFocused(content::WebContents* web_contents) { | |
| 91 FocusManager* focus_manager = GetFocusManager(); | |
| 92 if (focus_manager) | |
| 93 focus_manager->SetFocusedView(this); | |
| 94 } | |
| 95 | |
| 96 void WebView::SetPreferredSize(const gfx::Size& preferred_size) { | |
| 97 preferred_size_ = preferred_size; | |
| 98 PreferredSizeChanged(); | |
| 99 } | |
| 100 | |
| 101 //////////////////////////////////////////////////////////////////////////////// | |
| 102 // WebView, View overrides: | |
| 103 | |
| 104 const char* WebView::GetClassName() const { | |
| 105 return kViewClassName; | |
| 106 } | |
| 107 | |
| 108 ui::TextInputClient* WebView::GetTextInputClient() { | |
| 109 // This function delegates the text input handling to the underlying | |
| 110 // content::RenderWidgetHostView. So when the underlying RWHV is destroyed or | |
| 111 // replaced with another one, we have to notify the FocusManager through | |
| 112 // FocusManager::OnTextInputClientChanged() that the focused TextInputClient | |
| 113 // needs to be updated. | |
| 114 if (switches::IsTextInputFocusManagerEnabled() && | |
| 115 web_contents() && !web_contents()->IsBeingDestroyed()) { | |
| 116 content::RenderWidgetHostView* host_view = | |
| 117 is_embedding_fullscreen_widget_ ? | |
| 118 web_contents()->GetFullscreenRenderWidgetHostView() : | |
| 119 web_contents()->GetRenderWidgetHostView(); | |
| 120 if (host_view) | |
| 121 return host_view->GetTextInputClient(); | |
| 122 } | |
| 123 return NULL; | |
| 124 } | |
| 125 | |
| 126 scoped_ptr<content::WebContents> WebView::SwapWebContents( | |
| 127 scoped_ptr<content::WebContents> new_web_contents) { | |
| 128 if (wc_owner_) | |
| 129 wc_owner_->SetDelegate(NULL); | |
| 130 scoped_ptr<content::WebContents> old_web_contents(wc_owner_.Pass()); | |
| 131 wc_owner_ = new_web_contents.Pass(); | |
| 132 if (wc_owner_) | |
| 133 wc_owner_->SetDelegate(this); | |
| 134 SetWebContents(wc_owner_.get()); | |
| 135 return old_web_contents.Pass(); | |
| 136 } | |
| 137 | |
| 138 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { | |
| 139 // In most cases, the holder is simply sized to fill this WebView's bounds. | |
| 140 // Only WebContentses that are in fullscreen mode and being screen-captured | |
| 141 // will engage the special layout/sizing behavior. | |
| 142 gfx::Rect holder_bounds(bounds().size()); | |
| 143 if (!embed_fullscreen_widget_mode_enabled_ || | |
| 144 !web_contents() || | |
| 145 web_contents()->GetCapturerCount() == 0 || | |
| 146 web_contents()->GetPreferredSize().IsEmpty() || | |
| 147 !(is_embedding_fullscreen_widget_ || | |
| 148 (web_contents()->GetDelegate() && | |
| 149 web_contents()->GetDelegate()-> | |
| 150 IsFullscreenForTabOrPending(web_contents())))) { | |
| 151 holder_->SetBoundsRect(holder_bounds); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 // Size the holder to the capture video resolution and center it. If this | |
| 156 // WebView is not large enough to contain the holder at the preferred size, | |
| 157 // scale down to fit (preserving aspect ratio). | |
| 158 const gfx::Size capture_size = web_contents()->GetPreferredSize(); | |
| 159 if (capture_size.width() <= holder_bounds.width() && | |
| 160 capture_size.height() <= holder_bounds.height()) { | |
| 161 // No scaling, just centering. | |
| 162 holder_bounds.ClampToCenteredSize(capture_size); | |
| 163 } else { | |
| 164 // Scale down, preserving aspect ratio, and center. | |
| 165 // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it | |
| 166 // looks like others have written this code elsewhere. Let's considate | |
| 167 // into a shared function ui/gfx/geometry or around there. | |
| 168 const int64 x = static_cast<int64>(capture_size.width()) * | |
| 169 holder_bounds.height(); | |
| 170 const int64 y = static_cast<int64>(capture_size.height()) * | |
| 171 holder_bounds.width(); | |
| 172 if (y < x) { | |
| 173 holder_bounds.ClampToCenteredSize(gfx::Size( | |
| 174 holder_bounds.width(), static_cast<int>(y / capture_size.width()))); | |
| 175 } else { | |
| 176 holder_bounds.ClampToCenteredSize(gfx::Size( | |
| 177 static_cast<int>(x / capture_size.height()), holder_bounds.height())); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 holder_->SetBoundsRect(holder_bounds); | |
| 182 } | |
| 183 | |
| 184 void WebView::ViewHierarchyChanged( | |
| 185 const ViewHierarchyChangedDetails& details) { | |
| 186 if (details.is_add) | |
| 187 AttachWebContents(); | |
| 188 } | |
| 189 | |
| 190 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { | |
| 191 if (allow_accelerators_) | |
| 192 return FocusManager::IsTabTraversalKeyEvent(event); | |
| 193 | |
| 194 // Don't look-up accelerators or tab-traversal if we are showing a non-crashed | |
| 195 // TabContents. | |
| 196 // We'll first give the page a chance to process the key events. If it does | |
| 197 // not process them, they'll be returned to us and we'll treat them as | |
| 198 // accelerators then. | |
| 199 return web_contents() && !web_contents()->IsCrashed(); | |
| 200 } | |
| 201 | |
| 202 void WebView::OnFocus() { | |
| 203 if (web_contents()) | |
| 204 web_contents()->Focus(); | |
| 205 } | |
| 206 | |
| 207 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) { | |
| 208 if (web_contents()) | |
| 209 web_contents()->FocusThroughTabTraversal(reverse); | |
| 210 } | |
| 211 | |
| 212 void WebView::GetAccessibleState(ui::AXViewState* state) { | |
| 213 state->role = ui::AX_ROLE_GROUP; | |
| 214 } | |
| 215 | |
| 216 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() { | |
| 217 if (web_contents()) { | |
| 218 content::RenderWidgetHostView* host_view = | |
| 219 web_contents()->GetRenderWidgetHostView(); | |
| 220 if (host_view) | |
| 221 return host_view->GetNativeViewAccessible(); | |
| 222 } | |
| 223 return View::GetNativeViewAccessible(); | |
| 224 } | |
| 225 | |
| 226 gfx::Size WebView::GetPreferredSize() const { | |
| 227 if (preferred_size_ == gfx::Size()) | |
| 228 return View::GetPreferredSize(); | |
| 229 else | |
| 230 return preferred_size_; | |
| 231 } | |
| 232 | |
| 233 //////////////////////////////////////////////////////////////////////////////// | |
| 234 // WebView, content::WebContentsDelegate implementation: | |
| 235 | |
| 236 void WebView::WebContentsFocused(content::WebContents* web_contents) { | |
| 237 DCHECK(wc_owner_.get()); | |
| 238 // The WebView is only the delegate of WebContentses it creates itself. | |
| 239 OnWebContentsFocused(wc_owner_.get()); | |
| 240 } | |
| 241 | |
| 242 bool WebView::EmbedsFullscreenWidget() const { | |
| 243 DCHECK(wc_owner_.get()); | |
| 244 return embed_fullscreen_widget_mode_enabled_; | |
| 245 } | |
| 246 | |
| 247 //////////////////////////////////////////////////////////////////////////////// | |
| 248 // WebView, content::WebContentsObserver implementation: | |
| 249 | |
| 250 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) { | |
| 251 NotifyMaybeTextInputClientChanged(); | |
| 252 } | |
| 253 | |
| 254 void WebView::RenderProcessGone(base::TerminationStatus status) { | |
| 255 NotifyMaybeTextInputClientChanged(); | |
| 256 } | |
| 257 | |
| 258 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host, | |
| 259 content::RenderViewHost* new_host) { | |
| 260 FocusManager* const focus_manager = GetFocusManager(); | |
| 261 if (focus_manager && focus_manager->GetFocusedView() == this) | |
| 262 OnFocus(); | |
| 263 NotifyMaybeTextInputClientChanged(); | |
| 264 } | |
| 265 | |
| 266 void WebView::DidShowFullscreenWidget(int routing_id) { | |
| 267 if (embed_fullscreen_widget_mode_enabled_) | |
| 268 ReattachForFullscreenChange(true); | |
| 269 } | |
| 270 | |
| 271 void WebView::DidDestroyFullscreenWidget(int routing_id) { | |
| 272 if (embed_fullscreen_widget_mode_enabled_) | |
| 273 ReattachForFullscreenChange(false); | |
| 274 } | |
| 275 | |
| 276 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) { | |
| 277 if (embed_fullscreen_widget_mode_enabled_) | |
| 278 ReattachForFullscreenChange(entered_fullscreen); | |
| 279 } | |
| 280 | |
| 281 void WebView::DidAttachInterstitialPage() { | |
| 282 NotifyMaybeTextInputClientChanged(); | |
| 283 } | |
| 284 | |
| 285 void WebView::DidDetachInterstitialPage() { | |
| 286 NotifyMaybeTextInputClientChanged(); | |
| 287 } | |
| 288 | |
| 289 //////////////////////////////////////////////////////////////////////////////// | |
| 290 // WebView, private: | |
| 291 | |
| 292 void WebView::AttachWebContents() { | |
| 293 // Prevents attachment if the WebView isn't already in a Widget, or it's | |
| 294 // already attached. | |
| 295 if (!GetWidget() || !web_contents()) | |
| 296 return; | |
| 297 | |
| 298 const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ? | |
| 299 web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() : | |
| 300 web_contents()->GetNativeView(); | |
| 301 OnBoundsChanged(bounds()); | |
| 302 if (holder_->native_view() == view_to_attach) | |
| 303 return; | |
| 304 | |
| 305 holder_->Attach(view_to_attach); | |
| 306 | |
| 307 // The view will not be focused automatically when it is attached, so we need | |
| 308 // to pass on focus to it if the FocusManager thinks the view is focused. Note | |
| 309 // that not every Widget has a focus manager. | |
| 310 FocusManager* const focus_manager = GetFocusManager(); | |
| 311 if (focus_manager && focus_manager->GetFocusedView() == this) | |
| 312 OnFocus(); | |
| 313 | |
| 314 #if defined(OS_WIN) | |
| 315 if (!is_embedding_fullscreen_widget_) { | |
| 316 web_contents()->SetParentNativeViewAccessible( | |
| 317 parent()->GetNativeViewAccessible()); | |
| 318 } | |
| 319 #endif | |
| 320 } | |
| 321 | |
| 322 void WebView::DetachWebContents() { | |
| 323 if (web_contents()) { | |
| 324 holder_->Detach(); | |
| 325 #if defined(OS_WIN) | |
| 326 if (!is_embedding_fullscreen_widget_) | |
| 327 web_contents()->SetParentNativeViewAccessible(NULL); | |
| 328 #endif | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) { | |
| 333 DCHECK(embed_fullscreen_widget_mode_enabled_); | |
| 334 const bool web_contents_has_separate_fs_widget = | |
| 335 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView(); | |
| 336 if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) { | |
| 337 // Shutting down or starting up the embedding of the separate fullscreen | |
| 338 // widget. Need to detach and re-attach to a different native view. | |
| 339 DetachWebContents(); | |
| 340 is_embedding_fullscreen_widget_ = | |
| 341 enter_fullscreen && web_contents_has_separate_fs_widget; | |
| 342 AttachWebContents(); | |
| 343 } else { | |
| 344 // Entering or exiting "non-Flash" fullscreen mode, where the native view is | |
| 345 // the same. So, do not change attachment. | |
| 346 OnBoundsChanged(bounds()); | |
| 347 } | |
| 348 NotifyMaybeTextInputClientChanged(); | |
| 349 } | |
| 350 | |
| 351 void WebView::NotifyMaybeTextInputClientChanged() { | |
| 352 // Update the TextInputClient as needed; see GetTextInputClient(). | |
| 353 FocusManager* const focus_manager = GetFocusManager(); | |
| 354 if (focus_manager) | |
| 355 focus_manager->OnTextInputClientChanged(this); | |
| 356 } | |
| 357 | |
| 358 content::WebContents* WebView::CreateWebContents( | |
| 359 content::BrowserContext* browser_context) { | |
| 360 content::WebContents* contents = NULL; | |
| 361 if (ViewsDelegate::views_delegate) { | |
| 362 contents = ViewsDelegate::views_delegate->CreateWebContents( | |
| 363 browser_context, NULL); | |
| 364 } | |
| 365 | |
| 366 if (!contents) { | |
| 367 content::WebContents::CreateParams create_params( | |
| 368 browser_context, NULL); | |
| 369 return content::WebContents::Create(create_params); | |
| 370 } | |
| 371 | |
| 372 return contents; | |
| 373 } | |
| 374 | |
| 375 } // namespace views | |
| OLD | NEW |