Skip to content

Commit

Permalink
Allow background mode while an extension is communicating with a nati…
Browse files Browse the repository at this point in the history
…ve 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/+/1898894
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: Drew Wilson <atwilson@chromium.org>
Reviewed-by: Tommy Martino <tmartino@chromium.org>
Reviewed-by: Sergei Datsenko <dats@chromium.org>
Commit-Queue: Sam McNally <sammc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#716480}
  • Loading branch information
sammc authored and Commit Bot committed Nov 19, 2019
1 parent 4dab5cc commit c54a52f
Show file tree
Hide file tree
Showing 7 changed files with 406 additions and 58 deletions.
77 changes: 64 additions & 13 deletions chrome/browser/background/background_application_list_model.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/crx_file/id_util.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
Expand Down Expand Up @@ -85,9 +87,14 @@ void GetServiceApplications(extensions::ExtensionService* service,
ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
const ExtensionSet& enabled_extensions = registry->enabled_extensions();

auto* process_manager = extensions::ProcessManager::Get(service->profile());

for (const auto& extension : enabled_extensions) {
if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
service->profile())) {
if (BackgroundApplicationListModel::IsPersistentBackgroundApp(
*extension, service->profile()) ||
(BackgroundApplicationListModel::IsTransientBackgroundApp(
*extension, service->profile()) &&
process_manager->GetBackgroundHostForExtension(extension->id()))) {
applications_result->push_back(extension);
}
}
Expand All @@ -96,8 +103,8 @@ void GetServiceApplications(extensions::ExtensionService* service,
// crashed doesn't mean we should ignore it).
const ExtensionSet& terminated_extensions = registry->terminated_extensions();
for (const auto& extension : terminated_extensions) {
if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
service->profile())) {
if (BackgroundApplicationListModel::IsPersistentBackgroundApp(
*extension, service->profile())) {
applications_result->push_back(extension);
}
}
Expand Down Expand Up @@ -230,8 +237,9 @@ int BackgroundApplicationListModel::GetPosition(
}

// static
bool BackgroundApplicationListModel::IsBackgroundApp(
const Extension& extension, Profile* profile) {
bool BackgroundApplicationListModel::IsPersistentBackgroundApp(
const Extension& extension,
Profile* profile) {
// An extension is a "background app" if it has the "background API"
// permission, and meets one of the following criteria:
// 1) It is an extension (not a hosted app).
Expand Down Expand Up @@ -269,6 +277,25 @@ bool BackgroundApplicationListModel::IsBackgroundApp(
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(
int type,
const content::NotificationSource& source,
Expand Down Expand Up @@ -321,12 +348,16 @@ void BackgroundApplicationListModel::OnExtensionSystemReady() {
background_contents_service_observer_.Add(
BackgroundContentsServiceFactory::GetForProfile(profile_));

if (base::FeatureList::IsEnabled(features::kOnConnectNative))
process_manager_observer_.Add(extensions::ProcessManager::Get(profile_));

startup_done_ = true;
}

void BackgroundApplicationListModel::OnShutdown(ExtensionRegistry* registry) {
DCHECK_EQ(ExtensionRegistry::Get(profile_), registry);
extension_registry_observer_.Remove(registry);
process_manager_observer_.RemoveAll();
}

void BackgroundApplicationListModel::OnBackgroundContentsServiceChanged() {
Expand All @@ -341,17 +372,18 @@ void BackgroundApplicationListModel::OnExtensionPermissionsUpdated(
const Extension* extension,
UpdatedExtensionPermissionsInfo::Reason reason,
const PermissionSet& permissions) {
if (permissions.HasAPIPermission(APIPermission::kBackground)) {
if (permissions.HasAPIPermission(APIPermission::kBackground) ||
(base::FeatureList::IsEnabled(features::kOnConnectNative) &&
permissions.HasAPIPermission(APIPermission::kTransientBackground))) {
switch (reason) {
case UpdatedExtensionPermissionsInfo::ADDED:
DCHECK(IsBackgroundApp(*extension, profile_));
Update();
AssociateApplicationData(extension);
break;
case UpdatedExtensionPermissionsInfo::REMOVED:
DCHECK(!IsBackgroundApp(*extension, profile_));
Update();
DissociateApplicationData(extension);
if (IsBackgroundApp(*extension, profile_)) {
AssociateApplicationData(extension);
} else {
DissociateApplicationData(extension);
}
break;
default:
NOTREACHED();
Expand Down Expand Up @@ -391,3 +423,22 @@ void BackgroundApplicationListModel::Update() {
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();
}
33 changes: 27 additions & 6 deletions chrome/browser/background/background_application_list_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.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"

class Profile;
Expand All @@ -38,7 +40,8 @@ class ExtensionRegistry;
class BackgroundApplicationListModel
: public content::NotificationObserver,
public extensions::ExtensionRegistryObserver,
public BackgroundContentsServiceObserver {
public BackgroundContentsServiceObserver,
public extensions::ProcessManagerObserver {
public:
// Observer is informed of changes to the model. Users of the
// BackgroundApplicationListModel should anticipate that associated data,
Expand Down Expand Up @@ -83,7 +86,20 @@ class BackgroundApplicationListModel

// Returns true if the passed extension is a background app.
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.
void RemoveObserver(Observer* observer);
Expand All @@ -103,6 +119,8 @@ class BackgroundApplicationListModel
// Returns true if all startup notifications have already been issued.
bool startup_done() const { return startup_done_; }

bool HasPersistentBackgroundApps() const;

private:
// Contains data associated with a background application that is not
// represented by the Extension class.
Expand Down Expand Up @@ -145,10 +163,6 @@ class BackgroundApplicationListModel
// application, e.g. the Icon, has changed.
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.
void OnExtensionPermissionsUpdated(
const extensions::Extension* extension,
Expand All @@ -158,6 +172,10 @@ class BackgroundApplicationListModel
// Refresh the list of background applications and generate notifications.
void Update();

// ProcessManagerObserver:
void OnBackgroundHostCreated(extensions::ExtensionHost* host) override;
void OnBackgroundHostClose(const std::string& extension_id) override;

// Associates extension id strings with Application objects.
std::map<std::string, std::unique_ptr<Application>> applications_;

Expand All @@ -175,6 +193,9 @@ class BackgroundApplicationListModel
ScopedObserver<BackgroundContentsService, BackgroundContentsServiceObserver>
background_contents_service_observer_{this};

ScopedObserver<extensions::ProcessManager, extensions::ProcessManagerObserver>
process_manager_observer_{this};

base::WeakPtrFactory<BackgroundApplicationListModel> weak_ptr_factory_{this};

DISALLOW_COPY_AND_ASSIGN(BackgroundApplicationListModel);
Expand Down
61 changes: 44 additions & 17 deletions chrome/browser/background/background_mode_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,19 @@ Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
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;
}

void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
StatusIconMenuModel* menu,
StatusIconMenuModel* containing_menu) {
if (HasBackgroundClient()) {
if (HasAnyBackgroundClient()) {
// Add a menu item for each application (extension).
for (const auto& application : *applications_) {
gfx::ImageSkia icon = applications_->GetIcon(application.get());
Expand Down Expand Up @@ -205,10 +210,13 @@ BackgroundModeManager::BackgroundModeData::GetNewBackgroundApps() {
// Not found in our set yet - add it and maybe return as a previously
// unseen extension.
current_extensions_.insert(id);
// If this application has been newly loaded after the initial startup,
// notify the user.
if (applications_->startup_done())
// If this application has been newly loaded after the initial startup and
// this is a persistent background app, notify the user.
if (applications_->startup_done() &&
BackgroundApplicationListModel::IsPersistentBackgroundApp(
*application, profile_)) {
new_apps.insert(application.get());
}
}
}
return new_apps;
Expand Down Expand Up @@ -416,6 +424,7 @@ void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
bool enabled = IsBackgroundModePrefEnabled();
UMA_HISTOGRAM_BOOLEAN("BackgroundMode.BackgroundModeEnabledPrefChanged",
enabled);
UpdateEnableLaunchOnStartup();
if (enabled)
EnableBackgroundMode();
else
Expand Down Expand Up @@ -481,8 +490,8 @@ void BackgroundModeManager::OnProfileWillBeRemoved(
background_mode_data_.erase(it);
// If there are no background mode profiles any longer, then turn off
// background mode.
UpdateEnableLaunchOnStartup();
if (!ShouldBeInBackgroundMode()) {
EnableLaunchOnStartup(false);
EndBackgroundMode();
}
UpdateStatusTrayIconContextMenu();
Expand Down Expand Up @@ -633,7 +642,7 @@ void BackgroundModeManager::EnableBackgroundMode() {
if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
StartBackgroundMode();

EnableLaunchOnStartup(true);
UpdateEnableLaunchOnStartup();
}
}

Expand All @@ -642,7 +651,6 @@ void BackgroundModeManager::DisableBackgroundMode() {
// If background mode is currently enabled, turn it off.
if (in_background_mode_) {
EndBackgroundMode();
EnableLaunchOnStartup(false);
}
}

Expand Down Expand Up @@ -685,13 +693,14 @@ void BackgroundModeManager::OnClientsChanged(
ProfileAttributesEntry* entry;
if (profile_storage_->
GetProfileAttributesWithPath(profile->GetPath(), &entry)) {
entry->SetBackgroundStatus(HasBackgroundClientForProfile(profile));
entry->SetBackgroundStatus(
HasPersistentBackgroundClientForProfile(profile));
}

UpdateEnableLaunchOnStartup();
if (!ShouldBeInBackgroundMode()) {
// We've uninstalled our last background client, make sure we exit
// background mode and no longer launch on startup.
EnableLaunchOnStartup(false);
EndBackgroundMode();
} else {
// We have at least one background client - make sure we're in background
Expand All @@ -701,7 +710,6 @@ void BackgroundModeManager::OnClientsChanged(
// 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
// be created (to avoid overriding the specific user action).
EnableLaunchOnStartup(true);
StartBackgroundMode();
}

Expand All @@ -714,24 +722,32 @@ void BackgroundModeManager::OnClientsChanged(
}
}

bool BackgroundModeManager::HasBackgroundClient() const {
bool BackgroundModeManager::HasPersistentBackgroundClient() const {
for (const auto& it : background_mode_data_) {
if (it.second->HasBackgroundClient())
if (it.second->HasPersistentBackgroundClient())
return true;
}
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 {
BackgroundModeManager::BackgroundModeData* bmd =
GetBackgroundModeData(profile);
return bmd && bmd->HasBackgroundClient();
return bmd && bmd->HasPersistentBackgroundClient();
}

bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
return IsBackgroundModePrefEnabled() &&
(HasBackgroundClient() || keep_alive_for_test_);
(HasAnyBackgroundClient() || keep_alive_for_test_);
}

void BackgroundModeManager::OnBackgroundClientInstalled(
Expand All @@ -750,6 +766,17 @@ void BackgroundModeManager::OnBackgroundClientInstalled(
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
// platform and display settings.
gfx::ImageSkia GetStatusTrayIcon() {
Expand Down Expand Up @@ -841,7 +868,7 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
for (auto* bmd : bmd_vector) {
// We should only display the profile in the status icon if it has at
// least one background app.
if (bmd->HasBackgroundClient()) {
if (bmd->HasAnyBackgroundClient()) {
// The submenu constructor caller owns the lifetime of the submenu.
// The containing menu does not handle the lifetime.
submenus.push_back(std::make_unique<StatusIconMenuModel>(bmd));
Expand Down

0 comments on commit c54a52f

Please sign in to comment.