1// Copyright (c) 2011 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 "chrome/browser/notifications/balloon_host.h"
6#include "chrome/browser/extensions/extension_process_manager.h"
7#include "chrome/browser/extensions/extension_service.h"
8#include "chrome/browser/notifications/balloon.h"
9#include "chrome/browser/notifications/notification.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/renderer_preferences_util.h"
12#include "chrome/browser/ui/browser_list.h"
13#include "chrome/browser/ui/webui/chrome_web_ui_factory.h"
14#include "chrome/common/render_messages.h"
15#include "chrome/common/url_constants.h"
16#include "content/browser/renderer_host/browser_render_process_host.h"
17#include "content/browser/renderer_host/render_view_host.h"
18#include "content/browser/site_instance.h"
19#include "content/common/bindings_policy.h"
20#include "content/common/notification_service.h"
21#include "content/common/notification_source.h"
22#include "content/common/notification_type.h"
23#include "content/common/renderer_preferences.h"
24#include "content/common/view_messages.h"
25#include "webkit/glue/webpreferences.h"
26
27BalloonHost::BalloonHost(Balloon* balloon)
28    : render_view_host_(NULL),
29      balloon_(balloon),
30      initialized_(false),
31      should_notify_on_disconnect_(false),
32      enable_web_ui_(false) {
33  DCHECK(balloon_);
34
35  // If the notification is for an extension URL, make sure to use the extension
36  // process to render it, so that it can communicate with other views in the
37  // extension.
38  const GURL& balloon_url = balloon_->notification().content_url();
39  if (balloon_url.SchemeIs(chrome::kExtensionScheme)) {
40    site_instance_ =
41      balloon_->profile()->GetExtensionProcessManager()->GetSiteInstanceForURL(
42          balloon_url);
43  } else {
44    site_instance_ = SiteInstance::CreateSiteInstance(balloon_->profile());
45  }
46}
47
48void BalloonHost::Shutdown() {
49  NotifyDisconnect();
50  if (render_view_host_) {
51    render_view_host_->Shutdown();
52    render_view_host_ = NULL;
53  }
54}
55
56Browser* BalloonHost::GetBrowser() {
57  // Notifications aren't associated with a particular browser.
58  return NULL;
59}
60
61gfx::NativeView BalloonHost::GetNativeViewOfHost() {
62  // TODO(aa): Should this return the native view of the BalloonView*?
63  return NULL;
64}
65
66TabContents* BalloonHost::associated_tab_contents() const { return NULL; }
67
68const string16& BalloonHost::GetSource() const {
69  return balloon_->notification().display_source();
70}
71
72WebPreferences BalloonHost::GetWebkitPrefs() {
73  WebPreferences web_prefs =
74      RenderViewHostDelegateHelper::GetWebkitPrefs(GetProfile(),
75                                                   enable_web_ui_);
76  web_prefs.allow_scripts_to_close_windows = true;
77  return web_prefs;
78}
79
80SiteInstance* BalloonHost::GetSiteInstance() const {
81  return site_instance_.get();
82}
83
84Profile* BalloonHost::GetProfile() const {
85  return balloon_->profile();
86}
87
88const GURL& BalloonHost::GetURL() const {
89  return balloon_->notification().content_url();
90}
91
92void BalloonHost::Close(RenderViewHost* render_view_host) {
93  balloon_->CloseByScript();
94  NotifyDisconnect();
95}
96
97void BalloonHost::RenderViewCreated(RenderViewHost* render_view_host) {
98  render_view_host->Send(new ViewMsg_DisableScrollbarsForSmallWindows(
99      render_view_host->routing_id(), balloon_->min_scrollbar_size()));
100  render_view_host->WasResized();
101#if !defined(OS_MACOSX)
102  // TODO(levin): Make all of the code that went in originally with this change
103  // to be cross-platform. See http://crbug.com/64720
104  render_view_host->EnablePreferredSizeChangedMode(
105      kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow);
106#endif
107}
108
109void BalloonHost::RenderViewReady(RenderViewHost* render_view_host) {
110  should_notify_on_disconnect_ = true;
111  NotificationService::current()->Notify(
112      NotificationType::NOTIFY_BALLOON_CONNECTED,
113      Source<BalloonHost>(this), NotificationService::NoDetails());
114}
115
116void BalloonHost::RenderViewGone(RenderViewHost* render_view_host,
117                                 base::TerminationStatus status,
118                                 int error_code) {
119  Close(render_view_host);
120}
121
122int BalloonHost::GetBrowserWindowID() const {
123  return extension_misc::kUnknownWindowId;
124}
125
126ViewType::Type BalloonHost::GetRenderViewType() const {
127  return ViewType::NOTIFICATION;
128}
129
130RenderViewHostDelegate::View* BalloonHost::GetViewDelegate() {
131  return this;
132}
133
134void BalloonHost::ProcessWebUIMessage(
135    const ExtensionHostMsg_DomMessage_Params& params) {
136  if (extension_function_dispatcher_.get()) {
137    extension_function_dispatcher_->HandleRequest(params);
138  }
139}
140
141// RenderViewHostDelegate::View methods implemented to allow links to
142// open pages in new tabs.
143void BalloonHost::CreateNewWindow(
144    int route_id,
145    const ViewHostMsg_CreateWindow_Params& params) {
146  delegate_view_helper_.CreateNewWindow(
147      route_id,
148      balloon_->profile(),
149      site_instance_.get(),
150      ChromeWebUIFactory::GetInstance()->GetWebUIType(balloon_->profile(),
151          balloon_->notification().content_url()),
152      this,
153      params.window_container_type,
154      params.frame_name);
155}
156
157void BalloonHost::ShowCreatedWindow(int route_id,
158                                    WindowOpenDisposition disposition,
159                                    const gfx::Rect& initial_pos,
160                                    bool user_gesture) {
161  // Don't allow pop-ups from notifications.
162  if (disposition == NEW_POPUP)
163    return;
164
165  TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id);
166  if (!contents)
167    return;
168  Browser* browser = BrowserList::GetLastActiveWithProfile(balloon_->profile());
169  if (!browser)
170    return;
171
172  browser->AddTabContents(contents, disposition, initial_pos, user_gesture);
173}
174
175bool BalloonHost::PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
176                                         bool* is_keyboard_shortcut) {
177  return false;
178}
179
180void BalloonHost::UpdatePreferredSize(const gfx::Size& new_size) {
181  balloon_->SetContentPreferredSize(new_size);
182}
183
184void BalloonHost::HandleMouseDown() {
185  balloon_->OnClick();
186}
187
188RendererPreferences BalloonHost::GetRendererPrefs(Profile* profile) const {
189  RendererPreferences preferences;
190  renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile);
191  return preferences;
192}
193
194void BalloonHost::Init() {
195  DCHECK(!render_view_host_) << "BalloonViewHost already initialized.";
196  RenderViewHost* rvh = new RenderViewHost(
197      site_instance_.get(), this, MSG_ROUTING_NONE, NULL);
198  if (GetProfile()->GetExtensionService()) {
199    extension_function_dispatcher_.reset(
200        ExtensionFunctionDispatcher::Create(
201            rvh, this, balloon_->notification().content_url()));
202  }
203  if (extension_function_dispatcher_.get()) {
204    rvh->AllowBindings(BindingsPolicy::EXTENSION);
205    rvh->set_is_extension_process(true);
206    const Extension* installed_app =
207        GetProfile()->GetExtensionService()->GetInstalledApp(
208            balloon_->notification().content_url());
209    static_cast<BrowserRenderProcessHost*>(rvh->process())->set_installed_app(
210        installed_app);
211  } else if (enable_web_ui_) {
212    rvh->AllowBindings(BindingsPolicy::WEB_UI);
213  }
214
215  // Do platform-specific initialization.
216  render_view_host_ = rvh;
217  InitRenderWidgetHostView();
218  DCHECK(render_widget_host_view());
219
220  rvh->set_view(render_widget_host_view());
221  rvh->CreateRenderView(string16());
222#if defined(OS_MACOSX)
223  registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DID_PAINT,
224      Source<RenderWidgetHost>(render_view_host_));
225#endif
226  rvh->NavigateToURL(balloon_->notification().content_url());
227
228  initialized_ = true;
229}
230
231void BalloonHost::EnableWebUI() {
232  DCHECK(render_view_host_ == NULL) <<
233      "EnableWebUI has to be called before a renderer is created.";
234  enable_web_ui_ = true;
235}
236
237void BalloonHost::UpdateInspectorSetting(const std::string& key,
238                                         const std::string& value) {
239  RenderViewHostDelegateHelper::UpdateInspectorSetting(
240      GetProfile(), key, value);
241}
242
243void BalloonHost::ClearInspectorSettings() {
244  RenderViewHostDelegateHelper::ClearInspectorSettings(GetProfile());
245}
246
247void BalloonHost::Observe(NotificationType type,
248    const NotificationSource& source,
249    const NotificationDetails& details) {
250  if (type == NotificationType::RENDER_WIDGET_HOST_DID_PAINT) {
251    registrar_.RemoveAll();
252    render_view_host_->EnablePreferredSizeChangedMode(
253        kPreferredSizeWidth | kPreferredSizeHeightThisIsSlow);
254  }
255}
256
257BalloonHost::~BalloonHost() {
258  DCHECK(!render_view_host_);
259}
260
261void BalloonHost::NotifyDisconnect() {
262  if (!should_notify_on_disconnect_)
263    return;
264
265  should_notify_on_disconnect_ = false;
266  NotificationService::current()->Notify(
267      NotificationType::NOTIFY_BALLOON_DISCONNECTED,
268      Source<BalloonHost>(this), NotificationService::NoDetails());
269}
270