blob: f1308eb8e923e14e7896316d2d1b6e29ae2d5d5b [file] [log] [blame]
[email protected]a03d4448f2012-01-10 23:25:281// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]398206c2010-06-21 01:46:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]a07676b22011-06-17 16:36:535#include "chrome/browser/background/background_contents_service.h"
[email protected]398206c2010-06-21 01:46:086
7#include "base/basictypes.h"
[email protected]1c1977cc2011-10-27 21:44:548#include "base/bind.h"
[email protected]398206c2010-06-21 01:46:089#include "base/command_line.h"
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "base/values.h"
[email protected]a07676b22011-06-17 16:36:5313#include "chrome/browser/background/background_contents_service_factory.h"
[email protected]2c4fb7b2011-04-02 06:33:2914#include "chrome/browser/browser_process.h"
15#include "chrome/browser/extensions/extension_host.h"
[email protected]eaa7dd182010-12-14 11:09:0016#include "chrome/browser/extensions/extension_service.h"
[email protected]2c4fb7b2011-04-02 06:33:2917#include "chrome/browser/notifications/desktop_notification_service.h"
18#include "chrome/browser/notifications/notification.h"
19#include "chrome/browser/notifications/notification_ui_manager.h"
[email protected]37858e52010-08-26 00:22:0220#include "chrome/browser/prefs/pref_service.h"
[email protected]f33bee52011-03-31 09:18:4321#include "chrome/browser/prefs/scoped_user_pref_update.h"
[email protected]8ecad5e2010-12-02 21:18:3322#include "chrome/browser/profiles/profile.h"
[email protected]2ad4a902010-11-17 06:05:1323#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_list.h"
[email protected]432115822011-07-10 15:52:2725#include "chrome/common/chrome_notification_types.h"
[email protected]398206c2010-06-21 01:46:0826#include "chrome/common/chrome_switches.h"
27#include "chrome/common/extensions/extension.h"
[email protected]814a7bf0f2011-08-13 05:30:5928#include "chrome/common/extensions/extension_constants.h"
[email protected]398206c2010-06-21 01:46:0829#include "chrome/common/pref_names.h"
[email protected]5de634712011-03-02 00:20:1930#include "content/browser/site_instance.h"
[email protected]ad50def52011-10-19 23:17:0731#include "content/public/browser/notification_service.h"
[email protected]ef9572e2012-01-04 22:14:1232#include "content/public/browser/web_contents.h"
[email protected]2c4fb7b2011-04-02 06:33:2933#include "grit/generated_resources.h"
34#include "ui/base/l10n/l10n_util.h"
35
[email protected]2a6bc3e2011-12-28 23:51:3336using content::WebContents;
37
[email protected]2c4fb7b2011-04-02 06:33:2938namespace {
39
[email protected]7ec9b3d82011-04-13 17:19:2140const char kNotificationPrefix[] = "app.background.crashed.";
41
[email protected]2c4fb7b2011-04-02 06:33:2942void CloseBalloon(const std::string id) {
43 g_browser_process->notification_ui_manager()->CancelById(id);
44}
45
[email protected]7ec9b3d82011-04-13 17:19:2146void ScheduleCloseBalloon(const std::string& extension_id) {
[email protected]aa4e5ad42011-05-18 04:56:3547 if (!MessageLoop::current()) // For unit_tests
48 return;
[email protected]1c1977cc2011-10-27 21:44:5449 MessageLoop::current()->PostTask(
50 FROM_HERE, base::Bind(&CloseBalloon, kNotificationPrefix + extension_id));
[email protected]7ec9b3d82011-04-13 17:19:2151}
52
[email protected]2c4fb7b2011-04-02 06:33:2953class CrashNotificationDelegate : public NotificationDelegate {
54 public:
55 CrashNotificationDelegate(Profile* profile, const Extension* extension)
56 : profile_(profile),
[email protected]41619a9a2011-04-13 20:07:3257 is_hosted_app_(extension->is_hosted_app()),
[email protected]2c4fb7b2011-04-02 06:33:2958 extension_id_(extension->id()) {
59 }
60
61 ~CrashNotificationDelegate() {
62 }
63
64 void Display() {}
65
66 void Error() {}
67
68 void Close(bool by_user) {}
69
70 void Click() {
[email protected]41619a9a2011-04-13 20:07:3271 if (is_hosted_app_) {
[email protected]aa4e5ad42011-05-18 04:56:3572 // There can be a race here: user clicks the balloon, and simultaneously
73 // reloads the sad tab for the app. So we check here to be safe before
74 // loading the background page.
75 BackgroundContentsService* service =
76 BackgroundContentsServiceFactory::GetForProfile(profile_);
77 if (!service->GetAppBackgroundContents(ASCIIToUTF16(extension_id_)))
78 service->LoadBackgroundContentsForExtension(profile_, extension_id_);
[email protected]2c4fb7b2011-04-02 06:33:2979 } else {
80 profile_->GetExtensionService()->ReloadExtension(extension_id_);
81 }
82
[email protected]7ec9b3d82011-04-13 17:19:2183 // Closing the balloon here should be OK, but it causes a crash on Mac
[email protected]2c4fb7b2011-04-02 06:33:2984 // http://crbug.com/78167
[email protected]7ec9b3d82011-04-13 17:19:2185 ScheduleCloseBalloon(extension_id_);
[email protected]2c4fb7b2011-04-02 06:33:2986 }
87
88 std::string id() const {
[email protected]7ec9b3d82011-04-13 17:19:2189 return kNotificationPrefix + extension_id_;
[email protected]2c4fb7b2011-04-02 06:33:2990 }
91
92 private:
93 Profile* profile_;
[email protected]41619a9a2011-04-13 20:07:3294 bool is_hosted_app_;
[email protected]2c4fb7b2011-04-02 06:33:2995 std::string extension_id_;
96
97 DISALLOW_COPY_AND_ASSIGN(CrashNotificationDelegate);
98};
99
100void ShowBalloon(const Extension* extension, Profile* profile) {
101 string16 message = l10n_util::GetStringFUTF16(
[email protected]b3f7fe22011-11-11 19:27:56102 extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
[email protected]2c4fb7b2011-04-02 06:33:29103 IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE,
104 UTF8ToUTF16(extension->name()));
105 string16 content_url = DesktopNotificationService::CreateDataUrl(
106 extension->GetIconURL(Extension::EXTENSION_ICON_SMALLISH,
107 ExtensionIconSet::MATCH_BIGGER),
108 string16(), message, WebKit::WebTextDirectionDefault);
109 Notification notification(
110 extension->url(), GURL(content_url), string16(), string16(),
111 new CrashNotificationDelegate(profile, extension));
112 g_browser_process->notification_ui_manager()->Add(notification, profile);
113}
114
115}
[email protected]398206c2010-06-21 01:46:08116
117// Keys for the information we store about individual BackgroundContents in
118// prefs. There is one top-level DictionaryValue (stored at
119// prefs::kRegisteredBackgroundContents). Information about each
120// BackgroundContents is stored under that top-level DictionaryValue, keyed
121// by the parent application ID for easy lookup.
122//
123// kRegisteredBackgroundContents:
124// DictionaryValue {
125// <appid_1>: { "url": <url1>, "name": <frame_name> },
126// <appid_2>: { "url": <url2>, "name": <frame_name> },
127// ... etc ...
128// }
[email protected]ff4c1d82010-08-04 16:58:12129const char kUrlKey[] = "url";
130const char kFrameNameKey[] = "name";
[email protected]398206c2010-06-21 01:46:08131
132BackgroundContentsService::BackgroundContentsService(
133 Profile* profile, const CommandLine* command_line)
[email protected]4c793f02010-08-18 20:55:45134 : prefs_(NULL) {
[email protected]398206c2010-06-21 01:46:08135 // Don't load/store preferences if the proper switch is not enabled, or if
[email protected]2c910b72011-03-08 21:16:32136 // the parent profile is incognito.
[email protected]398206c2010-06-21 01:46:08137 if (!profile->IsOffTheRecord() &&
[email protected]f9a06842010-08-21 00:32:40138 !command_line->HasSwitch(switches::kDisableRestoreBackgroundContents))
[email protected]398206c2010-06-21 01:46:08139 prefs_ = profile->GetPrefs();
140
141 // Listen for events to tell us when to load/unload persisted background
142 // contents.
143 StartObserving(profile);
144}
145
146BackgroundContentsService::~BackgroundContentsService() {
147 // BackgroundContents should be shutdown before we go away, as otherwise
148 // our browser process refcount will be off.
149 DCHECK(contents_map_.empty());
150}
151
[email protected]da58f5b12010-11-10 19:31:12152std::vector<BackgroundContents*>
153BackgroundContentsService::GetBackgroundContents() const
154{
155 std::vector<BackgroundContents*> contents;
156 for (BackgroundContentsMap::const_iterator it = contents_map_.begin();
157 it != contents_map_.end(); ++it)
158 contents.push_back(it->second.contents);
159 return contents;
160}
161
[email protected]398206c2010-06-21 01:46:08162void BackgroundContentsService::StartObserving(Profile* profile) {
163 // On startup, load our background pages after extension-apps have loaded.
[email protected]432115822011-07-10 15:52:27164 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
[email protected]6c2381d2011-10-19 02:52:53165 content::Source<Profile>(profile));
[email protected]398206c2010-06-21 01:46:08166
167 // Track the lifecycle of all BackgroundContents in the system to allow us
168 // to store an up-to-date list of the urls. Start tracking contents when they
[email protected]da58f5b12010-11-10 19:31:12169 // have been opened via CreateBackgroundContents(), and stop tracking them
170 // when they are closed by script.
[email protected]432115822011-07-10 15:52:27171 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED,
[email protected]6c2381d2011-10-19 02:52:53172 content::Source<Profile>(profile));
[email protected]398206c2010-06-21 01:46:08173
174 // Stop tracking BackgroundContents when they have been deleted (happens
175 // during shutdown or if the render process dies).
[email protected]432115822011-07-10 15:52:27176 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
[email protected]6c2381d2011-10-19 02:52:53177 content::Source<Profile>(profile));
[email protected]2c4fb7b2011-04-02 06:33:29178
[email protected]398206c2010-06-21 01:46:08179 // Track when the BackgroundContents navigates to a new URL so we can update
180 // our persisted information as appropriate.
[email protected]432115822011-07-10 15:52:27181 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
[email protected]6c2381d2011-10-19 02:52:53182 content::Source<Profile>(profile));
[email protected]398206c2010-06-21 01:46:08183
[email protected]41619a9a2011-04-13 20:07:32184 // Listen for new extension installs so that we can load any associated
185 // background page.
[email protected]432115822011-07-10 15:52:27186 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
[email protected]6c2381d2011-10-19 02:52:53187 content::Source<Profile>(profile));
[email protected]41619a9a2011-04-13 20:07:32188
[email protected]2c4fb7b2011-04-02 06:33:29189 // Track when the extensions crash so that the user can be notified
190 // about it, and the crashed contents can be restarted.
[email protected]432115822011-07-10 15:52:27191 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED,
[email protected]6c2381d2011-10-19 02:52:53192 content::Source<Profile>(profile));
[email protected]432115822011-07-10 15:52:27193 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED,
[email protected]6c2381d2011-10-19 02:52:53194 content::Source<Profile>(profile));
[email protected]2c4fb7b2011-04-02 06:33:29195
[email protected]398206c2010-06-21 01:46:08196 // Listen for extensions to be unloaded so we can shutdown associated
197 // BackgroundContents.
[email protected]432115822011-07-10 15:52:27198 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
[email protected]6c2381d2011-10-19 02:52:53199 content::Source<Profile>(profile));
[email protected]7ec9b3d82011-04-13 17:19:21200
201 // Make sure the extension-crash balloons are removed when the extension is
202 // uninstalled/reloaded. We cannot do this from UNLOADED since a crashed
203 // extension is unloaded immediately after the crash, not when user reloads or
204 // uninstalls the extension.
[email protected]432115822011-07-10 15:52:27205 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
[email protected]6c2381d2011-10-19 02:52:53206 content::Source<Profile>(profile));
[email protected]398206c2010-06-21 01:46:08207}
208
[email protected]6c2381d2011-10-19 02:52:53209void BackgroundContentsService::Observe(
210 int type,
211 const content::NotificationSource& source,
212 const content::NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27213 switch (type) {
214 case chrome::NOTIFICATION_EXTENSIONS_READY:
[email protected]6c2381d2011-10-19 02:52:53215 LoadBackgroundContentsFromManifests(
216 content::Source<Profile>(source).ptr());
217 LoadBackgroundContentsFromPrefs(content::Source<Profile>(source).ptr());
[email protected]398206c2010-06-21 01:46:08218 break;
[email protected]432115822011-07-10 15:52:27219 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED:
[email protected]6c2381d2011-10-19 02:52:53220 BackgroundContentsShutdown(
221 content::Details<BackgroundContents>(details).ptr());
[email protected]398206c2010-06-21 01:46:08222 break;
[email protected]432115822011-07-10 15:52:27223 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED:
[email protected]6c2381d2011-10-19 02:52:53224 DCHECK(IsTracked(content::Details<BackgroundContents>(details).ptr()));
225 UnregisterBackgroundContents(
226 content::Details<BackgroundContents>(details).ptr());
[email protected]398206c2010-06-21 01:46:08227 break;
[email protected]432115822011-07-10 15:52:27228 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED: {
[email protected]6c2381d2011-10-19 02:52:53229 DCHECK(IsTracked(content::Details<BackgroundContents>(details).ptr()));
[email protected]41619a9a2011-04-13 20:07:32230
231 // Do not register in the pref if the extension has a manifest-specified
232 // background page.
233 BackgroundContents* bgcontents =
[email protected]6c2381d2011-10-19 02:52:53234 content::Details<BackgroundContents>(details).ptr();
235 Profile* profile = content::Source<Profile>(source).ptr();
[email protected]41619a9a2011-04-13 20:07:32236 const string16& appid = GetParentApplicationId(bgcontents);
237 ExtensionService* extension_service = profile->GetExtensionService();
238 // extension_service can be NULL when running tests.
239 if (extension_service) {
240 const Extension* extension =
241 extension_service->GetExtensionById(UTF16ToUTF8(appid), false);
[email protected]a03d4448f2012-01-10 23:25:28242 if (extension && extension->has_background_page())
[email protected]41619a9a2011-04-13 20:07:32243 break;
244 }
[email protected]aa4e5ad42011-05-18 04:56:35245 RegisterBackgroundContents(bgcontents);
[email protected]398206c2010-06-21 01:46:08246 break;
[email protected]41619a9a2011-04-13 20:07:32247 }
[email protected]432115822011-07-10 15:52:27248 case chrome::NOTIFICATION_EXTENSION_LOADED: {
[email protected]6c2381d2011-10-19 02:52:53249 const Extension* extension =
250 content::Details<const Extension>(details).ptr();
251 Profile* profile = content::Source<Profile>(source).ptr();
[email protected]41619a9a2011-04-13 20:07:32252 if (extension->is_hosted_app() &&
[email protected]a03d4448f2012-01-10 23:25:28253 extension->has_background_page()) {
[email protected]41619a9a2011-04-13 20:07:32254 // If there is a background page specified in the manifest for a hosted
255 // app, then blow away registered urls in the pref.
256 ShutdownAssociatedBackgroundContents(ASCIIToUTF16(extension->id()));
[email protected]2c4fb7b2011-04-02 06:33:29257
[email protected]41619a9a2011-04-13 20:07:32258 ExtensionService* service = profile->GetExtensionService();
259 if (service && service->is_ready()) {
260 // Now load the manifest-specified background page. If service isn't
261 // ready, then the background page will be loaded from the
262 // EXTENSIONS_READY callback.
[email protected]a03d4448f2012-01-10 23:25:28263 LoadBackgroundContents(profile, extension->GetBackgroundURL(),
[email protected]41619a9a2011-04-13 20:07:32264 ASCIIToUTF16("background"), UTF8ToUTF16(extension->id()));
265 }
266 }
267
268 // Remove any "This extension has crashed" balloons.
269 ScheduleCloseBalloon(extension->id());
270 break;
271 }
[email protected]432115822011-07-10 15:52:27272 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
273 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: {
[email protected]6c2381d2011-10-19 02:52:53274 Profile* profile = content::Source<Profile>(source).ptr();
[email protected]2c4fb7b2011-04-02 06:33:29275 const Extension* extension = NULL;
[email protected]432115822011-07-10 15:52:27276 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) {
[email protected]2c4fb7b2011-04-02 06:33:29277 BackgroundContents* bg =
[email protected]6c2381d2011-10-19 02:52:53278 content::Details<BackgroundContents>(details).ptr();
[email protected]d2fad142011-04-15 10:18:44279 std::string extension_id = UTF16ToASCII(
280 BackgroundContentsServiceFactory::GetForProfile(profile)->
281 GetParentApplicationId(bg));
[email protected]2c4fb7b2011-04-02 06:33:29282 extension =
283 profile->GetExtensionService()->GetExtensionById(extension_id, false);
284 } else {
[email protected]6c2381d2011-10-19 02:52:53285 ExtensionHost* extension_host =
286 content::Details<ExtensionHost>(details).ptr();
[email protected]2c4fb7b2011-04-02 06:33:29287 extension = extension_host->extension();
288 }
289 if (!extension)
290 break;
291
292 // When an extension crashes, EXTENSION_PROCESS_TERMINATED is followed by
293 // an EXTENSION_UNLOADED notification. This UNLOADED signal causes all the
294 // notifications for this extension to be cancelled by
295 // DesktopNotificationService. For this reason, instead of showing the
296 // balloon right now, we schedule it to show a little later.
[email protected]1c1977cc2011-10-27 21:44:54297 MessageLoop::current()->PostTask(
298 FROM_HERE, base::Bind(&ShowBalloon, extension, profile));
[email protected]2c4fb7b2011-04-02 06:33:29299 break;
300 }
[email protected]432115822011-07-10 15:52:27301 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
[email protected]6c2381d2011-10-19 02:52:53302 switch (content::Details<UnloadedExtensionInfo>(details)->reason) {
[email protected]b3f7fe22011-11-11 19:27:56303 case extension_misc::UNLOAD_REASON_DISABLE: // Fall through.
304 case extension_misc::UNLOAD_REASON_TERMINATE: // Fall through.
[email protected]814a7bf0f2011-08-13 05:30:59305 case extension_misc::UNLOAD_REASON_UNINSTALL:
[email protected]5e8bfe42011-01-18 20:31:13306 ShutdownAssociatedBackgroundContents(
[email protected]6c2381d2011-10-19 02:52:53307 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)->
308 extension->id()));
[email protected]5e8bfe42011-01-18 20:31:13309 break;
[email protected]814a7bf0f2011-08-13 05:30:59310 case extension_misc::UNLOAD_REASON_UPDATE: {
[email protected]41619a9a2011-04-13 20:07:32311 // If there is a manifest specified background page, then shut it down
312 // here, since if the updated extension still has the background page,
313 // then it will be loaded from LOADED callback. Otherwise, leave
314 // BackgroundContents in place.
315 const Extension* extension =
[email protected]6c2381d2011-10-19 02:52:53316 content::Details<UnloadedExtensionInfo>(details)->extension;
[email protected]a03d4448f2012-01-10 23:25:28317 if (extension->has_background_page())
[email protected]41619a9a2011-04-13 20:07:32318 ShutdownAssociatedBackgroundContents(ASCIIToUTF16(extension->id()));
[email protected]5e8bfe42011-01-18 20:31:13319 break;
[email protected]41619a9a2011-04-13 20:07:32320 }
[email protected]5e8bfe42011-01-18 20:31:13321 default:
322 NOTREACHED();
323 ShutdownAssociatedBackgroundContents(
[email protected]6c2381d2011-10-19 02:52:53324 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)->
325 extension->id()));
[email protected]5e8bfe42011-01-18 20:31:13326 break;
327 }
[email protected]398206c2010-06-21 01:46:08328 break;
[email protected]7ec9b3d82011-04-13 17:19:21329
[email protected]432115822011-07-10 15:52:27330 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
[email protected]7ec9b3d82011-04-13 17:19:21331 // Remove any "This extension has crashed" balloons.
[email protected]6c2381d2011-10-19 02:52:53332 ScheduleCloseBalloon(*content::Details<const std::string>(details).ptr());
[email protected]7ec9b3d82011-04-13 17:19:21333 break;
334 }
335
[email protected]398206c2010-06-21 01:46:08336 default:
337 NOTREACHED();
338 break;
339 }
340}
341
342// Loads all background contents whose urls have been stored in prefs.
[email protected]1e0fe2822010-06-24 21:04:19343void BackgroundContentsService::LoadBackgroundContentsFromPrefs(
344 Profile* profile) {
[email protected]398206c2010-06-21 01:46:08345 if (!prefs_)
346 return;
[email protected]398206c2010-06-21 01:46:08347 const DictionaryValue* contents =
348 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
349 if (!contents)
350 return;
[email protected]eaa7dd182010-12-14 11:09:00351 ExtensionService* extensions_service = profile->GetExtensionService();
[email protected]da58f5b12010-11-10 19:31:12352 DCHECK(extensions_service);
[email protected]398206c2010-06-21 01:46:08353 for (DictionaryValue::key_iterator it = contents->begin_keys();
354 it != contents->end_keys(); ++it) {
[email protected]da58f5b12010-11-10 19:31:12355 // Check to make sure that the parent extension is still enabled.
356 const Extension* extension = extensions_service->GetExtensionById(
357 *it, false);
[email protected]da58f5b12010-11-10 19:31:12358 if (!extension) {
359 // We should never reach here - it should not be possible for an app
360 // to become uninstalled without the associated BackgroundContents being
361 // unregistered via the EXTENSIONS_UNLOADED notification, unless there's a
[email protected]6aa25edc12011-10-06 02:16:11362 // crash before we could save our prefs, or if the user deletes the
363 // extension files manually rather than uninstalling it.
[email protected]da58f5b12010-11-10 19:31:12364 NOTREACHED() << "No extension found for BackgroundContents - id = "
365 << *it;
[email protected]6aa25edc12011-10-06 02:16:11366 // Don't cancel out of our loop, just ignore this BackgroundContents and
367 // load the next one.
368 continue;
[email protected]da58f5b12010-11-10 19:31:12369 }
[email protected]2c4fb7b2011-04-02 06:33:29370 LoadBackgroundContentsFromDictionary(profile, *it, contents);
[email protected]398206c2010-06-21 01:46:08371 }
372}
373
[email protected]2c4fb7b2011-04-02 06:33:29374void BackgroundContentsService::LoadBackgroundContentsForExtension(
375 Profile* profile,
376 const std::string& extension_id) {
[email protected]41619a9a2011-04-13 20:07:32377 // First look if the manifest specifies a background page.
378 const Extension* extension =
379 profile->GetExtensionService()->GetExtensionById(extension_id, false);
380 DCHECK(!extension || extension->is_hosted_app());
[email protected]a03d4448f2012-01-10 23:25:28381 if (extension && extension->has_background_page()) {
[email protected]41619a9a2011-04-13 20:07:32382 LoadBackgroundContents(profile,
[email protected]a03d4448f2012-01-10 23:25:28383 extension->GetBackgroundURL(),
[email protected]41619a9a2011-04-13 20:07:32384 ASCIIToUTF16("background"),
385 UTF8ToUTF16(extension->id()));
386 return;
387 }
388
389 // Now look in the prefs.
[email protected]2c4fb7b2011-04-02 06:33:29390 if (!prefs_)
391 return;
392 const DictionaryValue* contents =
393 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
394 if (!contents)
395 return;
396 LoadBackgroundContentsFromDictionary(profile, extension_id, contents);
397}
398
399void BackgroundContentsService::LoadBackgroundContentsFromDictionary(
400 Profile* profile,
401 const std::string& extension_id,
402 const DictionaryValue* contents) {
403 ExtensionService* extensions_service = profile->GetExtensionService();
404 DCHECK(extensions_service);
405
406 DictionaryValue* dict;
[email protected]aa4e5ad42011-05-18 04:56:35407 if (!contents->GetDictionaryWithoutPathExpansion(extension_id, &dict) ||
408 dict == NULL)
[email protected]2c4fb7b2011-04-02 06:33:29409 return;
[email protected]aa4e5ad42011-05-18 04:56:35410
[email protected]2c4fb7b2011-04-02 06:33:29411 string16 frame_name;
412 std::string url;
413 dict->GetString(kUrlKey, &url);
414 dict->GetString(kFrameNameKey, &frame_name);
415 LoadBackgroundContents(profile,
416 GURL(url),
417 frame_name,
418 UTF8ToUTF16(extension_id));
419}
420
[email protected]41619a9a2011-04-13 20:07:32421void BackgroundContentsService::LoadBackgroundContentsFromManifests(
422 Profile* profile) {
[email protected]84df8332011-12-06 18:22:46423 const ExtensionSet* extensions =
[email protected]41619a9a2011-04-13 20:07:32424 profile->GetExtensionService()->extensions();
[email protected]84df8332011-12-06 18:22:46425 ExtensionSet::const_iterator iter = extensions->begin();
[email protected]41619a9a2011-04-13 20:07:32426 for (; iter != extensions->end(); ++iter) {
427 const Extension* extension = *iter;
[email protected]a03d4448f2012-01-10 23:25:28428 if (extension->is_hosted_app() && extension->has_background_page()) {
[email protected]41619a9a2011-04-13 20:07:32429 LoadBackgroundContents(profile,
[email protected]a03d4448f2012-01-10 23:25:28430 extension->GetBackgroundURL(),
[email protected]41619a9a2011-04-13 20:07:32431 ASCIIToUTF16("background"),
432 UTF8ToUTF16(extension->id()));
433 }
434 }
435}
436
[email protected]da58f5b12010-11-10 19:31:12437void BackgroundContentsService::LoadBackgroundContents(
[email protected]1e0fe2822010-06-24 21:04:19438 Profile* profile,
[email protected]398206c2010-06-21 01:46:08439 const GURL& url,
440 const string16& frame_name,
441 const string16& application_id) {
442 // We are depending on the fact that we will initialize before any user
443 // actions or session restore can take place, so no BackgroundContents should
444 // be running yet for the passed application_id.
445 DCHECK(!GetAppBackgroundContents(application_id));
[email protected]1e0fe2822010-06-24 21:04:19446 DCHECK(!application_id.empty());
[email protected]398206c2010-06-21 01:46:08447 DCHECK(url.is_valid());
[email protected]8e96e502010-10-21 20:57:12448 DVLOG(1) << "Loading background content url: " << url;
[email protected]1e0fe2822010-06-24 21:04:19449
[email protected]da58f5b12010-11-10 19:31:12450 BackgroundContents* contents = CreateBackgroundContents(
[email protected]bc093872010-08-16 21:49:08451 SiteInstance::CreateSiteInstanceForURL(profile, url),
452 MSG_ROUTING_NONE,
[email protected]da58f5b12010-11-10 19:31:12453 profile,
454 frame_name,
455 application_id);
[email protected]1e0fe2822010-06-24 21:04:19456
[email protected]1e0fe2822010-06-24 21:04:19457 // TODO(atwilson): Create RenderViews asynchronously to avoid increasing
458 // startup latency (http://crbug.com/47236).
[email protected]4b19ea52012-01-02 20:15:25459 contents->web_contents()->GetController().LoadURL(
[email protected]95dacd02011-12-05 10:35:26460 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
[email protected]398206c2010-06-21 01:46:08461}
462
[email protected]da58f5b12010-11-10 19:31:12463BackgroundContents* BackgroundContentsService::CreateBackgroundContents(
464 SiteInstance* site,
465 int routing_id,
466 Profile* profile,
467 const string16& frame_name,
468 const string16& application_id) {
469 BackgroundContents* contents = new BackgroundContents(site, routing_id, this);
470
471 // Register the BackgroundContents internally, then send out a notification
472 // to external listeners.
473 BackgroundContentsOpenedDetails details = {contents,
474 frame_name,
475 application_id};
476 BackgroundContentsOpened(&details);
[email protected]ad50def52011-10-19 23:17:07477 content::NotificationService::current()->Notify(
[email protected]432115822011-07-10 15:52:27478 chrome::NOTIFICATION_BACKGROUND_CONTENTS_OPENED,
[email protected]6c2381d2011-10-19 02:52:53479 content::Source<Profile>(profile),
480 content::Details<BackgroundContentsOpenedDetails>(&details));
[email protected]da58f5b12010-11-10 19:31:12481 return contents;
482}
483
[email protected]398206c2010-06-21 01:46:08484void BackgroundContentsService::RegisterBackgroundContents(
485 BackgroundContents* background_contents) {
486 DCHECK(IsTracked(background_contents));
487 if (!prefs_)
488 return;
489
490 // We store the first URL we receive for a given application. If there's
491 // already an entry for this application, no need to do anything.
[email protected]1e0fe2822010-06-24 21:04:19492 // TODO(atwilson): Verify that this is the desired behavior based on developer
493 // feedback (http://crbug.com/47118).
[email protected]f33bee52011-03-31 09:18:43494 DictionaryPrefUpdate update(prefs_, prefs::kRegisteredBackgroundContents);
495 DictionaryValue* pref = update.Get();
[email protected]398206c2010-06-21 01:46:08496 const string16& appid = GetParentApplicationId(background_contents);
497 DictionaryValue* current;
[email protected]90018542010-08-14 01:49:14498 if (pref->GetDictionaryWithoutPathExpansion(UTF16ToUTF8(appid), &current))
[email protected]398206c2010-06-21 01:46:08499 return;
500
501 // No entry for this application yet, so add one.
502 DictionaryValue* dict = new DictionaryValue();
503 dict->SetString(kUrlKey, background_contents->GetURL().spec());
[email protected]ff4c1d82010-08-04 16:58:12504 dict->SetString(kFrameNameKey, contents_map_[appid].frame_name);
[email protected]8b6e84a2010-08-14 03:32:17505 pref->SetWithoutPathExpansion(UTF16ToUTF8(appid), dict);
[email protected]398206c2010-06-21 01:46:08506}
507
508void BackgroundContentsService::UnregisterBackgroundContents(
509 BackgroundContents* background_contents) {
510 if (!prefs_)
511 return;
512 DCHECK(IsTracked(background_contents));
513 const string16 appid = GetParentApplicationId(background_contents);
[email protected]f33bee52011-03-31 09:18:43514 DictionaryPrefUpdate update(prefs_, prefs::kRegisteredBackgroundContents);
515 update.Get()->RemoveWithoutPathExpansion(UTF16ToUTF8(appid), NULL);
[email protected]398206c2010-06-21 01:46:08516}
517
518void BackgroundContentsService::ShutdownAssociatedBackgroundContents(
519 const string16& appid) {
520 BackgroundContents* contents = GetAppBackgroundContents(appid);
521 if (contents) {
522 UnregisterBackgroundContents(contents);
523 // Background contents destructor shuts down the renderer.
524 delete contents;
525 }
526}
527
528void BackgroundContentsService::BackgroundContentsOpened(
529 BackgroundContentsOpenedDetails* details) {
[email protected]398206c2010-06-21 01:46:08530 // Add the passed object to our list. Should not already be tracked.
531 DCHECK(!IsTracked(details->contents));
532 DCHECK(!details->application_id.empty());
533 contents_map_[details->application_id].contents = details->contents;
534 contents_map_[details->application_id].frame_name = details->frame_name;
[email protected]aa4e5ad42011-05-18 04:56:35535
536 ScheduleCloseBalloon(UTF16ToASCII(details->application_id));
[email protected]398206c2010-06-21 01:46:08537}
538
539// Used by test code and debug checks to verify whether a given
540// BackgroundContents is being tracked by this instance.
541bool BackgroundContentsService::IsTracked(
542 BackgroundContents* background_contents) const {
543 return !GetParentApplicationId(background_contents).empty();
544}
545
546void BackgroundContentsService::BackgroundContentsShutdown(
547 BackgroundContents* background_contents) {
548 // Remove the passed object from our list.
549 DCHECK(IsTracked(background_contents));
550 string16 appid = GetParentApplicationId(background_contents);
551 contents_map_.erase(appid);
[email protected]398206c2010-06-21 01:46:08552}
553
554BackgroundContents* BackgroundContentsService::GetAppBackgroundContents(
555 const string16& application_id) {
556 BackgroundContentsMap::const_iterator it = contents_map_.find(application_id);
557 return (it != contents_map_.end()) ? it->second.contents : NULL;
558}
559
560const string16& BackgroundContentsService::GetParentApplicationId(
561 BackgroundContents* contents) const {
562 for (BackgroundContentsMap::const_iterator it = contents_map_.begin();
563 it != contents_map_.end(); ++it) {
564 if (contents == it->second.contents)
565 return it->first;
566 }
567 return EmptyString16();
568}
569
[email protected]2a6bc3e2011-12-28 23:51:33570void BackgroundContentsService::AddWebContents(
571 WebContents* new_contents,
[email protected]bc093872010-08-16 21:49:08572 WindowOpenDisposition disposition,
573 const gfx::Rect& initial_pos,
574 bool user_gesture) {
575 Browser* browser = BrowserList::GetLastActiveWithProfile(
[email protected]627e0512011-12-21 22:55:30576 Profile::FromBrowserContext(new_contents->GetBrowserContext()));
[email protected]bc093872010-08-16 21:49:08577 if (!browser)
578 return;
[email protected]2a6bc3e2011-12-28 23:51:33579 browser->AddWebContents(new_contents, disposition, initial_pos, user_gesture);
[email protected]bc093872010-08-16 21:49:08580}