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