| 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" |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 // <appid_2>: { "url": <url2>, "name": <frame_name> }, | 254 // <appid_2>: { "url": <url2>, "name": <frame_name> }, |
| 255 // ... etc ... | 255 // ... etc ... |
| 256 // } | 256 // } |
| 257 const char kUrlKey[] = "url"; | 257 const char kUrlKey[] = "url"; |
| 258 const char kFrameNameKey[] = "name"; | 258 const char kFrameNameKey[] = "name"; |
| 259 | 259 |
| 260 int BackgroundContentsService::restart_delay_in_ms_ = 3000; // 3 seconds. | 260 int BackgroundContentsService::restart_delay_in_ms_ = 3000; // 3 seconds. |
| 261 | 261 |
| 262 BackgroundContentsService::BackgroundContentsService( | 262 BackgroundContentsService::BackgroundContentsService( |
| 263 Profile* profile, const CommandLine* command_line) | 263 Profile* profile, const CommandLine* command_line) |
| 264 : prefs_(NULL) { | 264 : prefs_(NULL), extension_registry_observer_(this) { |
| 265 // Don't load/store preferences if the parent profile is incognito. | 265 // Don't load/store preferences if the parent profile is incognito. |
| 266 if (!profile->IsOffTheRecord()) | 266 if (!profile->IsOffTheRecord()) |
| 267 prefs_ = profile->GetPrefs(); | 267 prefs_ = profile->GetPrefs(); |
| 268 | 268 |
| 269 // Listen for events to tell us when to load/unload persisted background | 269 // Listen for events to tell us when to load/unload persisted background |
| 270 // contents. | 270 // contents. |
| 271 StartObserving(profile); | 271 StartObserving(profile); |
| 272 } | 272 } |
| 273 | 273 |
| 274 BackgroundContentsService::~BackgroundContentsService() { | 274 BackgroundContentsService::~BackgroundContentsService() { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 // Stop tracking BackgroundContents when they have been deleted (happens | 322 // Stop tracking BackgroundContents when they have been deleted (happens |
| 323 // during shutdown or if the render process dies). | 323 // during shutdown or if the render process dies). |
| 324 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, | 324 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, |
| 325 content::Source<Profile>(profile)); | 325 content::Source<Profile>(profile)); |
| 326 | 326 |
| 327 // Track when the BackgroundContents navigates to a new URL so we can update | 327 // Track when the BackgroundContents navigates to a new URL so we can update |
| 328 // our persisted information as appropriate. | 328 // our persisted information as appropriate. |
| 329 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, | 329 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, |
| 330 content::Source<Profile>(profile)); | 330 content::Source<Profile>(profile)); |
| 331 | 331 |
| 332 // Listen for new extension installs so that we can load any associated | |
| 333 // background page. | |
| 334 registrar_.Add(this, | |
| 335 chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, | |
| 336 content::Source<Profile>(profile)); | |
| 337 | |
| 338 // Track when the extensions crash so that the user can be notified | 332 // Track when the extensions crash so that the user can be notified |
| 339 // about it, and the crashed contents can be restarted. | 333 // about it, and the crashed contents can be restarted. |
| 340 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, | 334 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, |
| 341 content::Source<Profile>(profile)); | 335 content::Source<Profile>(profile)); |
| 342 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED, | 336 registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED, |
| 343 content::Source<Profile>(profile)); | 337 content::Source<Profile>(profile)); |
| 344 | 338 |
| 345 // Listen for extensions to be unloaded so we can shutdown associated | 339 // Listen for extension uninstall, load, unloaded notification. |
| 346 // BackgroundContents. | 340 extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile)); |
| 347 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
| 348 content::Source<Profile>(profile)); | |
| 349 | |
| 350 // Make sure the extension-crash balloons are removed when the extension is | |
| 351 // uninstalled/reloaded. We cannot do this from UNLOADED since a crashed | |
| 352 // extension is unloaded immediately after the crash, not when user reloads or | |
| 353 // uninstalls the extension. | |
| 354 registrar_.Add(this, | |
| 355 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, | |
| 356 content::Source<Profile>(profile)); | |
| 357 } | 341 } |
| 358 | 342 |
| 359 void BackgroundContentsService::Observe( | 343 void BackgroundContentsService::Observe( |
| 360 int type, | 344 int type, |
| 361 const content::NotificationSource& source, | 345 const content::NotificationSource& source, |
| 362 const content::NotificationDetails& details) { | 346 const content::NotificationDetails& details) { |
| 363 switch (type) { | 347 switch (type) { |
| 364 case chrome::NOTIFICATION_EXTENSIONS_READY: { | 348 case chrome::NOTIFICATION_EXTENSIONS_READY: { |
| 365 Profile* profile = content::Source<Profile>(source).ptr(); | 349 Profile* profile = content::Source<Profile>(source).ptr(); |
| 366 LoadBackgroundContentsFromManifests(profile); | 350 LoadBackgroundContentsFromManifests(profile); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 394 // extension_service can be NULL when running tests. | 378 // extension_service can be NULL when running tests. |
| 395 if (extension_service) { | 379 if (extension_service) { |
| 396 const Extension* extension = extension_service->GetExtensionById( | 380 const Extension* extension = extension_service->GetExtensionById( |
| 397 base::UTF16ToUTF8(appid), false); | 381 base::UTF16ToUTF8(appid), false); |
| 398 if (extension && BackgroundInfo::HasBackgroundPage(extension)) | 382 if (extension && BackgroundInfo::HasBackgroundPage(extension)) |
| 399 break; | 383 break; |
| 400 } | 384 } |
| 401 RegisterBackgroundContents(bgcontents); | 385 RegisterBackgroundContents(bgcontents); |
| 402 break; | 386 break; |
| 403 } | 387 } |
| 404 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { | |
| 405 const Extension* extension = | |
| 406 content::Details<const Extension>(details).ptr(); | |
| 407 Profile* profile = content::Source<Profile>(source).ptr(); | |
| 408 if (extension->is_hosted_app() && | |
| 409 BackgroundInfo::HasBackgroundPage(extension)) { | |
| 410 // If there is a background page specified in the manifest for a hosted | |
| 411 // app, then blow away registered urls in the pref. | |
| 412 ShutdownAssociatedBackgroundContents( | |
| 413 base::ASCIIToUTF16(extension->id())); | |
| 414 | |
| 415 ExtensionService* service = | |
| 416 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 417 if (service && service->is_ready()) { | |
| 418 // Now load the manifest-specified background page. If service isn't | |
| 419 // ready, then the background page will be loaded from the | |
| 420 // EXTENSIONS_READY callback. | |
| 421 LoadBackgroundContents(profile, | |
| 422 BackgroundInfo::GetBackgroundURL(extension), | |
| 423 base::ASCIIToUTF16("background"), | |
| 424 base::UTF8ToUTF16(extension->id())); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 // Close the crash notification balloon for the app/extension, if any. | |
| 429 ScheduleCloseBalloon(extension->id()); | |
| 430 SendChangeNotification(profile); | |
| 431 break; | |
| 432 } | |
| 433 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: | 388 case chrome::NOTIFICATION_EXTENSION_PROCESS_TERMINATED: |
| 434 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: { | 389 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED: { |
| 435 Profile* profile = content::Source<Profile>(source).ptr(); | 390 Profile* profile = content::Source<Profile>(source).ptr(); |
| 436 const Extension* extension = NULL; | 391 const Extension* extension = NULL; |
| 437 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) { | 392 if (type == chrome::NOTIFICATION_BACKGROUND_CONTENTS_TERMINATED) { |
| 438 BackgroundContents* bg = | 393 BackgroundContents* bg = |
| 439 content::Details<BackgroundContents>(details).ptr(); | 394 content::Details<BackgroundContents>(details).ptr(); |
| 440 std::string extension_id = base::UTF16ToASCII( | 395 std::string extension_id = base::UTF16ToASCII( |
| 441 BackgroundContentsServiceFactory::GetForProfile(profile)-> | 396 BackgroundContentsServiceFactory::GetForProfile(profile)-> |
| 442 GetParentApplicationId(bg)); | 397 GetParentApplicationId(bg)); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 455 extensions::Manifest::IsComponentLocation(extension->location()) || | 410 extensions::Manifest::IsComponentLocation(extension->location()) || |
| 456 extensions::Manifest::IsPolicyLocation(extension->location()); | 411 extensions::Manifest::IsPolicyLocation(extension->location()); |
| 457 if (!force_installed) { | 412 if (!force_installed) { |
| 458 ShowBalloon(extension, profile); | 413 ShowBalloon(extension, profile); |
| 459 } else { | 414 } else { |
| 460 // Restart the extension. | 415 // Restart the extension. |
| 461 RestartForceInstalledExtensionOnCrash(extension, profile); | 416 RestartForceInstalledExtensionOnCrash(extension, profile); |
| 462 } | 417 } |
| 463 break; | 418 break; |
| 464 } | 419 } |
| 465 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: | |
| 466 switch (content::Details<UnloadedExtensionInfo>(details)->reason) { | |
| 467 case UnloadedExtensionInfo::REASON_DISABLE: // Fall through. | |
| 468 case UnloadedExtensionInfo::REASON_TERMINATE: // Fall through. | |
| 469 case UnloadedExtensionInfo::REASON_UNINSTALL: // Fall through. | |
| 470 case UnloadedExtensionInfo::REASON_BLACKLIST: // Fall through. | |
| 471 case UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN: | |
| 472 ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16( | |
| 473 content::Details<UnloadedExtensionInfo>(details)-> | |
| 474 extension->id())); | |
| 475 SendChangeNotification(content::Source<Profile>(source).ptr()); | |
| 476 break; | |
| 477 case UnloadedExtensionInfo::REASON_UPDATE: { | |
| 478 // If there is a manifest specified background page, then shut it down | |
| 479 // here, since if the updated extension still has the background page, | |
| 480 // then it will be loaded from LOADED callback. Otherwise, leave | |
| 481 // BackgroundContents in place. | |
| 482 // We don't call SendChangeNotification here - it will be generated | |
| 483 // from the LOADED callback. | |
| 484 const Extension* extension = | |
| 485 content::Details<UnloadedExtensionInfo>(details)->extension; | |
| 486 if (BackgroundInfo::HasBackgroundPage(extension)) { | |
| 487 ShutdownAssociatedBackgroundContents( | |
| 488 base::ASCIIToUTF16(extension->id())); | |
| 489 } | |
| 490 break; | |
| 491 } | |
| 492 default: | |
| 493 NOTREACHED(); | |
| 494 ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16( | |
| 495 content::Details<UnloadedExtensionInfo>(details)-> | |
| 496 extension->id())); | |
| 497 break; | |
| 498 } | |
| 499 break; | |
| 500 | |
| 501 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED: { | |
| 502 // Close the crash notification balloon for the app/extension, if any. | |
| 503 ScheduleCloseBalloon( | |
| 504 content::Details<const Extension>(details).ptr()->id()); | |
| 505 break; | |
| 506 } | |
| 507 | 420 |
| 508 default: | 421 default: |
| 509 NOTREACHED(); | 422 NOTREACHED(); |
| 510 break; | 423 break; |
| 511 } | 424 } |
| 512 } | 425 } |
| 513 | 426 |
| 427 void BackgroundContentsService::OnExtensionLoaded( |
| 428 content::BrowserContext* browser_context, |
| 429 const extensions::Extension* extension) { |
| 430 Profile* profile = Profile::FromBrowserContext(browser_context); |
| 431 if (extension->is_hosted_app() && |
| 432 BackgroundInfo::HasBackgroundPage(extension)) { |
| 433 // If there is a background page specified in the manifest for a hosted |
| 434 // app, then blow away registered urls in the pref. |
| 435 ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16(extension->id())); |
| 436 |
| 437 ExtensionService* service = |
| 438 extensions::ExtensionSystem::Get(browser_context)->extension_service(); |
| 439 if (service && service->is_ready()) { |
| 440 // Now load the manifest-specified background page. If service isn't |
| 441 // ready, then the background page will be loaded from the |
| 442 // EXTENSIONS_READY callback. |
| 443 LoadBackgroundContents(profile, |
| 444 BackgroundInfo::GetBackgroundURL(extension), |
| 445 base::ASCIIToUTF16("background"), |
| 446 base::UTF8ToUTF16(extension->id())); |
| 447 } |
| 448 } |
| 449 |
| 450 // Close the crash notification balloon for the app/extension, if any. |
| 451 ScheduleCloseBalloon(extension->id()); |
| 452 SendChangeNotification(profile); |
| 453 } |
| 454 |
| 455 void BackgroundContentsService::OnExtensionUnloaded( |
| 456 content::BrowserContext* browser_context, |
| 457 const extensions::Extension* extension, |
| 458 extensions::UnloadedExtensionInfo::Reason reason) { |
| 459 switch (reason) { |
| 460 case UnloadedExtensionInfo::REASON_DISABLE: // Fall through. |
| 461 case UnloadedExtensionInfo::REASON_TERMINATE: // Fall through. |
| 462 case UnloadedExtensionInfo::REASON_UNINSTALL: // Fall through. |
| 463 case UnloadedExtensionInfo::REASON_BLACKLIST: // Fall through. |
| 464 case UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN: |
| 465 ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16(extension->id())); |
| 466 SendChangeNotification(Profile::FromBrowserContext(browser_context)); |
| 467 break; |
| 468 case UnloadedExtensionInfo::REASON_UPDATE: { |
| 469 // If there is a manifest specified background page, then shut it down |
| 470 // here, since if the updated extension still has the background page, |
| 471 // then it will be loaded from LOADED callback. Otherwise, leave |
| 472 // BackgroundContents in place. |
| 473 // We don't call SendChangeNotification here - it will be generated |
| 474 // from the LOADED callback. |
| 475 if (BackgroundInfo::HasBackgroundPage(extension)) { |
| 476 ShutdownAssociatedBackgroundContents( |
| 477 base::ASCIIToUTF16(extension->id())); |
| 478 } |
| 479 break; |
| 480 } |
| 481 default: |
| 482 NOTREACHED(); |
| 483 ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16(extension->id())); |
| 484 break; |
| 485 } |
| 486 } |
| 487 |
| 488 void BackgroundContentsService::OnExtensionUninstalled( |
| 489 content::BrowserContext* browser_context, |
| 490 const extensions::Extension* extension, |
| 491 extensions::UninstallReason reason) { |
| 492 // Make sure the extension-crash balloons are removed when the extension is |
| 493 // uninstalled/reloaded. We cannot do this from UNLOADED since a crashed |
| 494 // extension is unloaded immediately after the crash, not when user reloads or |
| 495 // uninstalls the extension. |
| 496 ScheduleCloseBalloon(extension->id()); |
| 497 } |
| 498 |
| 514 void BackgroundContentsService::RestartForceInstalledExtensionOnCrash( | 499 void BackgroundContentsService::RestartForceInstalledExtensionOnCrash( |
| 515 const Extension* extension, | 500 const Extension* extension, |
| 516 Profile* profile) { | 501 Profile* profile) { |
| 517 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, | 502 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 518 base::Bind(&ReloadExtension, extension->id(), profile), | 503 base::Bind(&ReloadExtension, extension->id(), profile), |
| 519 base::TimeDelta::FromMilliseconds(restart_delay_in_ms_)); | 504 base::TimeDelta::FromMilliseconds(restart_delay_in_ms_)); |
| 520 } | 505 } |
| 521 | 506 |
| 522 // Loads all background contents whose urls have been stored in prefs. | 507 // Loads all background contents whose urls have been stored in prefs. |
| 523 void BackgroundContentsService::LoadBackgroundContentsFromPrefs( | 508 void BackgroundContentsService::LoadBackgroundContentsFromPrefs( |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 786 bool user_gesture, | 771 bool user_gesture, |
| 787 bool* was_blocked) { | 772 bool* was_blocked) { |
| 788 Browser* browser = chrome::FindLastActiveWithProfile( | 773 Browser* browser = chrome::FindLastActiveWithProfile( |
| 789 Profile::FromBrowserContext(new_contents->GetBrowserContext()), | 774 Profile::FromBrowserContext(new_contents->GetBrowserContext()), |
| 790 chrome::GetActiveDesktop()); | 775 chrome::GetActiveDesktop()); |
| 791 if (browser) { | 776 if (browser) { |
| 792 chrome::AddWebContents(browser, NULL, new_contents, disposition, | 777 chrome::AddWebContents(browser, NULL, new_contents, disposition, |
| 793 initial_pos, user_gesture, was_blocked); | 778 initial_pos, user_gesture, was_blocked); |
| 794 } | 779 } |
| 795 } | 780 } |
| OLD | NEW |