OLD | NEW |
---|---|
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/memory/weak_ptr.h" | |
11 #include "base/message_loop/message_loop.h" | 12 #include "base/message_loop/message_loop.h" |
12 #include "base/prefs/pref_service.h" | 13 #include "base/prefs/pref_service.h" |
13 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
14 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
15 #include "base/values.h" | 16 #include "base/values.h" |
16 #include "chrome/browser/background/background_contents_service_factory.h" | 17 #include "chrome/browser/background/background_contents_service_factory.h" |
17 #include "chrome/browser/browser_process.h" | 18 #include "chrome/browser/browser_process.h" |
18 #include "chrome/browser/chrome_notification_types.h" | 19 #include "chrome/browser/chrome_notification_types.h" |
19 #include "chrome/browser/extensions/extension_host.h" | 20 #include "chrome/browser/extensions/extension_host.h" |
20 #include "chrome/browser/extensions/extension_service.h" | 21 #include "chrome/browser/extensions/extension_service.h" |
21 #include "chrome/browser/extensions/extension_system.h" | 22 #include "chrome/browser/extensions/extension_system.h" |
22 #include "chrome/browser/extensions/image_loader.h" | 23 #include "chrome/browser/extensions/image_loader.h" |
23 #include "chrome/browser/notifications/desktop_notification_service.h" | 24 #include "chrome/browser/notifications/desktop_notification_service.h" |
24 #include "chrome/browser/notifications/notification.h" | 25 #include "chrome/browser/notifications/notification.h" |
26 #include "chrome/browser/notifications/notification_delegate.h" | |
25 #include "chrome/browser/notifications/notification_ui_manager.h" | 27 #include "chrome/browser/notifications/notification_ui_manager.h" |
26 #include "chrome/browser/prefs/scoped_user_pref_update.h" | 28 #include "chrome/browser/prefs/scoped_user_pref_update.h" |
27 #include "chrome/browser/profiles/profile.h" | 29 #include "chrome/browser/profiles/profile.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" |
(...skipping 12 matching lines...) Expand all Loading... | |
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 Loading... | |
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, |
(...skipping 15 matching lines...) Expand all Loading... | |
198 // | 269 // |
199 // kRegisteredBackgroundContents: | 270 // kRegisteredBackgroundContents: |
200 // DictionaryValue { | 271 // DictionaryValue { |
201 // <appid_1>: { "url": <url1>, "name": <frame_name> }, | 272 // <appid_1>: { "url": <url1>, "name": <frame_name> }, |
202 // <appid_2>: { "url": <url2>, "name": <frame_name> }, | 273 // <appid_2>: { "url": <url2>, "name": <frame_name> }, |
203 // ... etc ... | 274 // ... etc ... |
204 // } | 275 // } |
205 const char kUrlKey[] = "url"; | 276 const char kUrlKey[] = "url"; |
206 const char kFrameNameKey[] = "name"; | 277 const char kFrameNameKey[] = "name"; |
207 | 278 |
279 int BackgroundContentsService::restart_delay_in_ms_ = 3000; // 3 seconds. | |
280 int BackgroundContentsService::crash_window_in_ms_ = 1000; // 1 second. | |
281 | |
208 BackgroundContentsService::BackgroundContentsService( | 282 BackgroundContentsService::BackgroundContentsService( |
209 Profile* profile, const CommandLine* command_line) | 283 Profile* profile, const CommandLine* command_line) |
210 : prefs_(NULL) { | 284 : prefs_(NULL), weak_factory_(this) { |
211 // Don't load/store preferences if the proper switch is not enabled, or if | 285 // Don't load/store preferences if the proper switch is not enabled, or if |
212 // the parent profile is incognito. | 286 // the parent profile is incognito. |
213 if (!profile->IsOffTheRecord() && | 287 if (!profile->IsOffTheRecord() && |
214 !command_line->HasSwitch(switches::kDisableRestoreBackgroundContents)) | 288 !command_line->HasSwitch(switches::kDisableRestoreBackgroundContents)) |
215 prefs_ = profile->GetPrefs(); | 289 prefs_ = profile->GetPrefs(); |
216 | 290 |
217 // Listen for events to tell us when to load/unload persisted background | 291 // Listen for events to tell us when to load/unload persisted background |
218 // contents. | 292 // contents. |
219 StartObserving(profile); | 293 StartObserving(profile); |
220 } | 294 } |
221 | 295 |
222 BackgroundContentsService::~BackgroundContentsService() { | 296 BackgroundContentsService::~BackgroundContentsService() { |
223 // BackgroundContents should be shutdown before we go away, as otherwise | 297 // BackgroundContents should be shutdown before we go away, as otherwise |
224 // our browser process refcount will be off. | 298 // our browser process refcount will be off. |
225 DCHECK(contents_map_.empty()); | 299 DCHECK(contents_map_.empty()); |
226 } | 300 } |
227 | 301 |
302 // static | |
303 void BackgroundContentsService:: | |
304 SetCrashDelaysForForceInstalledAppsAndExtensionsForTesting( | |
305 int restart_delay_in_ms, int crash_window_in_ms) { | |
306 restart_delay_in_ms_ = restart_delay_in_ms; | |
307 crash_window_in_ms_ = crash_window_in_ms; | |
308 } | |
309 | |
228 std::vector<BackgroundContents*> | 310 std::vector<BackgroundContents*> |
229 BackgroundContentsService::GetBackgroundContents() const | 311 BackgroundContentsService::GetBackgroundContents() const |
230 { | 312 { |
231 std::vector<BackgroundContents*> contents; | 313 std::vector<BackgroundContents*> contents; |
232 for (BackgroundContentsMap::const_iterator it = contents_map_.begin(); | 314 for (BackgroundContentsMap::const_iterator it = contents_map_.begin(); |
233 it != contents_map_.end(); ++it) | 315 it != contents_map_.end(); ++it) |
234 contents.push_back(it->second.contents); | 316 contents.push_back(it->second.contents); |
235 return contents; | 317 return contents; |
236 } | 318 } |
237 | 319 |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
343 // Now load the manifest-specified background page. If service isn't | 425 // Now load the manifest-specified background page. If service isn't |
344 // ready, then the background page will be loaded from the | 426 // ready, then the background page will be loaded from the |
345 // EXTENSIONS_READY callback. | 427 // EXTENSIONS_READY callback. |
346 LoadBackgroundContents(profile, | 428 LoadBackgroundContents(profile, |
347 BackgroundInfo::GetBackgroundURL(extension), | 429 BackgroundInfo::GetBackgroundURL(extension), |
348 ASCIIToUTF16("background"), | 430 ASCIIToUTF16("background"), |
349 UTF8ToUTF16(extension->id())); | 431 UTF8ToUTF16(extension->id())); |
350 } | 432 } |
351 } | 433 } |
352 | 434 |
353 // Remove any "This extension has crashed" balloons. | 435 // Remove any "app/extension has crashed" balloons. |
354 ScheduleCloseBalloon(extension->id()); | 436 ScheduleCloseCrashBalloon(extension->id()); |
355 SendChangeNotification(profile); | 437 SendChangeNotification(profile); |
356 break; | 438 break; |
357 } | 439 } |
358 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: | 440 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: |
359 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: { | 441 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: { |
360 Profile* profile = content::Source<Profile>(source).ptr(); | 442 Profile* profile = content::Source<Profile>(source).ptr(); |
361 const Extension* extension = NULL; | 443 const Extension* extension = NULL; |
362 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) { | 444 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) { |
363 BackgroundContents* bg = | 445 BackgroundContents* bg = |
364 content::Details<BackgroundContents>(details).ptr(); | 446 content::Details<BackgroundContents>(details).ptr(); |
365 std::string extension_id = UTF16ToASCII( | 447 std::string extension_id = UTF16ToASCII( |
366 BackgroundContentsServiceFactory::GetForProfile(profile)-> | 448 BackgroundContentsServiceFactory::GetForProfile(profile)-> |
367 GetParentApplicationId(bg)); | 449 GetParentApplicationId(bg)); |
368 extension = | 450 extension = |
369 extensions::ExtensionSystem::Get(profile)->extension_service()-> | 451 extensions::ExtensionSystem::Get(profile)->extension_service()-> |
370 GetExtensionById(extension_id, false); | 452 GetExtensionById(extension_id, false); |
371 } else { | 453 } else { |
372 extensions::ExtensionHost* extension_host = | 454 extensions::ExtensionHost* extension_host = |
373 content::Details<extensions::ExtensionHost>(details).ptr(); | 455 content::Details<extensions::ExtensionHost>(details).ptr(); |
374 extension = extension_host->extension(); | 456 extension = extension_host->extension(); |
375 } | 457 } |
376 if (!extension) | 458 if (!extension) |
377 break; | 459 break; |
378 | 460 |
379 // When an extension crashes, EXTENSION_PROCESS_TERMINATED is followed by | 461 const bool force_installed = extension->location() == |
380 // an EXTENSION_UNLOADED notification. This UNLOADED signal causes all the | 462 extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD; |
381 // notifications for this extension to be cancelled by | 463 if (!force_installed) { |
382 // DesktopNotificationService. For this reason, instead of showing the | 464 // When an extension crashes, EXTENSION_PROCESS_TERMINATED is followed |
383 // balloon right now, we schedule it to show a little later. | 465 // by an EXTENSION_UNLOADED notification. This UNLOADED signal causes |
384 base::MessageLoop::current()->PostTask( | 466 // all the notifications for this extension to be cancelled by |
385 FROM_HERE, base::Bind(&ShowBalloon, extension, profile)); | 467 // DesktopNotificationService. For this reason, we post the crash |
468 // handling code as a task here so that it is not executed before this | |
469 // event. | |
470 base::MessageLoop::current()->PostTask( | |
471 FROM_HERE, base::Bind(&ShowBalloon, extension, profile, false)); | |
472 } else { | |
473 // Restart the extension; notify user if crash recurs frequently. | |
474 RestartForceInstalledExtensionOnCrash(extension, profile); | |
475 } | |
386 break; | 476 break; |
387 } | 477 } |
388 case chrome::NOTIFICATION_EXTENSION_UNLOADED: | 478 case chrome::NOTIFICATION_EXTENSION_UNLOADED: |
389 switch (content::Details<UnloadedExtensionInfo>(details)->reason) { | 479 switch (content::Details<UnloadedExtensionInfo>(details)->reason) { |
390 case extension_misc::UNLOAD_REASON_DISABLE: // Fall through. | 480 case extension_misc::UNLOAD_REASON_DISABLE: // Fall through. |
391 case extension_misc::UNLOAD_REASON_TERMINATE: // Fall through. | 481 case extension_misc::UNLOAD_REASON_TERMINATE: // Fall through. |
392 case extension_misc::UNLOAD_REASON_UNINSTALL: // Fall through. | 482 case extension_misc::UNLOAD_REASON_UNINSTALL: // Fall through. |
393 case extension_misc::UNLOAD_REASON_BLACKLIST: | 483 case extension_misc::UNLOAD_REASON_BLACKLIST: |
394 ShutdownAssociatedBackgroundContents( | 484 ShutdownAssociatedBackgroundContents( |
395 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> | 485 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> |
(...skipping 16 matching lines...) Expand all Loading... | |
412 default: | 502 default: |
413 NOTREACHED(); | 503 NOTREACHED(); |
414 ShutdownAssociatedBackgroundContents( | 504 ShutdownAssociatedBackgroundContents( |
415 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> | 505 ASCIIToUTF16(content::Details<UnloadedExtensionInfo>(details)-> |
416 extension->id())); | 506 extension->id())); |
417 break; | 507 break; |
418 } | 508 } |
419 break; | 509 break; |
420 | 510 |
421 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { | 511 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: { |
422 // Remove any "This extension has crashed" balloons. | 512 const std::string& extension_id = |
423 ScheduleCloseBalloon( | 513 content::Details<const Extension>(details).ptr()->id(); |
424 content::Details<const Extension>(details).ptr()->id()); | 514 // Remove any balloons shown for this app/extension. |
515 ScheduleCloseBalloons(extension_id); | |
516 misbehaving_extensions_.erase(extension_id); | |
517 extension_crashlog_map_.erase(extension_id); | |
425 break; | 518 break; |
426 } | 519 } |
427 | 520 |
428 default: | 521 default: |
429 NOTREACHED(); | 522 NOTREACHED(); |
430 break; | 523 break; |
431 } | 524 } |
432 } | 525 } |
433 | 526 |
527 void BackgroundContentsService::RestartForceInstalledExtensionOnCrash( | |
528 const Extension* extension, Profile* profile) { | |
529 const std::string& extension_id = extension->id(); | |
530 const bool already_notified = misbehaving_extensions_.find(extension_id) != | |
531 misbehaving_extensions_.end(); | |
532 std::queue<base::TimeTicks>& crashes = extension_crashlog_map_[extension_id]; | |
533 const base::TimeDelta recent_time_window = | |
534 base::TimeDelta::FromMilliseconds(kMisbehaveCrashCountThreshold * | |
535 (restart_delay_in_ms_ + crash_window_in_ms_)); | |
536 if (!already_notified) { | |
537 // Show a notification if the threshold number of crashes has occurred | |
538 // within a recent time period. | |
539 const bool should_notify = | |
540 crashes.size() == kMisbehaveCrashCountThreshold - 1 && | |
541 base::TimeTicks::Now() - crashes.front() < recent_time_window; | |
542 if (should_notify) { | |
543 base::MessageLoop::current()->PostTask(FROM_HERE, | |
544 base::Bind(&ShowBalloon, extension, profile, true)); | |
545 misbehaving_extensions_.insert(extension_id); | |
546 extension_crashlog_map_.erase(extension_id); | |
547 } else { | |
548 while (!crashes.empty() && | |
549 base::TimeTicks::Now() - crashes.front() > recent_time_window) { | |
550 // Remove old timestamps. | |
551 crashes.pop(); | |
552 } | |
553 crashes.push(base::TimeTicks::Now()); | |
554 if (crashes.size() == kMisbehaveCrashCountThreshold) | |
555 crashes.pop(); | |
556 } | |
557 } | |
558 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
559 base::Bind(&BackgroundContentsService::ReloadExtension, | |
560 weak_factory_.GetWeakPtr(), extension_id, profile), | |
Andrew T Wilson (Slow)
2013/08/28 17:37:35
Does ReloadExtension() actually need to be a membe
bartfab (slow)
2013/08/28 17:43:00
If ReloadExtension() is not a member function, it
anitawoodruff
2013/08/29 12:02:36
On Bartosz' suggestion, I have now avoided using a
| |
561 base::TimeDelta::FromMilliseconds(restart_delay_in_ms_)); | |
562 } | |
563 | |
564 void BackgroundContentsService::ReloadExtension( | |
565 const std::string& extension_id, Profile* profile) { | |
566 extensions::ExtensionSystem* extension_system = | |
567 extensions::ExtensionSystem::Get(profile); | |
568 if (!extension_system || !extension_system->extension_service()) | |
569 return; | |
570 if (!extension_system->extension_service()-> | |
571 GetTerminatedExtension(extension_id)) { | |
572 // Policy has changed. The app/extension is no longer force-installed. | |
573 return; | |
574 } | |
575 extension_system->extension_service()->ReloadExtension(extension_id); | |
576 } | |
577 | |
434 // Loads all background contents whose urls have been stored in prefs. | 578 // Loads all background contents whose urls have been stored in prefs. |
435 void BackgroundContentsService::LoadBackgroundContentsFromPrefs( | 579 void BackgroundContentsService::LoadBackgroundContentsFromPrefs( |
436 Profile* profile) { | 580 Profile* profile) { |
437 if (!prefs_) | 581 if (!prefs_) |
438 return; | 582 return; |
439 const DictionaryValue* contents = | 583 const DictionaryValue* contents = |
440 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents); | 584 prefs_->GetDictionary(prefs::kRegisteredBackgroundContents); |
441 if (!contents) | 585 if (!contents) |
442 return; | 586 return; |
443 ExtensionService* extensions_service = | 587 ExtensionService* extensions_service = |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
646 } | 790 } |
647 | 791 |
648 void BackgroundContentsService::BackgroundContentsOpened( | 792 void BackgroundContentsService::BackgroundContentsOpened( |
649 BackgroundContentsOpenedDetails* details) { | 793 BackgroundContentsOpenedDetails* details) { |
650 // Add the passed object to our list. Should not already be tracked. | 794 // Add the passed object to our list. Should not already be tracked. |
651 DCHECK(!IsTracked(details->contents)); | 795 DCHECK(!IsTracked(details->contents)); |
652 DCHECK(!details->application_id.empty()); | 796 DCHECK(!details->application_id.empty()); |
653 contents_map_[details->application_id].contents = details->contents; | 797 contents_map_[details->application_id].contents = details->contents; |
654 contents_map_[details->application_id].frame_name = details->frame_name; | 798 contents_map_[details->application_id].frame_name = details->frame_name; |
655 | 799 |
656 ScheduleCloseBalloon(UTF16ToASCII(details->application_id)); | 800 ScheduleCloseCrashBalloon(UTF16ToASCII(details->application_id)); |
657 } | 801 } |
658 | 802 |
659 // Used by test code and debug checks to verify whether a given | 803 // Used by test code and debug checks to verify whether a given |
660 // BackgroundContents is being tracked by this instance. | 804 // BackgroundContents is being tracked by this instance. |
661 bool BackgroundContentsService::IsTracked( | 805 bool BackgroundContentsService::IsTracked( |
662 BackgroundContents* background_contents) const { | 806 BackgroundContents* background_contents) const { |
663 return !GetParentApplicationId(background_contents).empty(); | 807 return !GetParentApplicationId(background_contents).empty(); |
664 } | 808 } |
665 | 809 |
666 void BackgroundContentsService::BackgroundContentsShutdown( | 810 void BackgroundContentsService::BackgroundContentsShutdown( |
(...skipping 27 matching lines...) Expand all Loading... | |
694 bool user_gesture, | 838 bool user_gesture, |
695 bool* was_blocked) { | 839 bool* was_blocked) { |
696 Browser* browser = chrome::FindLastActiveWithProfile( | 840 Browser* browser = chrome::FindLastActiveWithProfile( |
697 Profile::FromBrowserContext(new_contents->GetBrowserContext()), | 841 Profile::FromBrowserContext(new_contents->GetBrowserContext()), |
698 chrome::GetActiveDesktop()); | 842 chrome::GetActiveDesktop()); |
699 if (browser) { | 843 if (browser) { |
700 chrome::AddWebContents(browser, NULL, new_contents, disposition, | 844 chrome::AddWebContents(browser, NULL, new_contents, disposition, |
701 initial_pos, user_gesture, was_blocked); | 845 initial_pos, user_gesture, was_blocked); |
702 } | 846 } |
703 } | 847 } |
OLD | NEW |