blob: 9169ac0ecbf66b0d634419bc25f87f95f9e61aa9 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/webview/webview.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "ipc/ipc_message.h"
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/events/event.h"
#include "ui/views/accessibility/native_view_accessibility.h"
#include "ui/views/controls/native/native_view_host.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/views_delegate.h"
namespace views {
// static
const char WebView::kViewClassName[] = "WebView";
////////////////////////////////////////////////////////////////////////////////
// WebView, public:
WebView::WebView(content::BrowserContext* browser_context)
: wcv_holder_(new NativeViewHost),
web_contents_(NULL),
embed_fullscreen_widget_mode_enabled_(false),
is_embedding_fullscreen_widget_(false),
browser_context_(browser_context),
allow_accelerators_(false) {
AddChildView(wcv_holder_);
NativeViewAccessibility::RegisterWebView(this);
}
WebView::~WebView() {
NativeViewAccessibility::UnregisterWebView(this);
}
content::WebContents* WebView::GetWebContents() {
CreateWebContentsWithSiteInstance(NULL);
return web_contents_;
}
void WebView::CreateWebContentsWithSiteInstance(
content::SiteInstance* site_instance) {
if (!web_contents_) {
wc_owner_.reset(CreateWebContents(browser_context_, site_instance));
web_contents_ = wc_owner_.get();
web_contents_->SetDelegate(this);
AttachWebContents();
}
}
void WebView::SetWebContents(content::WebContents* web_contents) {
if (web_contents == web_contents_)
return;
DetachWebContents();
if (wc_owner_ != web_contents)
wc_owner_.reset();
web_contents_ = web_contents;
if (embed_fullscreen_widget_mode_enabled_) {
WebContentsObserver::Observe(web_contents_);
is_embedding_fullscreen_widget_ =
web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
} else {
is_embedding_fullscreen_widget_ = false;
}
AttachWebContents();
}
void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
bool should_be_embedded = enable;
if (!embed_fullscreen_widget_mode_enabled_ && enable) {
DCHECK(!is_embedding_fullscreen_widget_);
embed_fullscreen_widget_mode_enabled_ = true;
WebContentsObserver::Observe(web_contents_);
should_be_embedded =
web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
} else if (embed_fullscreen_widget_mode_enabled_ && !enable) {
embed_fullscreen_widget_mode_enabled_ = false;
WebContentsObserver::Observe(NULL);
}
if (should_be_embedded != is_embedding_fullscreen_widget_)
ReattachForFullscreenChange(should_be_embedded);
}
void WebView::LoadInitialURL(const GURL& url) {
GetWebContents()->GetController().LoadURL(
url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
}
void WebView::SetFastResize(bool fast_resize) {
wcv_holder_->set_fast_resize(fast_resize);
}
void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
FocusManager* focus_manager = GetFocusManager();
if (focus_manager)
focus_manager->SetFocusedView(this);
}
void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
preferred_size_ = preferred_size;
PreferredSizeChanged();
}
////////////////////////////////////////////////////////////////////////////////
// WebView, View overrides:
const char* WebView::GetClassName() const {
return kViewClassName;
}
void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
wcv_holder_->SetSize(bounds().size());
}
void WebView::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
if (details.is_add)
AttachWebContents();
}
bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
if (allow_accelerators_)
return FocusManager::IsTabTraversalKeyEvent(event);
// Don't look-up accelerators or tab-traversal if we are showing a non-crashed
// TabContents.
// We'll first give the page a chance to process the key events. If it does
// not process them, they'll be returned to us and we'll treat them as
// accelerators then.
return web_contents_ && !web_contents_->IsCrashed();
}
bool WebView::IsFocusable() const {
// We need to be focusable when our contents is not a view hierarchy, as
// clicking on the contents needs to focus us.
return !!web_contents_;
}
void WebView::OnFocus() {
if (!web_contents_)
return;
if (is_embedding_fullscreen_widget_) {
content::RenderWidgetHostView* const current_fs_view =
web_contents_->GetFullscreenRenderWidgetHostView();
if (current_fs_view)
current_fs_view->Focus();
} else {
web_contents_->GetView()->Focus();
}
}
void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
if (web_contents_)
web_contents_->FocusThroughTabTraversal(reverse);
}
void WebView::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_GROUPING;
}
gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
if (web_contents_) {
content::RenderWidgetHostView* host_view =
web_contents_->GetRenderWidgetHostView();
if (host_view)
return host_view->GetNativeViewAccessible();
}
return View::GetNativeViewAccessible();
}
gfx::Size WebView::GetPreferredSize() {
if (preferred_size_ == gfx::Size())
return View::GetPreferredSize();
else
return preferred_size_;
}
////////////////////////////////////////////////////////////////////////////////
// WebView, content::NotificationObserver implementation:
void WebView::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type == content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED) {
FocusManager* const focus_manager = GetFocusManager();
if (focus_manager && focus_manager->GetFocusedView() == this)
OnFocus();
} else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
DCHECK(content::Source<content::WebContents>(source).ptr() ==
web_contents_);
SetWebContents(NULL);
} else {
NOTREACHED();
}
}
////////////////////////////////////////////////////////////////////////////////
// WebView, content::WebContentsDelegate implementation:
void WebView::WebContentsFocused(content::WebContents* web_contents) {
DCHECK(wc_owner_.get());
// The WebView is only the delegate of WebContentses it creates itself.
OnWebContentsFocused(web_contents_);
}
bool WebView::EmbedsFullscreenWidget() const {
DCHECK(wc_owner_.get());
return embed_fullscreen_widget_mode_enabled_;
}
////////////////////////////////////////////////////////////////////////////////
// WebView, content::WebContentsObserver implementation:
void WebView::DidShowFullscreenWidget(int routing_id) {
DCHECK(embed_fullscreen_widget_mode_enabled_);
ReattachForFullscreenChange(true);
}
void WebView::DidDestroyFullscreenWidget(int routing_id) {
DCHECK(embed_fullscreen_widget_mode_enabled_);
ReattachForFullscreenChange(false);
}
////////////////////////////////////////////////////////////////////////////////
// WebView, private:
void WebView::AttachWebContents() {
// Prevents attachment if the WebView isn't already in a Widget, or it's
// already attached.
if (!GetWidget() || !web_contents_)
return;
const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
web_contents_->GetFullscreenRenderWidgetHostView()->GetNativeView() :
web_contents_->GetView()->GetNativeView();
if (wcv_holder_->native_view() == view_to_attach)
return;
wcv_holder_->Attach(view_to_attach);
// The view will not be focused automatically when it is attached, so we need
// to pass on focus to it if the FocusManager thinks the view is focused. Note
// that not every Widget has a focus manager.
FocusManager* const focus_manager = GetFocusManager();
if (focus_manager && focus_manager->GetFocusedView() == this)
OnFocus();
registrar_.Add(
this,
content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
content::Source<content::NavigationController>(
&web_contents_->GetController()));
registrar_.Add(
this,
content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::Source<content::WebContents>(web_contents_));
#if defined(OS_WIN) && defined(USE_AURA)
if (!is_embedding_fullscreen_widget_) {
web_contents_->SetParentNativeViewAccessible(
parent()->GetNativeViewAccessible());
}
#endif
}
void WebView::DetachWebContents() {
if (web_contents_) {
wcv_holder_->Detach();
#if defined(OS_WIN)
if (!is_embedding_fullscreen_widget_) {
#if !defined(USE_AURA)
// TODO(beng): This should either not be necessary, or be done implicitly
// by NativeViewHostWin on Detach(). As it stands, this is needed so that
// the of the detached contents knows to tell the renderer it's been
// hidden.
//
// Moving this out of here would also mean we wouldn't be potentially
// calling member functions on a half-destroyed WebContents.
ShowWindow(web_contents_->GetView()->GetNativeView(), SW_HIDE);
#else
web_contents_->SetParentNativeViewAccessible(NULL);
#endif
}
#endif
}
registrar_.RemoveAll();
}
void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
DetachWebContents();
is_embedding_fullscreen_widget_ = enter_fullscreen &&
web_contents_ && web_contents_->GetFullscreenRenderWidgetHostView();
AttachWebContents();
}
content::WebContents* WebView::CreateWebContents(
content::BrowserContext* browser_context,
content::SiteInstance* site_instance) {
content::WebContents* contents = NULL;
if (ViewsDelegate::views_delegate) {
contents = ViewsDelegate::views_delegate->CreateWebContents(
browser_context, site_instance);
}
if (!contents) {
content::WebContents::CreateParams create_params(
browser_context, site_instance);
return content::WebContents::Create(create_params);
}
return contents;
}
} // namespace views