Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Side by Side Diff: chrome/browser/background/background_contents_service.cc

Issue 23427003: Reload force-installed extensions on crash/force-close (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: good2.crx was committed manually, this should now work! Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/background/background_contents_service.h" 5 #include "chrome/browser/background/background_contents_service.h"
6 6
7 #include "apps/app_load_service.h" 7 #include "apps/app_load_service.h"
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/message_loop/message_loop.h" 11 #include "base/message_loop/message_loop.h"
12 #include "base/prefs/pref_service.h" 12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h" 15 #include "base/values.h"
16 #include "chrome/browser/background/background_contents_service_factory.h" 16 #include "chrome/browser/background/background_contents_service_factory.h"
17 #include "chrome/browser/browser_process.h" 17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h" 18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/extension_host.h" 19 #include "chrome/browser/extensions/extension_host.h"
20 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h" 21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/extensions/image_loader.h" 22 #include "chrome/browser/extensions/image_loader.h"
23 #include "chrome/browser/notifications/desktop_notification_service.h" 23 #include "chrome/browser/notifications/desktop_notification_service.h"
24 #include "chrome/browser/notifications/notification.h" 24 #include "chrome/browser/notifications/notification.h"
25 #include "chrome/browser/notifications/notification_delegate.h"
25 #include "chrome/browser/notifications/notification_ui_manager.h" 26 #include "chrome/browser/notifications/notification_ui_manager.h"
26 #include "chrome/browser/prefs/scoped_user_pref_update.h" 27 #include "chrome/browser/prefs/scoped_user_pref_update.h"
27 #include "chrome/browser/profiles/profile.h" 28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/browser/ui/browser.h" 30 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_finder.h" 31 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_tabstrip.h" 32 #include "chrome/browser/ui/browser_tabstrip.h"
31 #include "chrome/browser/ui/host_desktop.h" 33 #include "chrome/browser/ui/host_desktop.h"
32 #include "chrome/common/chrome_switches.h" 34 #include "chrome/common/chrome_switches.h"
33 #include "chrome/common/extensions/background_info.h" 35 #include "chrome/common/extensions/background_info.h"
34 #include "chrome/common/extensions/extension.h" 36 #include "chrome/common/extensions/extension.h"
35 #include "chrome/common/extensions/extension_constants.h" 37 #include "chrome/common/extensions/extension_constants.h"
36 #include "chrome/common/extensions/extension_icon_set.h" 38 #include "chrome/common/extensions/extension_icon_set.h"
37 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 39 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
38 #include "chrome/common/pref_names.h" 40 #include "chrome/common/pref_names.h"
39 #include "content/public/browser/notification_service.h" 41 #include "content/public/browser/notification_service.h"
40 #include "content/public/browser/site_instance.h" 42 #include "content/public/browser/site_instance.h"
41 #include "content/public/browser/web_contents.h" 43 #include "content/public/browser/web_contents.h"
42 #include "grit/generated_resources.h" 44 #include "grit/generated_resources.h"
43 #include "grit/theme_resources.h" 45 #include "grit/theme_resources.h"
44 #include "ipc/ipc_message.h" 46 #include "ipc/ipc_message.h"
45 #include "ui/base/l10n/l10n_util.h" 47 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h" 48 #include "ui/base/resource/resource_bundle.h"
47 #include "ui/gfx/image/image.h" 49 #include "ui/gfx/image/image.h"
48 50
49 using content::SiteInstance; 51 using content::SiteInstance;
50 using content::WebContents; 52 using content::WebContents;
51 using extensions::BackgroundInfo; 53 using extensions::BackgroundInfo;
52 using extensions::Extension; 54 using extensions::Extension;
53 using extensions::UnloadedExtensionInfo; 55 using extensions::UnloadedExtensionInfo;
54 56
55 namespace { 57 namespace {
56 58
57 const char kNotificationPrefix[] = "app.background.crashed."; 59 const char kCrashNotificationPrefix[] = "app.background.crashed.";
60 const char kMisbehaveNotificationPrefix[] = "app.background.misbehaved.";
58 61
59 void CloseBalloon(const std::string& id) { 62 // Number of recent crashes of a force-installed app/extension that will
60 g_browser_process->notification_ui_manager()->CancelById(id); 63 // trigger an 'App/Extension is misbehaving' balloon.
64 const unsigned int kMisbehaveCrashCountThreshold = 5;
65
66 void CloseBalloon(const std::string& balloon_id) {
67 g_browser_process->notification_ui_manager()->
68 CancelById(balloon_id);
61 } 69 }
62 70
63 void ScheduleCloseBalloon(const std::string& extension_id) { 71 // Closes the balloon with this id.
72 void ScheduleCloseBalloon(const std::string& balloon_id) {
64 if (!base::MessageLoop::current()) // For unit_tests 73 if (!base::MessageLoop::current()) // For unit_tests
65 return; 74 return;
66 base::MessageLoop::current()->PostTask( 75 base::MessageLoop::current()->PostTask(
67 FROM_HERE, base::Bind(&CloseBalloon, kNotificationPrefix + extension_id)); 76 FROM_HERE, base::Bind(&CloseBalloon, balloon_id));
68 } 77 }
69 78
79 // Closes the crash notification balloon for the app/extension with this id.
80 void ScheduleCloseCrashBalloon(const std::string& extension_id) {
81 ScheduleCloseBalloon(kCrashNotificationPrefix + extension_id);
82 }
83
84 // Closes all notification balloons relating to the app/extension with this id.
85 void ScheduleCloseBalloons(const std::string& extension_id) {
86 ScheduleCloseBalloon(kMisbehaveNotificationPrefix + extension_id);
87 ScheduleCloseBalloon(kCrashNotificationPrefix + extension_id);
88 }
89
90 // Delegate for the 'app/extension has crashed' popup balloon. Restarts the
91 // app/extension when the balloon is clicked.
70 class CrashNotificationDelegate : public NotificationDelegate { 92 class CrashNotificationDelegate : public NotificationDelegate {
71 public: 93 public:
72 CrashNotificationDelegate(Profile* profile, 94 CrashNotificationDelegate(Profile* profile,
73 const Extension* extension) 95 const Extension* extension)
74 : profile_(profile), 96 : profile_(profile),
75 is_hosted_app_(extension->is_hosted_app()), 97 is_hosted_app_(extension->is_hosted_app()),
76 is_platform_app_(extension->is_platform_app()), 98 is_platform_app_(extension->is_platform_app()),
77 extension_id_(extension->id()) { 99 extension_id_(extension->id()) {
78 } 100 }
79 101
(...skipping 20 matching lines...) Expand all
100 service->LoadBackgroundContentsForExtension(profile_, 122 service->LoadBackgroundContentsForExtension(profile_,
101 copied_extension_id); 123 copied_extension_id);
102 } else if (is_platform_app_) { 124 } else if (is_platform_app_) {
103 apps::AppLoadService::Get(profile_)-> 125 apps::AppLoadService::Get(profile_)->
104 RestartApplication(copied_extension_id); 126 RestartApplication(copied_extension_id);
105 } else { 127 } else {
106 extensions::ExtensionSystem::Get(profile_)->extension_service()-> 128 extensions::ExtensionSystem::Get(profile_)->extension_service()->
107 ReloadExtension(copied_extension_id); 129 ReloadExtension(copied_extension_id);
108 } 130 }
109 131
110 // Closing the balloon here should be OK, but it causes a crash on Mac 132 // Closing the 'app/extension has crashed' balloon here should be OK, but it
111 // http://crbug.com/78167 133 // causes a crash on Mac (http://crbug.com/78167).
112 ScheduleCloseBalloon(copied_extension_id); 134 ScheduleCloseCrashBalloon(copied_extension_id);
113 } 135 }
114 136
115 virtual bool HasClickedListener() OVERRIDE { return true; } 137 virtual bool HasClickedListener() OVERRIDE { return true; }
116 138
117 virtual std::string id() const OVERRIDE { 139 virtual std::string id() const OVERRIDE {
118 return kNotificationPrefix + extension_id_; 140 return kCrashNotificationPrefix + extension_id_;
119 } 141 }
120 142
121 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { 143 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
122 return NULL; 144 return NULL;
123 } 145 }
124 146
125 private: 147 private:
126 virtual ~CrashNotificationDelegate() {} 148 virtual ~CrashNotificationDelegate() {}
127 149
128 Profile* profile_; 150 Profile* profile_;
129 bool is_hosted_app_; 151 bool is_hosted_app_;
130 bool is_platform_app_; 152 bool is_platform_app_;
131 std::string extension_id_; 153 std::string extension_id_;
132 154
133 DISALLOW_COPY_AND_ASSIGN(CrashNotificationDelegate); 155 DISALLOW_COPY_AND_ASSIGN(CrashNotificationDelegate);
134 }; 156 };
135 157
158 // Empty delegate for the 'app/extension is misbehaving' popup balloon, which is
159 // triggered if a force-installed app/extension gets stuck in a crash/reload
160 // cycle. Doesn't do anything on click because force-installed apps/extensions
161 // get restarted automatically.
162 class MisbehaveNotificationDelegate : public NotificationDelegate {
163 public:
164 explicit MisbehaveNotificationDelegate(const Extension* extension)
165 : extension_id_(extension->id()) {
166 }
167
168 virtual void Display() OVERRIDE {}
169
170 virtual void Error() OVERRIDE {}
171
172 virtual void Close(bool by_user) OVERRIDE {}
173
174 virtual void Click() OVERRIDE {}
175
176 virtual bool HasClickedListener() OVERRIDE { return true; }
177
178 virtual std::string id() const OVERRIDE {
179 return kMisbehaveNotificationPrefix + extension_id_;
180 }
181
182 virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
183 return NULL;
184 }
185
186 private:
187 virtual ~MisbehaveNotificationDelegate() {}
188
189 std::string extension_id_;
190
191 DISALLOW_COPY_AND_ASSIGN(MisbehaveNotificationDelegate);
192 };
193
136 #if defined(ENABLE_NOTIFICATIONS) 194 #if defined(ENABLE_NOTIFICATIONS)
137 void NotificationImageReady( 195 void NotificationImageReady(
138 const std::string extension_name, 196 const std::string extension_name,
139 const string16 message, 197 const string16 message,
140 const GURL extension_url, 198 const GURL extension_url,
141 scoped_refptr<CrashNotificationDelegate> delegate, 199 scoped_refptr<NotificationDelegate> delegate,
142 Profile* profile, 200 Profile* profile,
143 const gfx::Image& icon) { 201 const gfx::Image& icon) {
144 gfx::Image notification_icon(icon); 202 gfx::Image notification_icon(icon);
145 if (icon.IsEmpty()) { 203 if (icon.IsEmpty()) {
146 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 204 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
147 notification_icon = rb.GetImageNamed(IDR_EXTENSION_DEFAULT_ICON); 205 notification_icon = rb.GetImageNamed(IDR_EXTENSION_DEFAULT_ICON);
148 } 206 }
149 string16 title; // no notification title 207 string16 title; // no notification title
150 DesktopNotificationService::AddIconNotification(extension_url, 208 DesktopNotificationService::AddIconNotification(extension_url,
151 title, 209 title,
152 message, 210 message,
153 notification_icon, 211 notification_icon,
154 string16(), 212 string16(),
155 delegate.get(), 213 delegate.get(),
156 profile); 214 profile);
157 } 215 }
158 #endif 216 #endif
159 217
160 void ShowBalloon(const Extension* extension, Profile* profile) { 218 // Show a popup notification balloon with a crash message for a given app/
219 // extension. If |force_installed| is true we show an 'App/extension
220 // is misbehaving' message instead of a crash message.
221 void ShowBalloon(const Extension* extension, Profile* profile,
222 bool force_installed) {
161 #if defined(ENABLE_NOTIFICATIONS) 223 #if defined(ENABLE_NOTIFICATIONS)
162 string16 message = l10n_util::GetStringFUTF16( 224 string16 message;
163 extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE : 225 scoped_refptr<NotificationDelegate> delegate;
164 IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE, 226 if (force_installed) {
165 UTF8ToUTF16(extension->name())); 227 message = l10n_util::GetStringFUTF16(
166 228 extension->is_app() ?
229 IDS_BACKGROUND_MISBEHAVING_APP_BALLOON_MESSAGE :
230 IDS_BACKGROUND_MISBEHAVING_EXTENSION_BALLOON_MESSAGE,
231 UTF8ToUTF16(extension->name()));
232 delegate = new MisbehaveNotificationDelegate(extension);
233 } else {
234 message = l10n_util::GetStringFUTF16(
235 extension->is_app() ? IDS_BACKGROUND_CRASHED_APP_BALLOON_MESSAGE :
236 IDS_BACKGROUND_CRASHED_EXTENSION_BALLOON_MESSAGE,
237 UTF8ToUTF16(extension->name()));
238 delegate = new CrashNotificationDelegate(profile, extension);
239 }
167 extension_misc::ExtensionIcons size(extension_misc::EXTENSION_ICON_MEDIUM); 240 extension_misc::ExtensionIcons size(extension_misc::EXTENSION_ICON_MEDIUM);
168 extensions::ExtensionResource resource = 241 extensions::ExtensionResource resource =
169 extensions::IconsInfo::GetIconResource( 242 extensions::IconsInfo::GetIconResource(
170 extension, size, ExtensionIconSet::MATCH_SMALLER); 243 extension, size, ExtensionIconSet::MATCH_SMALLER);
171 scoped_refptr<CrashNotificationDelegate> delegate =
172 new CrashNotificationDelegate(profile, extension);
173 // We can't just load the image in the Observe method below because, despite 244 // We can't just load the image in the Observe method below because, despite
174 // what this method is called, it may call the callback synchronously. 245 // what this method is called, it may call the callback synchronously.
175 // However, it's possible that the extension went away during the interim, 246 // However, it's possible that the extension went away during the interim,
176 // so we'll bind all the pertinent data here. 247 // so we'll bind all the pertinent data here.
177 extensions::ImageLoader::Get(profile)->LoadImageAsync( 248 extensions::ImageLoader::Get(profile)->LoadImageAsync(
178 extension, 249 extension,
179 resource, 250 resource,
180 gfx::Size(size, size), 251 gfx::Size(size, size),
181 base::Bind( 252 base::Bind(
182 &NotificationImageReady, 253 &NotificationImageReady,
183 extension->name(), 254 extension->name(),
184 message, 255 message,
185 extension->url(), 256 extension->url(),
186 delegate, 257 delegate,
187 profile)); 258 profile));
188 #endif 259 #endif
189 } 260 }
190 261
262 void ReloadExtension(
263 const std::string& extension_id, Profile* profile) {
264 if (g_browser_process->IsShuttingDown() ||
265 !g_browser_process->profile_manager()->IsValidProfile(profile)) {
266 return;
267 }
268 extensions::ExtensionSystem* extension_system =
269 extensions::ExtensionSystem::Get(profile);
270 if (!extension_system || !extension_system->extension_service())
271 return;
272 if (!extension_system->extension_service()->
273 GetTerminatedExtension(extension_id)) {
274 // Either the app/extension was uninstalled by policy or it has since
275 // been restarted successfully by someone else (the user).
276 return;
277 }
278 extension_system->extension_service()->ReloadExtension(extension_id);
279 }
280
191 } // namespace 281 } // namespace
192 282
193 // Keys for the information we store about individual BackgroundContents in 283 // Keys for the information we store about individual BackgroundContents in
194 // prefs. There is one top-level DictionaryValue (stored at 284 // prefs. There is one top-level DictionaryValue (stored at
195 // prefs::kRegisteredBackgroundContents). Information about each 285 // prefs::kRegisteredBackgroundContents). Information about each
196 // BackgroundContents is stored under that top-level DictionaryValue, keyed 286 // BackgroundContents is stored under that top-level DictionaryValue, keyed
197 // by the parent application ID for easy lookup. 287 // by the parent application ID for easy lookup.
198 // 288 //
199 // kRegisteredBackgroundContents: 289 // kRegisteredBackgroundContents:
200 // DictionaryValue { 290 // DictionaryValue {
201 // <appid_1>: { "url": <url1>, "name": <frame_name> }, 291 // <appid_1>: { "url": <url1>, "name": <frame_name> },
202 // <appid_2>: { "url": <url2>, "name": <frame_name> }, 292 // <appid_2>: { "url": <url2>, "name": <frame_name> },
203 // ... etc ... 293 // ... etc ...
204 // } 294 // }
205 const char kUrlKey[] = "url"; 295 const char kUrlKey[] = "url";
206 const char kFrameNameKey[] = "name"; 296 const char kFrameNameKey[] = "name";
207 297
298 int BackgroundContentsService::restart_delay_in_ms_ = 3000; // 3 seconds.
299 int BackgroundContentsService::crash_window_in_ms_ = 1000; // 1 second.
300
208 BackgroundContentsService::BackgroundContentsService( 301 BackgroundContentsService::BackgroundContentsService(
209 Profile* profile, const CommandLine* command_line) 302 Profile* profile, const CommandLine* command_line)
210 : prefs_(NULL) { 303 : prefs_(NULL) {
211 // Don't load/store preferences if the proper switch is not enabled, or if 304 // Don't load/store preferences if the proper switch is not enabled, or if
212 // the parent profile is incognito. 305 // the parent profile is incognito.
213 if (!profile->IsOffTheRecord() && 306 if (!profile->IsOffTheRecord() &&
214 !command_line->HasSwitch(switches::kDisableRestoreBackgroundContents)) 307 !command_line->HasSwitch(switches::kDisableRestoreBackgroundContents))
215 prefs_ = profile->GetPrefs(); 308 prefs_ = profile->GetPrefs();
216 309
217 // Listen for events to tell us when to load/unload persisted background 310 // Listen for events to tell us when to load/unload persisted background
218 // contents. 311 // contents.
219 StartObserving(profile); 312 StartObserving(profile);
220 } 313 }
221 314
222 BackgroundContentsService::~BackgroundContentsService() { 315 BackgroundContentsService::~BackgroundContentsService() {
223 // BackgroundContents should be shutdown before we go away, as otherwise 316 // BackgroundContents should be shutdown before we go away, as otherwise
224 // our browser process refcount will be off. 317 // our browser process refcount will be off.
225 DCHECK(contents_map_.empty()); 318 DCHECK(contents_map_.empty());
226 } 319 }
227 320
321 // static
322 void BackgroundContentsService::
323 SetCrashDelaysForForceInstalledAppsAndExtensionsForTesting(
324 int restart_delay_in_ms, int crash_window_in_ms) {
325 restart_delay_in_ms_ = restart_delay_in_ms;
326 crash_window_in_ms_ = crash_window_in_ms;
327 }
328
228 std::vector<BackgroundContents*> 329 std::vector<BackgroundContents*>
229 BackgroundContentsService::GetBackgroundContents() const 330 BackgroundContentsService::GetBackgroundContents() const
230 { 331 {
231 std::vector<BackgroundContents*> contents; 332 std::vector<BackgroundContents*> contents;
232 for (BackgroundContentsMap::const_iterator it = contents_map_.begin(); 333 for (BackgroundContentsMap::const_iterator it = contents_map_.begin();
233 it != contents_map_.end(); ++it) 334 it != contents_map_.end(); ++it)
234 contents.push_back(it->second.contents); 335 contents.push_back(it->second.contents);
235 return contents; 336 return contents;
236 } 337 }
237 338
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 // Now load the manifest-specified background page. If service isn't 444 // Now load the manifest-specified background page. If service isn't
344 // ready, then the background page will be loaded from the 445 // ready, then the background page will be loaded from the
345 // EXTENSIONS_READY callback. 446 // EXTENSIONS_READY callback.
346 LoadBackgroundContents(profile, 447 LoadBackgroundContents(profile,
347 BackgroundInfo::GetBackgroundURL(extension), 448 BackgroundInfo::GetBackgroundURL(extension),
348 ASCIIToUTF16("background"), 449 ASCIIToUTF16("background"),
349 UTF8ToUTF16(extension->id())); 450 UTF8ToUTF16(extension->id()));
350 } 451 }
351 } 452 }
352 453
353 // Remove any "This extension has crashed" balloons. 454 // Remove any "app/extension has crashed" balloons.
354 ScheduleCloseBalloon(extension->id()); 455 ScheduleCloseCrashBalloon(extension->id());
355 SendChangeNotification(profile); 456 SendChangeNotification(profile);
356 break; 457 break;
357 } 458 }
358 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: 459 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED:
359 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: { 460 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: {
360 Profile* profile = content::Source<Profile>(source).ptr(); 461 Profile* profile = content::Source<Profile>(source).ptr();
361 const Extension* extension = NULL; 462 const Extension* extension = NULL;
362 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) { 463 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) {
363 BackgroundContents* bg = 464 BackgroundContents* bg =
364 content::Details<BackgroundContents>(details).ptr(); 465 content::Details<BackgroundContents>(details).ptr();
365 std::string extension_id = UTF16ToASCII( 466 std::string extension_id = UTF16ToASCII(
366 BackgroundContentsServiceFactory::GetForProfile(profile)-> 467 BackgroundContentsServiceFactory::GetForProfile(profile)->
367 GetParentApplicationId(bg)); 468 GetParentApplicationId(bg));
368 extension = 469 extension =
369 extensions::ExtensionSystem::Get(profile)->extension_service()-> 470 extensions::ExtensionSystem::Get(profile)->extension_service()->
370 GetExtensionById(extension_id, false); 471 GetExtensionById(extension_id, false);
371 } else { 472 } else {
372 extensions::ExtensionHost* extension_host = 473 extensions::ExtensionHost* extension_host =
373 content::Details<extensions::ExtensionHost>(details).ptr(); 474 content::Details<extensions::ExtensionHost>(details).ptr();
374 extension = extension_host->extension(); 475 extension = extension_host->extension();
375 } 476 }
376 if (!extension) 477 if (!extension)
377 break; 478 break;
378 479
379 // When an extension crashes, EXTENSION_PROCESS_TERMINATED is followed by 480 const bool force_installed = extension->location() ==
380 // an EXTENSION_UNLOADED notification. This UNLOADED signal causes all the 481 extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD;
381 // notifications for this extension to be cancelled by 482 if (!force_installed) {
382 // DesktopNotificationService. For this reason, instead of showing the 483 // When an extension crashes, EXTENSION_PROCESS_TERMINATED is followed
383 // balloon right now, we schedule it to show a little later. 484 // by an EXTENSION_UNLOADED notification. This UNLOADED signal causes
384 base::MessageLoop::current()->PostTask( 485 // all the notifications for this extension to be cancelled by
385 FROM_HERE, base::Bind(&ShowBalloon, extension, profile)); 486 // DesktopNotificationService. For this reason, we post the crash
487 // handling code as a task here so that it is not executed before this
488 // event.
489 base::MessageLoop::current()->PostTask(
490 FROM_HERE, base::Bind(&ShowBalloon, extension, profile, false));
491 } else {
492 // Restart the extension; notify user if crash recurs frequently.
493 RestartForceInstalledExtensionOnCrash(extension, profile);
494 }
386 break; 495 break;
387 } 496 }
388 case chrome::NOTIFICATION_EXTENSION_UNLOADED: 497 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
389 switch (content::Details<UnloadedExtensionInfo>(details)->reason) { 498 switch (content::Details<UnloadedExtensionInfo>(details)->reason) {
390 case extension_misc::UNLOAD_REASON_DISABLE: // Fall through. 499 case extension_misc::UNLOAD_REASON_DISABLE: // Fall through.
391 case extension_misc::UNLOAD_REASON_TERMINATE: // Fall through. 500 case extension_misc::UNLOAD_REASON_TERMINATE: // Fall through.
392 case extension_misc::UNLOAD_REASON_UNINSTALL: // Fall through. 501 case extension_misc::UNLOAD_REASON_UNINSTALL: // Fall through.
393 case extension_misc::UNLOAD_REASON_BLACKLIST: 502 case extension_misc::UNLOAD_REASON_BLACKLIST:
394 ShutdownAssociatedBackgroundContents( 503 ShutdownAssociatedBackgroundContents(
395 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> 504 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)->
(...skipping 16 matching lines...) Expand all
412 default: 521 default:
413 NOTREACHED(); 522 NOTREACHED();
414 ShutdownAssociatedBackgroundContents( 523 ShutdownAssociatedBackgroundContents(
415 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> 524 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)->
416 extension->id())); 525 extension->id()));
417 break; 526 break;
418 } 527 }
419 break; 528 break;
420 529
421 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { 530 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
422 // Remove any "This extension has crashed" balloons. 531 const std::string& extension_id =
423 ScheduleCloseBalloon( 532 content::Details<const Extension>(details).ptr()->id();
424 content::Details<const Extension>(details).ptr()->id()); 533 // Remove any balloons shown for this app/extension.
534 ScheduleCloseBalloons(extension_id);
535 misbehaving_extensions_.erase(extension_id);
536 extension_crashlog_map_.erase(extension_id);
425 break; 537 break;
426 } 538 }
427 539
428 default: 540 default:
429 NOTREACHED(); 541 NOTREACHED();
430 break; 542 break;
431 } 543 }
432 } 544 }
433 545
546 void BackgroundContentsService::RestartForceInstalledExtensionOnCrash(
547 const Extension* extension, Profile* profile) {
548 const std::string& extension_id = extension->id();
549 const bool already_notified = misbehaving_extensions_.find(extension_id) !=
550 misbehaving_extensions_.end();
551 std::queue<base::TimeTicks>& crashes = extension_crashlog_map_[extension_id];
552 const base::TimeDelta recent_time_window =
553 base::TimeDelta::FromMilliseconds(kMisbehaveCrashCountThreshold *
554 (restart_delay_in_ms_ + crash_window_in_ms_));
555 if (!already_notified) {
556 // Show a notification if the threshold number of crashes has occurred
557 // within a recent time period.
558 const bool should_notify =
559 crashes.size() == kMisbehaveCrashCountThreshold - 1 &&
560 base::TimeTicks::Now() - crashes.front() < recent_time_window;
561 if (should_notify) {
562 base::MessageLoop::current()->PostTask(FROM_HERE,
563 base::Bind(&ShowBalloon, extension, profile, true));
564 misbehaving_extensions_.insert(extension_id);
565 extension_crashlog_map_.erase(extension_id);
566 } else {
567 while (!crashes.empty() &&
568 base::TimeTicks::Now() - crashes.front() > recent_time_window) {
569 // Remove old timestamps.
570 crashes.pop();
571 }
572 crashes.push(base::TimeTicks::Now());
573 if (crashes.size() == kMisbehaveCrashCountThreshold)
574 crashes.pop();
575 }
576 }
577 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
578 base::Bind(&ReloadExtension, extension_id, profile),
579 base::TimeDelta::FromMilliseconds(restart_delay_in_ms_));
580 }
581
434 // Loads all background contents whose urls have been stored in prefs. 582 // Loads all background contents whose urls have been stored in prefs.
435 void BackgroundContentsService::LoadBackgroundContentsFromPrefs( 583 void BackgroundContentsService::LoadBackgroundContentsFromPrefs(
436 Profile* profile) { 584 Profile* profile) {
437 if (!prefs_) 585 if (!prefs_)
438 return; 586 return;
439 const DictionaryValue* contents = 587 const DictionaryValue* contents =
440 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents); 588 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents);
441 if (!contents) 589 if (!contents)
442 return; 590 return;
443 ExtensionService* extensions_service = 591 ExtensionService* extensions_service =
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
646 } 794 }
647 795
648 void BackgroundContentsService::BackgroundContentsOpened( 796 void BackgroundContentsService::BackgroundContentsOpened(
649 BackgroundContentsOpenedDetails* details) { 797 BackgroundContentsOpenedDetails* details) {
650 // Add the passed object to our list. Should not already be tracked. 798 // Add the passed object to our list. Should not already be tracked.
651 DCHECK(!IsTracked(details->contents)); 799 DCHECK(!IsTracked(details->contents));
652 DCHECK(!details->application_id.empty()); 800 DCHECK(!details->application_id.empty());
653 contents_map_[details->application_id].contents = details->contents; 801 contents_map_[details->application_id].contents = details->contents;
654 contents_map_[details->application_id].frame_name = details->frame_name; 802 contents_map_[details->application_id].frame_name = details->frame_name;
655 803
656 ScheduleCloseBalloon(UTF16ToASCII(details->application_id)); 804 ScheduleCloseCrashBalloon(UTF16ToASCII(details->application_id));
657 } 805 }
658 806
659 // Used by test code and debug checks to verify whether a given 807 // Used by test code and debug checks to verify whether a given
660 // BackgroundContents is being tracked by this instance. 808 // BackgroundContents is being tracked by this instance.
661 bool BackgroundContentsService::IsTracked( 809 bool BackgroundContentsService::IsTracked(
662 BackgroundContents* background_contents) const { 810 BackgroundContents* background_contents) const {
663 return !GetParentApplicationId(background_contents).empty(); 811 return !GetParentApplicationId(background_contents).empty();
664 } 812 }
665 813
666 void BackgroundContentsService::BackgroundContentsShutdown( 814 void BackgroundContentsService::BackgroundContentsShutdown(
(...skipping 27 matching lines...) Expand all
694 bool user_gesture, 842 bool user_gesture,
695 bool* was_blocked) { 843 bool* was_blocked) {
696 Browser* browser = chrome::FindLastActiveWithProfile( 844 Browser* browser = chrome::FindLastActiveWithProfile(
697 Profile::FromBrowserContext(new_contents->GetBrowserContext()), 845 Profile::FromBrowserContext(new_contents->GetBrowserContext()),
698 chrome::GetActiveDesktop()); 846 chrome::GetActiveDesktop());
699 if (browser) { 847 if (browser) {
700 chrome::AddWebContents(browser, NULL, new_contents, disposition, 848 chrome::AddWebContents(browser, NULL, new_contents, disposition,
701 initial_pos, user_gesture, was_blocked); 849 initial_pos, user_gesture, was_blocked);
702 } 850 }
703 } 851 }
OLDNEW
« no previous file with comments | « chrome/browser/background/background_contents_service.h ('k') | chrome/browser/policy/policy_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698