Commit c54a52fa authored by Sam McNally's avatar Sam McNally Committed by Commit Bot

Allow background mode while an extension is communicating with a native app.

Add support for enabling background mode while an extension capable of
accepting inbound native messaging launch requests is active. Allow
cooperating native apps to launch Chrome without a browser window when
initiating communications with a cooperating extension.

Bug: 967262
Change-Id: I61353c0becb341ad3e08a5a8635e171f9f9bd59b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1898894Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Reviewed-by: default avatarDrew Wilson <atwilson@chromium.org>
Reviewed-by: default avatarTommy Martino <tmartino@chromium.org>
Reviewed-by: default avatarSergei Datsenko <dats@chromium.org>
Commit-Queue: Sam McNally <sammc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#716480}
parent 4dab5cc0
...@@ -19,10 +19,12 @@ ...@@ -19,10 +19,12 @@
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_constants.h"
#include "components/crx_file/id_util.h" #include "components/crx_file/id_util.h"
#include "content/public/browser/notification_details.h" #include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h" #include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
...@@ -85,9 +87,14 @@ void GetServiceApplications(extensions::ExtensionService* service, ...@@ -85,9 +87,14 @@ void GetServiceApplications(extensions::ExtensionService* service,
ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile()); ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
const ExtensionSet& enabled_extensions = registry->enabled_extensions(); const ExtensionSet& enabled_extensions = registry->enabled_extensions();
auto* process_manager = extensions::ProcessManager::Get(service->profile());
for (const auto& extension : enabled_extensions) { for (const auto& extension : enabled_extensions) {
if (BackgroundApplicationListModel::IsBackgroundApp(*extension, if (BackgroundApplicationListModel::IsPersistentBackgroundApp(
service->profile())) { *extension, service->profile()) ||
(BackgroundApplicationListModel::IsTransientBackgroundApp(
*extension, service->profile()) &&
process_manager->GetBackgroundHostForExtension(extension->id()))) {
applications_result->push_back(extension); applications_result->push_back(extension);
} }
} }
...@@ -96,8 +103,8 @@ void GetServiceApplications(extensions::ExtensionService* service, ...@@ -96,8 +103,8 @@ void GetServiceApplications(extensions::ExtensionService* service,
// crashed doesn't mean we should ignore it). // crashed doesn't mean we should ignore it).
const ExtensionSet& terminated_extensions = registry->terminated_extensions(); const ExtensionSet& terminated_extensions = registry->terminated_extensions();
for (const auto& extension : terminated_extensions) { for (const auto& extension : terminated_extensions) {
if (BackgroundApplicationListModel::IsBackgroundApp(*extension, if (BackgroundApplicationListModel::IsPersistentBackgroundApp(
service->profile())) { *extension, service->profile())) {
applications_result->push_back(extension); applications_result->push_back(extension);
} }
} }
...@@ -230,8 +237,9 @@ int BackgroundApplicationListModel::GetPosition( ...@@ -230,8 +237,9 @@ int BackgroundApplicationListModel::GetPosition(
} }
// static // static
bool BackgroundApplicationListModel::IsBackgroundApp( bool BackgroundApplicationListModel::IsPersistentBackgroundApp(
const Extension& extension, Profile* profile) { const Extension& extension,
Profile* profile) {
// An extension is a "background app" if it has the "background API" // An extension is a "background app" if it has the "background API"
// permission, and meets one of the following criteria: // permission, and meets one of the following criteria:
// 1) It is an extension (not a hosted app). // 1) It is an extension (not a hosted app).
...@@ -269,6 +277,25 @@ bool BackgroundApplicationListModel::IsBackgroundApp( ...@@ -269,6 +277,25 @@ bool BackgroundApplicationListModel::IsBackgroundApp(
return false; return false;
} }
// static
bool BackgroundApplicationListModel::IsTransientBackgroundApp(
const Extension& extension,
Profile* profile) {
return base::FeatureList::IsEnabled(features::kOnConnectNative) &&
extension.permissions_data()->HasAPIPermission(
APIPermission::kTransientBackground) &&
extensions::BackgroundInfo::HasLazyBackgroundPage(&extension);
}
bool BackgroundApplicationListModel::HasPersistentBackgroundApps() const {
for (auto& extension : extensions_) {
if (IsPersistentBackgroundApp(*extension, profile_)) {
return true;
}
}
return false;
}
void BackgroundApplicationListModel::Observe( void BackgroundApplicationListModel::Observe(
int type, int type,
const content::NotificationSource& source, const content::NotificationSource& source,
...@@ -321,12 +348,16 @@ void BackgroundApplicationListModel::OnExtensionSystemReady() { ...@@ -321,12 +348,16 @@ void BackgroundApplicationListModel::OnExtensionSystemReady() {
background_contents_service_observer_.Add( background_contents_service_observer_.Add(
BackgroundContentsServiceFactory::GetForProfile(profile_)); BackgroundContentsServiceFactory::GetForProfile(profile_));
if (base::FeatureList::IsEnabled(features::kOnConnectNative))
process_manager_observer_.Add(extensions::ProcessManager::Get(profile_));
startup_done_ = true; startup_done_ = true;
} }
void BackgroundApplicationListModel::OnShutdown(ExtensionRegistry* registry) { void BackgroundApplicationListModel::OnShutdown(ExtensionRegistry* registry) {
DCHECK_EQ(ExtensionRegistry::Get(profile_), registry); DCHECK_EQ(ExtensionRegistry::Get(profile_), registry);
extension_registry_observer_.Remove(registry); extension_registry_observer_.Remove(registry);
process_manager_observer_.RemoveAll();
} }
void BackgroundApplicationListModel::OnBackgroundContentsServiceChanged() { void BackgroundApplicationListModel::OnBackgroundContentsServiceChanged() {
...@@ -341,17 +372,18 @@ void BackgroundApplicationListModel::OnExtensionPermissionsUpdated( ...@@ -341,17 +372,18 @@ void BackgroundApplicationListModel::OnExtensionPermissionsUpdated(
const Extension* extension, const Extension* extension,
UpdatedExtensionPermissionsInfo::Reason reason, UpdatedExtensionPermissionsInfo::Reason reason,
const PermissionSet& permissions) { const PermissionSet& permissions) {
if (permissions.HasAPIPermission(APIPermission::kBackground)) { if (permissions.HasAPIPermission(APIPermission::kBackground) ||
(base::FeatureList::IsEnabled(features::kOnConnectNative) &&
permissions.HasAPIPermission(APIPermission::kTransientBackground))) {
switch (reason) { switch (reason) {
case UpdatedExtensionPermissionsInfo::ADDED: case UpdatedExtensionPermissionsInfo::ADDED:
DCHECK(IsBackgroundApp(*extension, profile_));
Update();
AssociateApplicationData(extension);
break;
case UpdatedExtensionPermissionsInfo::REMOVED: case UpdatedExtensionPermissionsInfo::REMOVED:
DCHECK(!IsBackgroundApp(*extension, profile_));
Update(); Update();
DissociateApplicationData(extension); if (IsBackgroundApp(*extension, profile_)) {
AssociateApplicationData(extension);
} else {
DissociateApplicationData(extension);
}
break; break;
default: default:
NOTREACHED(); NOTREACHED();
...@@ -391,3 +423,22 @@ void BackgroundApplicationListModel::Update() { ...@@ -391,3 +423,22 @@ void BackgroundApplicationListModel::Update() {
observer.OnApplicationListChanged(profile_); observer.OnApplicationListChanged(profile_);
} }
} }
void BackgroundApplicationListModel::OnBackgroundHostCreated(
extensions::ExtensionHost* host) {
if (IsTransientBackgroundApp(*host->extension(), profile_)) {
Update();
}
}
void BackgroundApplicationListModel::OnBackgroundHostClose(
const std::string& extension_id) {
auto* extension =
ExtensionRegistry::Get(profile_)->GetInstalledExtension(extension_id);
if (!extension || !IsTransientBackgroundApp(*extension, profile_)) {
return;
}
Update();
}
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "extensions/browser/extension_registry_observer.h" #include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_observer.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
class Profile; class Profile;
...@@ -38,7 +40,8 @@ class ExtensionRegistry; ...@@ -38,7 +40,8 @@ class ExtensionRegistry;
class BackgroundApplicationListModel class BackgroundApplicationListModel
: public content::NotificationObserver, : public content::NotificationObserver,
public extensions::ExtensionRegistryObserver, public extensions::ExtensionRegistryObserver,
public BackgroundContentsServiceObserver { public BackgroundContentsServiceObserver,
public extensions::ProcessManagerObserver {
public: public:
// Observer is informed of changes to the model. Users of the // Observer is informed of changes to the model. Users of the
// BackgroundApplicationListModel should anticipate that associated data, // BackgroundApplicationListModel should anticipate that associated data,
...@@ -83,7 +86,20 @@ class BackgroundApplicationListModel ...@@ -83,7 +86,20 @@ class BackgroundApplicationListModel
// Returns true if the passed extension is a background app. // Returns true if the passed extension is a background app.
static bool IsBackgroundApp(const extensions::Extension& extension, static bool IsBackgroundApp(const extensions::Extension& extension,
Profile* profile); Profile* profile) {
return IsPersistentBackgroundApp(extension, profile) ||
IsTransientBackgroundApp(extension, profile);
}
// Returns true if the passed extension is a persistent background app.
static bool IsPersistentBackgroundApp(const extensions::Extension& extension,
Profile* profile);
// Returns true if the passed extension is a transient background app.
// Transient background apps should only be treated as background apps while
// their background page is active.
static bool IsTransientBackgroundApp(const extensions::Extension& extension,
Profile* profile);
// Dissociate observer from this model. // Dissociate observer from this model.
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
...@@ -103,6 +119,8 @@ class BackgroundApplicationListModel ...@@ -103,6 +119,8 @@ class BackgroundApplicationListModel
// Returns true if all startup notifications have already been issued. // Returns true if all startup notifications have already been issued.
bool startup_done() const { return startup_done_; } bool startup_done() const { return startup_done_; }
bool HasPersistentBackgroundApps() const;
private: private:
// Contains data associated with a background application that is not // Contains data associated with a background application that is not
// represented by the Extension class. // represented by the Extension class.
...@@ -145,10 +163,6 @@ class BackgroundApplicationListModel ...@@ -145,10 +163,6 @@ class BackgroundApplicationListModel
// application, e.g. the Icon, has changed. // application, e.g. the Icon, has changed.
void SendApplicationDataChangedNotifications(); void SendApplicationDataChangedNotifications();
// Notifies observers that at least one background application has been added
// or removed.
void SendApplicationListChangedNotifications();
// Invoked by Observe for NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED. // Invoked by Observe for NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED.
void OnExtensionPermissionsUpdated( void OnExtensionPermissionsUpdated(
const extensions::Extension* extension, const extensions::Extension* extension,
...@@ -158,6 +172,10 @@ class BackgroundApplicationListModel ...@@ -158,6 +172,10 @@ class BackgroundApplicationListModel
// Refresh the list of background applications and generate notifications. // Refresh the list of background applications and generate notifications.
void Update(); void Update();
// ProcessManagerObserver:
void OnBackgroundHostCreated(extensions::ExtensionHost* host) override;
void OnBackgroundHostClose(const std::string& extension_id) override;
// Associates extension id strings with Application objects. // Associates extension id strings with Application objects.
std::map<std::string, std::unique_ptr<Application>> applications_; std::map<std::string, std::unique_ptr<Application>> applications_;
...@@ -175,6 +193,9 @@ class BackgroundApplicationListModel ...@@ -175,6 +193,9 @@ class BackgroundApplicationListModel
ScopedObserver<BackgroundContentsService, BackgroundContentsServiceObserver> ScopedObserver<BackgroundContentsService, BackgroundContentsServiceObserver>
background_contents_service_observer_{this}; background_contents_service_observer_{this};
ScopedObserver<extensions::ProcessManager, extensions::ProcessManagerObserver>
process_manager_observer_{this};
base::WeakPtrFactory<BackgroundApplicationListModel> weak_ptr_factory_{this}; base::WeakPtrFactory<BackgroundApplicationListModel> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationListModel); DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationListModel);
......
...@@ -130,14 +130,19 @@ Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() { ...@@ -130,14 +130,19 @@ Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
return BackgroundModeManager::GetBrowserWindowForProfile(profile_); return BackgroundModeManager::GetBrowserWindowForProfile(profile_);
} }
bool BackgroundModeManager::BackgroundModeData::HasBackgroundClient() const { bool BackgroundModeManager::BackgroundModeData::HasPersistentBackgroundClient()
const {
return applications_->HasPersistentBackgroundApps();
}
bool BackgroundModeManager::BackgroundModeData::HasAnyBackgroundClient() const {
return applications_->size() > 0; return applications_->size() > 0;
} }
void BackgroundModeManager::BackgroundModeData::BuildProfileMenu( void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
StatusIconMenuModel* menu, StatusIconMenuModel* menu,
StatusIconMenuModel* containing_menu) { StatusIconMenuModel* containing_menu) {
if (HasBackgroundClient()) { if (HasAnyBackgroundClient()) {
// Add a menu item for each application (extension). // Add a menu item for each application (extension).
for (const auto& application : *applications_) { for (const auto& application : *applications_) {
gfx::ImageSkia icon = applications_->GetIcon(application.get()); gfx::ImageSkia icon = applications_->GetIcon(application.get());
...@@ -205,10 +210,13 @@ BackgroundModeManager::BackgroundModeData::GetNewBackgroundApps() { ...@@ -205,10 +210,13 @@ BackgroundModeManager::BackgroundModeData::GetNewBackgroundApps() {
// Not found in our set yet - add it and maybe return as a previously // Not found in our set yet - add it and maybe return as a previously
// unseen extension. // unseen extension.
current_extensions_.insert(id); current_extensions_.insert(id);
// If this application has been newly loaded after the initial startup, // If this application has been newly loaded after the initial startup and
// notify the user. // this is a persistent background app, notify the user.
if (applications_->startup_done()) if (applications_->startup_done() &&
BackgroundApplicationListModel::IsPersistentBackgroundApp(
*application, profile_)) {
new_apps.insert(application.get()); new_apps.insert(application.get());
}
} }
} }
return new_apps; return new_apps;
...@@ -416,6 +424,7 @@ void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() { ...@@ -416,6 +424,7 @@ void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
bool enabled = IsBackgroundModePrefEnabled(); bool enabled = IsBackgroundModePrefEnabled();
UMA_HISTOGRAM_BOOLEAN("BackgroundMode.BackgroundModeEnabledPrefChanged", UMA_HISTOGRAM_BOOLEAN("BackgroundMode.BackgroundModeEnabledPrefChanged",
enabled); enabled);
UpdateEnableLaunchOnStartup();
if (enabled) if (enabled)
EnableBackgroundMode(); EnableBackgroundMode();
else else
...@@ -481,8 +490,8 @@ void BackgroundModeManager::OnProfileWillBeRemoved( ...@@ -481,8 +490,8 @@ void BackgroundModeManager::OnProfileWillBeRemoved(
background_mode_data_.erase(it); background_mode_data_.erase(it);
// If there are no background mode profiles any longer, then turn off // If there are no background mode profiles any longer, then turn off
// background mode. // background mode.
UpdateEnableLaunchOnStartup();
if (!ShouldBeInBackgroundMode()) { if (!ShouldBeInBackgroundMode()) {
EnableLaunchOnStartup(false);
EndBackgroundMode(); EndBackgroundMode();
} }
UpdateStatusTrayIconContextMenu(); UpdateStatusTrayIconContextMenu();
...@@ -633,7 +642,7 @@ void BackgroundModeManager::EnableBackgroundMode() { ...@@ -633,7 +642,7 @@ void BackgroundModeManager::EnableBackgroundMode() {
if (!in_background_mode_ && ShouldBeInBackgroundMode()) { if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
StartBackgroundMode(); StartBackgroundMode();
EnableLaunchOnStartup(true); UpdateEnableLaunchOnStartup();
} }
} }
...@@ -642,7 +651,6 @@ void BackgroundModeManager::DisableBackgroundMode() { ...@@ -642,7 +651,6 @@ void BackgroundModeManager::DisableBackgroundMode() {
// If background mode is currently enabled, turn it off. // If background mode is currently enabled, turn it off.
if (in_background_mode_) { if (in_background_mode_) {
EndBackgroundMode(); EndBackgroundMode();
EnableLaunchOnStartup(false);
} }
} }
...@@ -685,13 +693,14 @@ void BackgroundModeManager::OnClientsChanged( ...@@ -685,13 +693,14 @@ void BackgroundModeManager::OnClientsChanged(
ProfileAttributesEntry* entry; ProfileAttributesEntry* entry;
if (profile_storage_-> if (profile_storage_->
GetProfileAttributesWithPath(profile->GetPath(), &entry)) { GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
entry->SetBackgroundStatus(HasBackgroundClientForProfile(profile)); entry->SetBackgroundStatus(
HasPersistentBackgroundClientForProfile(profile));
} }
UpdateEnableLaunchOnStartup();
if (!ShouldBeInBackgroundMode()) { if (!ShouldBeInBackgroundMode()) {
// We've uninstalled our last background client, make sure we exit // We've uninstalled our last background client, make sure we exit
// background mode and no longer launch on startup. // background mode and no longer launch on startup.
EnableLaunchOnStartup(false);
EndBackgroundMode(); EndBackgroundMode();
} else { } else {
// We have at least one background client - make sure we're in background // We have at least one background client - make sure we're in background
...@@ -701,7 +710,6 @@ void BackgroundModeManager::OnClientsChanged( ...@@ -701,7 +710,6 @@ void BackgroundModeManager::OnClientsChanged(
// enabled. On Mac, the platform-specific code tracks whether the user // enabled. On Mac, the platform-specific code tracks whether the user
// has deleted a login item in the past, and if so, no login item will // has deleted a login item in the past, and if so, no login item will
// be created (to avoid overriding the specific user action). // be created (to avoid overriding the specific user action).
EnableLaunchOnStartup(true);
StartBackgroundMode(); StartBackgroundMode();
} }
...@@ -714,24 +722,32 @@ void BackgroundModeManager::OnClientsChanged( ...@@ -714,24 +722,32 @@ void BackgroundModeManager::OnClientsChanged(
} }
} }
bool BackgroundModeManager::HasBackgroundClient() const { bool BackgroundModeManager::HasPersistentBackgroundClient() const {
for (const auto& it : background_mode_data_) { for (const auto& it : background_mode_data_) {
if (it.second->HasBackgroundClient()) if (it.second->HasPersistentBackgroundClient())
return true; return true;
} }
return false; return false;
} }
bool BackgroundModeManager::HasBackgroundClientForProfile( bool BackgroundModeManager::HasAnyBackgroundClient() const {
for (const auto& it : background_mode_data_) {
if (it.second->HasAnyBackgroundClient())
return true;
}
return false;
}
bool BackgroundModeManager::HasPersistentBackgroundClientForProfile(
const Profile* profile) const { const Profile* profile) const {
BackgroundModeManager::BackgroundModeData* bmd = BackgroundModeManager::BackgroundModeData* bmd =
GetBackgroundModeData(profile); GetBackgroundModeData(profile);
return bmd && bmd->HasBackgroundClient(); return bmd && bmd->HasPersistentBackgroundClient();
} }
bool BackgroundModeManager::ShouldBeInBackgroundMode() const { bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
return IsBackgroundModePrefEnabled() && return IsBackgroundModePrefEnabled() &&
(HasBackgroundClient() || keep_alive_for_test_); (HasAnyBackgroundClient() || keep_alive_for_test_);
} }
void BackgroundModeManager::OnBackgroundClientInstalled( void BackgroundModeManager::OnBackgroundClientInstalled(
...@@ -750,6 +766,17 @@ void BackgroundModeManager::OnBackgroundClientInstalled( ...@@ -750,6 +766,17 @@ void BackgroundModeManager::OnBackgroundClientInstalled(
DisplayClientInstalledNotification(name); DisplayClientInstalledNotification(name);
} }
void BackgroundModeManager::UpdateEnableLaunchOnStartup() {
bool new_launch_on_startup =
ShouldBeInBackgroundMode() && HasPersistentBackgroundClient();
if (launch_on_startup_enabled_ &&
new_launch_on_startup == *launch_on_startup_enabled_) {
return;
}
launch_on_startup_enabled_.emplace(new_launch_on_startup);
EnableLaunchOnStartup(*launch_on_startup_enabled_);
}
// Gets the image for the status tray icon, at the correct size for the current // Gets the image for the status tray icon, at the correct size for the current
// platform and display settings. // platform and display settings.
gfx::ImageSkia GetStatusTrayIcon() { gfx::ImageSkia GetStatusTrayIcon() {
...@@ -841,7 +868,7 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { ...@@ -841,7 +868,7 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
for (auto* bmd : bmd_vector) { for (auto* bmd : bmd_vector) {
// We should only display the profile in the status icon if it has at // We should only display the profile in the status icon if it has at
// least one background app. // least one background app.
if (bmd->HasBackgroundClient()) { if (bmd->HasAnyBackgroundClient()) {
// The submenu constructor caller owns the lifetime of the submenu. // The submenu constructor caller owns the lifetime of the submenu.
// The containing menu does not handle the lifetime. // The containing menu does not handle the lifetime.
submenus.push_back(std::make_unique<StatusIconMenuModel>(bmd)); submenus.push_back(std::make_unique<StatusIconMenuModel>(bmd));
......
...@@ -168,8 +168,13 @@ class BackgroundModeManager : public content::NotificationObserver, ...@@ -168,8 +168,13 @@ class BackgroundModeManager : public content::NotificationObserver,
// Browser window. // Browser window.
Browser* GetBrowserWindow(); Browser* GetBrowserWindow();
// Returns if this profile has background clients. A client is an extension. // Returns if this profile has persistent background clients. A client is an
bool HasBackgroundClient() const; // extension.
bool HasPersistentBackgroundClient() const;
// Returns if this profile has any background clients. A client is an
// extension.
bool HasAnyBackgroundClient() const;
// Builds the profile specific parts of the menu. The menu passed in may // Builds the profile specific parts of the menu. The menu passed in may
// be a submenu in the case of multi-profiles or the main menu in the case // be a submenu in the case of multi-profiles or the main menu in the case
...@@ -257,6 +262,10 @@ class BackgroundModeManager : public content::NotificationObserver, ...@@ -257,6 +262,10 @@ class BackgroundModeManager : public content::NotificationObserver,
// launch-on-startup is enabled if appropriate. // launch-on-startup is enabled if appropriate.
void OnBackgroundClientInstalled(const base::string16& name); void OnBackgroundClientInstalled(const base::string16& name);
// Update whether Chrome should be launched on startup, depending on whether
// |this| has any persistent background clients.
void UpdateEnableLaunchOnStartup();
// Called to make sure that our launch-on-startup mode is properly set. // Called to make sure that our launch-on-startup mode is properly set.
// (virtual so it can be mocked in tests). // (virtual so it can be mocked in tests).
virtual void EnableLaunchOnStartup(bool should_launch); virtual void EnableLaunchOnStartup(bool should_launch);
...@@ -321,13 +330,18 @@ class BackgroundModeManager : public content::NotificationObserver, ...@@ -321,13 +330,18 @@ class BackgroundModeManager : public content::NotificationObserver,
// Turns on background mode if it's currently disabled. // Turns on background mode if it's currently disabled.
void EnableBackgroundMode(); void EnableBackgroundMode();
// Returns if any profile on the system has a background client. // Returns if any profile on the system has a persistent background client.
// A client is an extension. (virtual to allow overriding in unit tests) // A client is an extension. (virtual to allow overriding in unit tests)
virtual bool HasBackgroundClient() const; virtual bool HasPersistentBackgroundClient() const;
// Returns if there are background clients for a profile. A client is an // Returns if any profile on the system has any background client.
// extension. // A client is an extension. (virtual to allow overriding in unit tests)
virtual bool HasBackgroundClientForProfile(const Profile* profile) const; virtual bool HasAnyBackgroundClient() const;
// Returns if there are persistent background clients for a profile. A client
// is an extension.
virtual bool HasPersistentBackgroundClientForProfile(
const Profile* profile) const;
// Returns true if we should be in background mode. // Returns true if we should be in background mode.
bool ShouldBeInBackgroundMode() const; bool ShouldBeInBackgroundMode() const;
...@@ -406,6 +420,8 @@ class BackgroundModeManager : public content::NotificationObserver, ...@@ -406,6 +420,8 @@ class BackgroundModeManager : public content::NotificationObserver,
// Set to true when background mode is suspended. // Set to true when background mode is suspended.
bool background_mode_suspended_ = false; bool background_mode_suspended_ = false;
base::Optional<bool> launch_on_startup_enabled_;
// Task runner for making startup/login configuration changes that may // Task runner for making startup/login configuration changes that may
// require file system or registry access. // require file system or registry access.
const scoped_refptr<base::SequencedTaskRunner> task_runner_; const scoped_refptr<base::SequencedTaskRunner> task_runner_;
......
...@@ -136,35 +136,54 @@ class AdvancedTestBackgroundModeManager : public TestBackgroundModeManager { ...@@ -136,35 +136,54 @@ class AdvancedTestBackgroundModeManager : public TestBackgroundModeManager {
~AdvancedTestBackgroundModeManager() override {} ~AdvancedTestBackgroundModeManager() override {}
// TestBackgroundModeManager: // TestBackgroundModeManager:
bool HasBackgroundClient() const override { bool HasPersistentBackgroundClient() const override {
for (const auto& profile_count_pair : profile_app_counts_) { return std::find_if(profile_app_counts_.begin(), profile_app_counts_.end(),
if (profile_count_pair.second > 0) [](const auto& profile_count_pair) {
return true; return profile_count_pair.second.persistent > 0;
} }) != profile_app_counts_.end();
return false; }
bool HasAnyBackgroundClient() const override {
return std::find_if(profile_app_counts_.begin(), profile_app_counts_.end(),
[](const auto& profile_count_pair) {
return profile_count_pair.second.any > 0;
}) != profile_app_counts_.end();
} }
bool HasBackgroundClientForProfile(const Profile* profile) const override { bool HasPersistentBackgroundClientForProfile(
const Profile* profile) const override {
auto it = profile_app_counts_.find(profile); auto it = profile_app_counts_.find(profile);
if (it == profile_app_counts_.end()) { if (it == profile_app_counts_.end()) {
ADD_FAILURE(); ADD_FAILURE();
return false; return false;
} }
return it->second > 0; return it->second.persistent > 0;
} }
bool IsBackgroundModePrefEnabled() const override { return enabled_; } bool IsBackgroundModePrefEnabled() const override { return enabled_; }
void SetBackgroundClientCountForProfile(const Profile* profile, void SetBackgroundClientCountForProfile(const Profile* profile,
size_t count) { size_t count) {
profile_app_counts_[profile] = count; profile_app_counts_[profile] = {count, count};
}
void SetPersistentBackgroundClientCountForProfile(const Profile* profile,
size_t count) {
profile_app_counts_[profile].persistent = count;
} }
void SetEnabled(bool enabled) { void SetEnabled(bool enabled) {
enabled_ = enabled; enabled_ = enabled;
OnBackgroundModeEnabledPrefChanged(); OnBackgroundModeEnabledPrefChanged();
} }
using BackgroundModeManager::OnApplicationListChanged;
private: private:
struct AppCounts {
size_t any = 0;
size_t persistent = 0;
};
bool enabled_; bool enabled_;
std::map<const Profile*, size_t> profile_app_counts_; std::map<const Profile*, AppCounts> profile_app_counts_;
DISALLOW_COPY_AND_ASSIGN(AdvancedTestBackgroundModeManager); DISALLOW_COPY_AND_ASSIGN(AdvancedTestBackgroundModeManager);
}; };
...@@ -358,10 +377,12 @@ TEST_F(BackgroundModeManagerTest, BackgroundAppInstallUninstallWhileDisabled) { ...@@ -358,10 +377,12 @@ TEST_F(BackgroundModeManagerTest, BackgroundAppInstallUninstallWhileDisabled) {
*command_line_, profile_manager_->profile_attributes_storage(), true); *command_line_, profile_manager_->profile_attributes_storage(), true);
manager.RegisterProfile(profile_); manager.RegisterProfile(profile_);
// Turn off background mode (shouldn't explicitly disable launch-on-startup as // Turn off background mode (should explicitly disable launch-on-startup as
// the app-count is zero and launch-on-startup shouldn't be considered on). // the app-count is zero and launch-on-startup hasn't been initialized yet).
EXPECT_CALL(manager, EnableLaunchOnStartup(false)).Times(Exactly(1));
manager.SetEnabled(false); manager.SetEnabled(false);
AssertBackgroundModeInactive(manager); AssertBackgroundModeInactive(manager);
Mock::VerifyAndClearExpectations(&manager);
// When a new client is installed, status tray icons will not be created, // When a new client is installed, status tray icons will not be created,
// launch on startup status will not be modified. // launch on startup status will not be modified.
...@@ -890,3 +911,128 @@ TEST_F(BackgroundModeManagerWithExtensionsTest, BalloonDisplay) { ...@@ -890,3 +911,128 @@ TEST_F(BackgroundModeManagerWithExtensionsTest, BalloonDisplay) {
service->AddExtension(upgraded_no_bg_ext_has_bg.get()); service->AddExtension(upgraded_no_bg_ext_has_bg.get());
EXPECT_TRUE(manager_->HasShownBalloon()); EXPECT_TRUE(manager_->HasShownBalloon());
} }
TEST_F(BackgroundModeManagerTest, TransientBackgroundApp) {
AdvancedTestBackgroundModeManager manager(
*command_line_, profile_manager_->profile_attributes_storage(), true);
manager.RegisterProfile(profile_);
ProfileAttributesEntry* entry;
ASSERT_TRUE(profile_manager_->profile_attributes_storage()
->GetProfileAttributesWithPath(profile_->GetPath(), &entry));
EXPECT_FALSE(entry->GetBackgroundStatus());
EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
EXPECT_CALL(manager, EnableLaunchOnStartup(false)).Times(1);
manager.SetBackgroundClientCountForProfile(profile_, 0);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
// Mimic transient app launch.
EXPECT_CALL(manager, EnableLaunchOnStartup(_)).Times(0);
manager.SetBackgroundClientCountForProfile(profile_, 1);
manager.SetPersistentBackgroundClientCountForProfile(profile_, 0);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_FALSE(entry->GetBackgroundStatus());
manager.SuspendBackgroundMode();
AssertBackgroundModeInactive(manager);
EXPECT_FALSE(entry->GetBackgroundStatus());
manager.ResumeBackgroundMode();
// Mimic transient app shutdown.
EXPECT_CALL(manager, EnableLaunchOnStartup(_)).Times(0);
manager.SetBackgroundClientCountForProfile(profile_, 0);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeInactive(manager);
EXPECT_FALSE(entry->GetBackgroundStatus());
}
TEST_F(BackgroundModeManagerTest, TransientBackgroundAppWithPersistent) {
AdvancedTestBackgroundModeManager manager(
*command_line_, profile_manager_->profile_attributes_storage(), true);
manager.RegisterProfile(profile_);
ProfileAttributesEntry* entry;
ASSERT_TRUE(profile_manager_->profile_attributes_storage()
->GetProfileAttributesWithPath(profile_->GetPath(), &entry));
EXPECT_FALSE(entry->GetBackgroundStatus());
EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
EXPECT_CALL(manager, EnableLaunchOnStartup(true)).Times(1);
manager.SetBackgroundClientCountForProfile(profile_, 1);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
// Mimic transient app launch.
EXPECT_CALL(manager, EnableLaunchOnStartup(_)).Times(0);
manager.SetBackgroundClientCountForProfile(profile_, 2);
manager.SetPersistentBackgroundClientCountForProfile(profile_, 1);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
manager.SuspendBackgroundMode();
AssertBackgroundModeInactive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
manager.ResumeBackgroundMode();
// Mimic transient app shutdown.
EXPECT_CALL(manager, EnableLaunchOnStartup(_)).Times(0);
manager.SetBackgroundClientCountForProfile(profile_, 1);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
}
TEST_F(BackgroundModeManagerTest,
BackgroundPersistentAppWhileTransientRunning) {
AdvancedTestBackgroundModeManager manager(
*command_line_, profile_manager_->profile_attributes_storage(), true);
manager.RegisterProfile(profile_);
ProfileAttributesEntry* entry;
ASSERT_TRUE(profile_manager_->profile_attributes_storage()
->GetProfileAttributesWithPath(profile_->GetPath(), &entry));
EXPECT_FALSE(entry->GetBackgroundStatus());
EXPECT_FALSE(KeepAliveRegistry::GetInstance()->IsKeepingAlive());
// Mimic transient app launch.
EXPECT_CALL(manager, EnableLaunchOnStartup(false)).Times(1);
manager.SetBackgroundClientCountForProfile(profile_, 1);
manager.SetPersistentBackgroundClientCountForProfile(profile_, 0);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_FALSE(entry->GetBackgroundStatus());
// Mimic persistent app install.
EXPECT_CALL(manager, EnableLaunchOnStartup(true)).Times(1);
manager.SetBackgroundClientCountForProfile(profile_, 2);
manager.SetPersistentBackgroundClientCountForProfile(profile_, 1);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
manager.SuspendBackgroundMode();
AssertBackgroundModeInactive(manager);
EXPECT_TRUE(entry->GetBackgroundStatus());
manager.ResumeBackgroundMode();
// Mimic persistent app uninstall.
EXPECT_CALL(manager, EnableLaunchOnStartup(false)).Times(1);
manager.SetBackgroundClientCountForProfile(profile_, 1);
manager.SetPersistentBackgroundClientCountForProfile(profile_, 0);
manager.OnApplicationListChanged(profile_);
Mock::VerifyAndClearExpectations(&manager);
AssertBackgroundModeActive(manager);
EXPECT_FALSE(entry->GetBackgroundStatus());
}
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "base/scoped_observer.h" #include "base/scoped_observer.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/background/background_mode_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.h" #include "chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.h"
#include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h" #include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
#include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h"
...@@ -88,6 +90,7 @@ base::CommandLine CreateNativeMessagingConnectCommandLine( ...@@ -88,6 +90,7 @@ base::CommandLine CreateNativeMessagingConnectCommandLine(
kSupportsNativeInitiatedConnectionsHostName); kSupportsNativeInitiatedConnectionsHostName);
command_line.AppendSwitchASCII(switches::kNativeMessagingConnectId, command_line.AppendSwitchASCII(switches::kNativeMessagingConnectId,
connect_id); connect_id);
command_line.AppendSwitch(switches::kNoStartupWindow);
return command_line; return command_line;
} }
...@@ -120,10 +123,16 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, MAYBE_Success) { ...@@ -120,10 +123,16 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, MAYBE_Success) {
ResultCatcher catcher; ResultCatcher catcher;
EXPECT_FALSE(
g_browser_process->background_mode_manager()->IsBackgroundModeActive());
StartupBrowserCreator::ProcessCommandLineAlreadyRunning( StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
CreateNativeMessagingConnectCommandLine("test-connect-id"), {}, CreateNativeMessagingConnectCommandLine("test-connect-id"), {},
profile()->GetPath()); profile()->GetPath());
EXPECT_TRUE(
g_browser_process->background_mode_manager()->IsBackgroundModeActive());
if (!catcher.GetNextResult()) { if (!catcher.GetNextResult()) {
FAIL() << catcher.message(); FAIL() << catcher.message();
} }
...@@ -156,6 +165,7 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, UnsupportedByNativeHost) { ...@@ -156,6 +165,7 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, UnsupportedByNativeHost) {
extension->id()); extension->id());
command_line.AppendSwitchASCII(switches::kNativeMessagingConnectHost, command_line.AppendSwitchASCII(switches::kNativeMessagingConnectHost,
ScopedTestNativeMessagingHost::kHostName); ScopedTestNativeMessagingHost::kHostName);
command_line.AppendSwitch(switches::kNoStartupWindow);
StartupBrowserCreator::ProcessCommandLineAlreadyRunning(command_line, {}, StartupBrowserCreator::ProcessCommandLineAlreadyRunning(command_line, {},
profile()->GetPath()); profile()->GetPath());
...@@ -310,6 +320,75 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, InvalidExtensionId) { ...@@ -310,6 +320,75 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchApiTest, InvalidExtensionId) {
test_host_.temp_dir().AppendASCII("invalid_connect_id.txt"))); test_host_.temp_dir().AppendASCII("invalid_connect_id.txt")));
} }
constexpr char kExtensionId[] = "knldjmfmopnpolahpmmgbagdohdnhkik";
class NativeMessagingLaunchBackgroundModeApiTest
: public NativeMessagingLaunchApiTest {
public:
NativeMessagingLaunchBackgroundModeApiTest() {
ProcessManager::SetEventPageIdleTimeForTesting(1);
ProcessManager::SetEventPageSuspendingTimeForTesting(1);
test_host_.RegisterTestHost(false);
}
NativeMessagingLaunchBackgroundModeApiTest(
const NativeMessagingLaunchBackgroundModeApiTest&) = delete;
NativeMessagingLaunchBackgroundModeApiTest& operator=(
const NativeMessagingLaunchBackgroundModeApiTest&) = delete;
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
NativeMessagingLaunchApiTest::SetUpCommandLine(command_line);
if (base::StringPiece(
::testing::UnitTest::GetInstance()->current_test_info()->name())
.starts_with("PRE")) {
return;
}
set_exit_when_last_browser_closes(false);
command_line->AppendSwitchASCII(switches::kNativeMessagingConnectExtension,
kExtensionId);
command_line->AppendSwitchASCII(
switches::kNativeMessagingConnectHost,
ScopedTestNativeMessagingHost::
kSupportsNativeInitiatedConnectionsHostName);
command_line->AppendSwitchASCII(switches::kNativeMessagingConnectId,
"test-connect-id");
command_line->AppendSwitch(switches::kNoStartupWindow);
}
void SetUpOnMainThread() override {
NativeMessagingLaunchApiTest::SetUpOnMainThread();
catcher_ = std::make_unique<ResultCatcher>();
}
std::unique_ptr<ResultCatcher> catcher_;
};
IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchBackgroundModeApiTest,
PRE_Success) {
auto* extension =
LoadExtension(test_data_dir_.AppendASCII("native_messaging_launch"));
EXPECT_EQ(kExtensionId, extension->id());
}
IN_PROC_BROWSER_TEST_F(NativeMessagingLaunchBackgroundModeApiTest, Success) {
EXPECT_TRUE(
g_browser_process->background_mode_manager()->IsBackgroundModeActive());
if (!catcher_->GetNextResult()) {
FAIL() << catcher_->message();
}
size_t tabs = 0;
for (auto* browser : *BrowserList::GetInstance()) {
tabs += browser->tab_strip_model()->count();
}
EXPECT_EQ(0u, tabs);
ASSERT_NO_FATAL_FAILURE(TestKeepAliveStateObserver().WaitForNoKeepAlive());
}
#endif // !defined(OS_CHROMEOS) #endif // !defined(OS_CHROMEOS)
} // namespace } // namespace
......
...@@ -688,17 +688,25 @@ bool StartupBrowserCreator::ProcessCmdLineImpl( ...@@ -688,17 +688,25 @@ bool StartupBrowserCreator::ProcessCmdLineImpl(
} }
#if !defined(OS_CHROMEOS) #if !defined(OS_CHROMEOS)
if (!process_startup && if (base::FeatureList::IsEnabled(features::kOnConnectNative) &&
base::FeatureList::IsEnabled(features::kOnConnectNative) &&
command_line.HasSwitch(switches::kNativeMessagingConnectHost) && command_line.HasSwitch(switches::kNativeMessagingConnectHost) &&
command_line.HasSwitch(switches::kNativeMessagingConnectExtension)) { command_line.HasSwitch(switches::kNativeMessagingConnectExtension)) {
silent_launch = true;
extensions::LaunchNativeMessageHostFromNativeApp( extensions::LaunchNativeMessageHostFromNativeApp(
command_line.GetSwitchValueASCII( command_line.GetSwitchValueASCII(
switches::kNativeMessagingConnectExtension), switches::kNativeMessagingConnectExtension),
command_line.GetSwitchValueASCII(switches::kNativeMessagingConnectHost), command_line.GetSwitchValueASCII(switches::kNativeMessagingConnectHost),
command_line.GetSwitchValueASCII(switches::kNativeMessagingConnectId), command_line.GetSwitchValueASCII(switches::kNativeMessagingConnectId),
last_used_profile); last_used_profile);
// Chrome's lifetime, if the specified extension and native messaging host
// are both valid and a connection is established, is prolonged by
// BackgroundModeManager. If |process_startup| is true, --no-startup-window
// must be set or a browser window must be created for BackgroundModeManager
// to start background mode. Without this, nothing will take the first
// keep-alive and the browser process will not terminate. To avoid this
// situation, don't set |silent_launch| in response to the native messaging
// connect switches; require the client to pass --no-startup-window if
// suppressing the creation of a window is desired.
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment