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/tab_contents/background_contents.h"
6
7#include "chrome/browser/background_contents_service.h"
8#include "chrome/browser/extensions/extension_message_service.h"
9#include "chrome/browser/profiles/profile.h"
10#include "chrome/browser/renderer_preferences_util.h"
11#include "chrome/browser/ui/webui/chrome_web_ui_factory.h"
12#include "chrome/common/extensions/extension_constants.h"
13#include "chrome/common/extensions/extension_messages.h"
14#include "chrome/common/url_constants.h"
15#include "chrome/common/view_types.h"
16#include "content/browser/browsing_instance.h"
17#include "content/browser/renderer_host/render_view_host.h"
18#include "content/browser/site_instance.h"
19#include "content/common/notification_service.h"
20#include "content/common/view_messages.h"
21#include "ui/gfx/rect.h"
22
23////////////////
24// BackgroundContents
25
26BackgroundContents::BackgroundContents(SiteInstance* site_instance,
27                                       int routing_id,
28                                       Delegate* delegate)
29    : delegate_(delegate) {
30  Profile* profile = site_instance->browsing_instance()->profile();
31
32  // TODO(rafaelw): Implement correct session storage.
33  render_view_host_ = new RenderViewHost(site_instance, this, routing_id, NULL);
34
35  // Close ourselves when the application is shutting down.
36  registrar_.Add(this, NotificationType::APP_TERMINATING,
37                 NotificationService::AllSources());
38
39  // Register for our parent profile to shutdown, so we can shut ourselves down
40  // as well (should only be called for OTR profiles, as we should receive
41  // APP_TERMINATING before non-OTR profiles are destroyed).
42  registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
43                 Source<Profile>(profile));
44}
45
46// Exposed to allow creating mocks.
47BackgroundContents::BackgroundContents()
48    : delegate_(NULL),
49      render_view_host_(NULL) {
50}
51
52BackgroundContents::~BackgroundContents() {
53  if (!render_view_host_)   // Will be null for unit tests.
54    return;
55  Profile* profile = render_view_host_->process()->profile();
56  NotificationService::current()->Notify(
57      NotificationType::BACKGROUND_CONTENTS_DELETED,
58      Source<Profile>(profile),
59      Details<BackgroundContents>(this));
60  render_view_host_->Shutdown();  // deletes render_view_host
61}
62
63BackgroundContents* BackgroundContents::GetAsBackgroundContents() {
64  return this;
65}
66
67RenderViewHostDelegate::View* BackgroundContents::GetViewDelegate() {
68  return this;
69}
70
71const GURL& BackgroundContents::GetURL() const {
72  return url_;
73}
74
75ViewType::Type BackgroundContents::GetRenderViewType() const {
76  return ViewType::BACKGROUND_CONTENTS;
77}
78
79int BackgroundContents::GetBrowserWindowID() const {
80  return extension_misc::kUnknownWindowId;
81}
82
83void BackgroundContents::DidNavigate(
84    RenderViewHost* render_view_host,
85    const ViewHostMsg_FrameNavigate_Params& params) {
86  // We only care when the outer frame changes.
87  if (!PageTransition::IsMainFrame(params.transition))
88    return;
89
90  // Note: because BackgroundContents are only available to extension apps,
91  // navigation is limited to urls within the app's extent. This is enforced in
92  // RenderView::decidePolicyForNaviation. If BackgroundContents become
93  // available as a part of the web platform, it probably makes sense to have
94  // some way to scope navigation of a background page to its opener's security
95  // origin. Note: if the first navigation is to a URL outside the app's
96  // extent a background page will be opened but will remain at about:blank.
97  url_ = params.url;
98
99  Profile* profile = render_view_host->process()->profile();
100  NotificationService::current()->Notify(
101      NotificationType::BACKGROUND_CONTENTS_NAVIGATED,
102      Source<Profile>(profile),
103      Details<BackgroundContents>(this));
104}
105
106void BackgroundContents::RunJavaScriptMessage(
107    const std::wstring& message,
108    const std::wstring& default_prompt,
109    const GURL& frame_url,
110    const int flags,
111    IPC::Message* reply_msg,
112    bool* did_suppress_message) {
113  // TODO(rafaelw): Implement, The JavaScriptModalDialog needs to learn about
114  // BackgroundContents.
115  *did_suppress_message = true;
116}
117
118bool BackgroundContents::PreHandleKeyboardEvent(
119    const NativeWebKeyboardEvent& event,
120    bool* is_keyboard_shortcut) {
121  return false;
122}
123
124void BackgroundContents::Observe(NotificationType type,
125                                 const NotificationSource& source,
126                                 const NotificationDetails& details) {
127  // TODO(rafaelw): Implement pagegroup ref-counting so that non-persistent
128  // background pages are closed when the last referencing frame is closed.
129  switch (type.value) {
130    case NotificationType::PROFILE_DESTROYED:
131    case NotificationType::APP_TERMINATING: {
132      delete this;
133      break;
134    }
135    default:
136      NOTREACHED() << "Unexpected notification sent.";
137      break;
138  }
139}
140
141void BackgroundContents::OnMessageBoxClosed(IPC::Message* reply_msg,
142                                            bool success,
143                                            const std::wstring& prompt) {
144  render_view_host_->JavaScriptMessageBoxClosed(reply_msg, success, prompt);
145}
146
147gfx::NativeWindow BackgroundContents::GetMessageBoxRootWindow() {
148  NOTIMPLEMENTED();
149  return NULL;
150}
151
152TabContents* BackgroundContents::AsTabContents() {
153  return NULL;
154}
155
156ExtensionHost* BackgroundContents::AsExtensionHost() {
157  return NULL;
158}
159
160void BackgroundContents::UpdateInspectorSetting(const std::string& key,
161                                         const std::string& value) {
162  Profile* profile = render_view_host_->process()->profile();
163  RenderViewHostDelegateHelper::UpdateInspectorSetting(profile, key, value);
164}
165
166void BackgroundContents::ClearInspectorSettings() {
167  Profile* profile = render_view_host_->process()->profile();
168  RenderViewHostDelegateHelper::ClearInspectorSettings(profile);
169}
170
171void BackgroundContents::Close(RenderViewHost* render_view_host) {
172  Profile* profile = render_view_host->process()->profile();
173  NotificationService::current()->Notify(
174      NotificationType::BACKGROUND_CONTENTS_CLOSED,
175      Source<Profile>(profile),
176      Details<BackgroundContents>(this));
177  delete this;
178}
179
180void BackgroundContents::RenderViewGone(RenderViewHost* rvh,
181                                        base::TerminationStatus status,
182                                        int error_code) {
183  Profile* profile = rvh->process()->profile();
184  NotificationService::current()->Notify(
185      NotificationType::BACKGROUND_CONTENTS_TERMINATED,
186      Source<Profile>(profile),
187      Details<BackgroundContents>(this));
188
189  // Our RenderView went away, so we should go away also, so killing the process
190  // via the TaskManager doesn't permanently leave a BackgroundContents hanging
191  // around the system, blocking future instances from being created
192  // (http://crbug.com/65189).
193  delete this;
194}
195
196RendererPreferences BackgroundContents::GetRendererPrefs(
197    Profile* profile) const {
198  RendererPreferences preferences;
199  renderer_preferences_util::UpdateFromSystemSettings(&preferences, profile);
200  return preferences;
201}
202
203WebPreferences BackgroundContents::GetWebkitPrefs() {
204  // TODO(rafaelw): Consider enabling the webkit_prefs.dom_paste_enabled for
205  // apps.
206  Profile* profile = render_view_host_->process()->profile();
207  return RenderViewHostDelegateHelper::GetWebkitPrefs(profile,
208                                                      false);  // is_web_ui
209}
210
211void BackgroundContents::ProcessWebUIMessage(
212    const ExtensionHostMsg_DomMessage_Params& params) {
213  // TODO(rafaelw): It may make sense for extensions to be able to open
214  // BackgroundContents to chrome-extension://<id> pages. Consider implementing.
215  render_view_host_->Send(new ExtensionMsg_Response(
216      render_view_host_->routing_id(), params.request_id, false,
217      std::string(), "Access to extension API denied."));
218}
219
220void BackgroundContents::CreateNewWindow(
221    int route_id,
222    const ViewHostMsg_CreateWindow_Params& params) {
223  delegate_view_helper_.CreateNewWindow(
224      route_id,
225      render_view_host_->process()->profile(),
226      render_view_host_->site_instance(),
227      ChromeWebUIFactory::GetInstance()->GetWebUIType(
228          render_view_host_->process()->profile(), url_),
229      this,
230      params.window_container_type,
231      params.frame_name);
232}
233
234void BackgroundContents::CreateNewWidget(int route_id,
235                                         WebKit::WebPopupType popup_type) {
236  NOTREACHED();
237}
238
239void BackgroundContents::CreateNewFullscreenWidget(int route_id) {
240  NOTREACHED();
241}
242
243void BackgroundContents::ShowCreatedWindow(int route_id,
244                                           WindowOpenDisposition disposition,
245                                           const gfx::Rect& initial_pos,
246                                           bool user_gesture) {
247  TabContents* contents = delegate_view_helper_.GetCreatedWindow(route_id);
248  if (contents)
249    delegate_->AddTabContents(contents, disposition, initial_pos, user_gesture);
250}
251
252void BackgroundContents::ShowCreatedWidget(int route_id,
253                                           const gfx::Rect& initial_pos) {
254  NOTIMPLEMENTED();
255}
256
257void BackgroundContents::ShowCreatedFullscreenWidget(int route_id) {
258  NOTIMPLEMENTED();
259}
260
261// static
262BackgroundContents*
263BackgroundContents::GetBackgroundContentsByID(int render_process_id,
264                                              int render_view_id) {
265  RenderViewHost* render_view_host =
266      RenderViewHost::FromID(render_process_id, render_view_id);
267  if (!render_view_host)
268    return NULL;
269
270  return render_view_host->delegate()->GetAsBackgroundContents();
271}
272