// Copyright (c) 2011 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 "chrome/browser/notifications/balloon_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/notifications/balloon.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/webui/chrome_web_ui_factory.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/browser_render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/site_instance.h"
#include "content/common/bindings_policy.h"
#include "content/common/notification_service.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
#include "content/common/renderer_preferences.h"
#include "content/common/view_messages.h"
#include "webkit/glue/webpreferences.h"
BalloonHost::BalloonHost(Balloon* balloon)
: render_view_host_(NULL),
balloon_(balloon),
initialized_(false),
should_notify_on_disconnect_(false),
enable_web_ui_(false) {
DCHECK(balloon_);
// If the notification is for an extension URL, make sure to use the extension
// process to render it, so that it can communicate with other views in the
// extension.
const GURL& balloon_url = balloon_->notification().content_url();
if (balloon_url.SchemeIs(chrome::kExtensionScheme)) {
site_instance_ =
balloon_->profile()->GetExtensionProcessManager()->GetSiteInstanceForURL(
balloon_url);
} else {
site_instance_ = SiteInstance::CreateSiteInstance(balloon_->profile());
}
}
void BalloonHost::Shutdown() {
NotifyDisconnect();
if (render_view_host_) {
render_view_host_->Shutdown();
render_view_host_ = NULL;
}
}
Browser* BalloonHost::GetBrowser() {
// Notifications aren't associated with a particular browser.
return NULL;
}
gfx::NativeView BalloonHost::GetNativeViewOfHost() {
// TODO(aa): Should this return the native view of the BalloonView*?
return NULL;
}
TabContents* BalloonHost::associated_tab_contents() const { return NULL; }
const string16& BalloonHost::GetSource() const {
return balloon_->notification().display_source();
}
WebPreferences BalloonHost::GetWebkitPrefs() {
WebPreferences web_prefs =
RenderViewHostDelegateHelper::GetWebkitPrefs(GetProfile(),
enable_web_ui_);
web_prefs.allow_scripts_to_close_windows = true;
return web_prefs;
}
SiteInstance* BalloonHost::GetSiteInstance() const {
return site_instance_.get();
}
Profile* BalloonHost::GetProfile() const {
return balloon_->profile();
}
const GURL& BalloonHost::GetURL() const {
return balloon_->notification().content_url();
}
void BalloonHost::Close(RenderViewHost* render_view_host) {
balloon_->CloseByScript();
NotifyDisconnect();
}
void BalloonHost::RenderViewCreated(RenderViewHost* render_view_host) {
render_view_host->Send(new ViewMsg_DisableScrollbarsForSmallWindows(
render_view_host->routing_id(), balloon_->min_scrollbar_size()));
render_view_host->WasResized();
#if !defined(OS_MACOSX)
// TODO(levin): Make all of the code that went in originally with this change
// to be cross-platform. See http://crbug.com/64720
render_view_host->EnablePreferredSizeChangedMode(
kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow);
#endif
}
void BalloonHost::RenderViewReady(RenderViewHost* render_view_host) {
should_notify_on_disconnect_ = true;
NotificationService::current()->Notify(
NotificationType::NOTIFY_BALLOON_CONNECTED,
Source<BalloonHost>(this), NotificationService::NoDetails());
}
void BalloonHost::RenderViewGone(RenderViewHost* render_view_host,
base::TerminationStatus status,
int error_code) {
Close(render_view_host);
}
int BalloonHost::GetBrowserWindowID() const {
return extension_misc::kUnknownWindowId;
}
ViewType::Type BalloonHost::GetRenderViewType() const {
return ViewType::NOTIFICATION;
}
RenderViewHostDelegate::View* BalloonHost::GetViewDelegate() {
return this;
}
void BalloonHost::ProcessWebUIMessage(
const ExtensionHostMsg_DomMessage_Params& params) {
if (extension_function_dispatcher_.get()) {
extension_function_dispatcher_->HandleRequest(params);
}
}
// RenderViewHostDelegate::View methods implemented to allow links to
// open pages in new tabs.
void BalloonHost::CreateNewWindow(
int route_id,
const ViewHostMsg_CreateWindow_Params& params) {
delegate_view_helper_.CreateNewWindow(
route_id,
balloon_->profile(),
site_instance_.get(),
ChromeWebUIFactory::GetInstance()->GetWebUIType(balloon_->profile(),
balloon_->notification().content_url()),
this,
params.window_container_type,
params.frame_name);
}
void BalloonHost::ShowCreatedWindow(int route_id,
WindowOpenDisposition disposition,
const gfx::Rect& initial_pos,
bool user_gesture) {
// Don't allow pop-ups from notifications.
if (disposition == NEW_POPUP)
return;
TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id);
if (!contents)
return;
Browser* browser = BrowserList::GetLastActiveWithProfile(balloon_->profile());
if (!browser)
return;
browser->AddTabContents(contents, disposition, initial_pos, user_gesture);
}
bool BalloonHost::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
bool* is_keyboard_shortcut) {
return false;
}
void BalloonHost::UpdatePreferredSize(const gfx::Size& new_size) {
balloon_->SetContentPreferredSize(new_size);
}
void BalloonHost::HandleMouseDown() {
balloon_->OnClick();
}
RendererPreferences BalloonHost::GetRendererPrefs(Profile* profile) const {
RendererPreferences preferences;
renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile);
return preferences;
}
void BalloonHost::Init() {
DCHECK(!render_view_host_) << "BalloonViewHost already initialized.";
RenderViewHost* rvh = new RenderViewHost(
site_instance_.get(), this, MSG_ROUTING_NONE, NULL);
if (GetProfile()->GetExtensionService()) {
extension_function_dispatcher_.reset(
ExtensionFunctionDispatcher::Create(
rvh, this, balloon_->notification().content_url()));
}
if (extension_function_dispatcher_.get()) {
rvh->AllowBindings(BindingsPolicy::EXTENSION);
rvh->set_is_extension_process(true);
const Extension* installed_app =
GetProfile()->GetExtensionService()->GetInstalledApp(
balloon_->notification().content_url());
static_cast<BrowserRenderProcessHost*>(rvh->process())->set_installed_app(
installed_app);
} else if (enable_web_ui_) {
rvh->AllowBindings(BindingsPolicy::WEB_UI);
}
// Do platform-specific initialization.
render_view_host_ = rvh;
InitRenderWidgetHostView();
DCHECK(render_widget_host_view());
rvh->set_view(render_widget_host_view());
rvh->CreateRenderView(string16());
#if defined(OS_MACOSX)
registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DID_PAINT,
Source<RenderWidgetHost>(render_view_host_));
#endif
rvh->NavigateToURL(balloon_->notification().content_url());
initialized_ = true;
}
void BalloonHost::EnableWebUI() {
DCHECK(render_view_host_ == NULL) <<
"EnableWebUI has to be called before a renderer is created.";
enable_web_ui_ = true;
}
void BalloonHost::UpdateInspectorSetting(const std::string& key,
const std::string& value) {
RenderViewHostDelegateHelper::UpdateInspectorSetting(
GetProfile(), key, value);
}
void BalloonHost::ClearInspectorSettings() {
RenderViewHostDelegateHelper::ClearInspectorSettings(GetProfile());
}
void BalloonHost::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::RENDER_WIDGET_HOST_DID_PAINT) {
registrar_.RemoveAll();
render_view_host_->EnablePreferredSizeChangedMode(
kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow);
}
}
BalloonHost::~BalloonHost() {
DCHECK(!render_view_host_);
}
void BalloonHost::NotifyDisconnect() {
if (!should_notify_on_disconnect_)
return;
should_notify_on_disconnect_ = false;
NotificationService::current()->Notify(
NotificationType::NOTIFY_BALLOON_DISCONNECTED,
Source<BalloonHost>(this), NotificationService::NoDetails());
}