diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/about_flags.cc | 7 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_mapping.cc | 44 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_mapping.h | 8 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_mapping_unittest.cc | 10 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_provider.cc | 54 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_provider.h | 14 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_provider_browsertest.cc | 154 | ||||
-rw-r--r-- | chrome/browser/apps/drive/drive_app_uninstall_sync_service.h | 22 | ||||
-rw-r--r-- | chrome/browser/drive/fake_drive_service.cc | 5 | ||||
-rw-r--r-- | chrome/browser/drive/fake_drive_service.h | 3 | ||||
-rw-r--r-- | chrome/browser/ui/app_list/app_list_syncable_service.cc | 70 | ||||
-rw-r--r-- | chrome/browser/ui/app_list/app_list_syncable_service.h | 8 | ||||
-rw-r--r-- | chrome/chrome_browser_extensions.gypi | 1 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 4 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 | ||||
-rw-r--r-- | chrome/test/data/drive/applist_app_template.json | 2 |
17 files changed, 385 insertions, 28 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 971deaa..a18da45 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6411,6 +6411,12 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_FLAGS_ENABLE_CENTERED_APP_LIST_DESCRIPTION" desc="Description of the flag to center the app launcher and make it wide instead of tall."> Positions the App Launcher in the center of the screen with a landscape aspect. </message> + <message name="IDS_FLAGS_DISABLE_DRIVE_APPS_IN_APP_LIST_NAME" desc="Name of the flag to disable Drive apps in app launcher."> + Disable Drive apps in App Launcher. + </message> + <message name="IDS_FLAGS_DISABLE_DRIVE_APPS_IN_APP_LIST_DESCRIPTION" desc="Description of the flag to disable Drive apps in app launcher."> + Do not show Drive apps side by side with Chrome apps in App Launcher. + </message> </if> <if expr="chromeos"> <message name="IDS_FLAGS_ENABLE_FIRST_RUN_UI_TRANSITIONS_NAME" desc="Name of the flag to enable animated transitions for the first-run tutorial."> diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 20efe3c..16afec5 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1479,6 +1479,13 @@ const Experiment kExperiments[] = { kOsLinux | kOsWin | kOsCrOS, SINGLE_VALUE_TYPE(app_list::switches::kDisableAppInfo) }, + { + "disable-drive-apps-in-app-list", + IDS_FLAGS_DISABLE_DRIVE_APPS_IN_APP_LIST_NAME, + IDS_FLAGS_DISABLE_DRIVE_APPS_IN_APP_LIST_DESCRIPTION, + kOsCrOS, + SINGLE_VALUE_TYPE(app_list::switches::kDisableDriveAppsInAppList) + }, #endif #if defined(OS_ANDROID) { diff --git a/chrome/browser/apps/drive/drive_app_mapping.cc b/chrome/browser/apps/drive/drive_app_mapping.cc index c64c060..8456fcd 100644 --- a/chrome/browser/apps/drive/drive_app_mapping.cc +++ b/chrome/browser/apps/drive/drive_app_mapping.cc @@ -34,6 +34,7 @@ scoped_ptr<base::DictionaryValue> CreateInfoDict( } // namespace DriveAppMapping::DriveAppMapping(PrefService* prefs) : prefs_(prefs) { + GetUninstalledIdsFromPref(); } DriveAppMapping::~DriveAppMapping() { @@ -45,6 +46,9 @@ void DriveAppMapping::RegisterProfilePrefs( registry->RegisterDictionaryPref( prefs::kAppLauncherDriveAppMapping, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterListPref( + prefs::kAppLauncherUninstalledDriveApps, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); } void DriveAppMapping::Add(const std::string& drive_app_id, @@ -125,3 +129,43 @@ std::set<std::string> DriveAppMapping::GetDriveAppIds() const { } return keys; } + +void DriveAppMapping::AddUninstalledDriveApp(const std::string& drive_app_id) { + if (IsUninstalledDriveApp(drive_app_id)) + return; + uninstalled_app_ids_.insert(drive_app_id); + UpdateUninstalledList(); +} + +void DriveAppMapping::RemoveUninstalledDriveApp( + const std::string& drive_app_id) { + auto it = uninstalled_app_ids_.find(drive_app_id); + if (it == uninstalled_app_ids_.end()) + return; + uninstalled_app_ids_.erase(it); + UpdateUninstalledList(); +} + +bool DriveAppMapping::IsUninstalledDriveApp( + const std::string& drive_app_id) const { + return uninstalled_app_ids_.find(drive_app_id) != uninstalled_app_ids_.end(); +} + +void DriveAppMapping::GetUninstalledIdsFromPref() { + uninstalled_app_ids_.clear(); + const base::ListValue* list = + prefs_->GetList(prefs::kAppLauncherUninstalledDriveApps); + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string app_id; + if (!list->GetString(i, &app_id)) + continue; + uninstalled_app_ids_.insert(app_id); + } +} + +void DriveAppMapping::UpdateUninstalledList() { + ListPrefUpdate update(prefs_, prefs::kAppLauncherUninstalledDriveApps); + update->Clear(); + for (const auto& app_id : uninstalled_app_ids_) + update->AppendString(app_id); +} diff --git a/chrome/browser/apps/drive/drive_app_mapping.h b/chrome/browser/apps/drive/drive_app_mapping.h index be3c1ee..3ac44c09 100644 --- a/chrome/browser/apps/drive/drive_app_mapping.h +++ b/chrome/browser/apps/drive/drive_app_mapping.h @@ -38,8 +38,16 @@ class DriveAppMapping { std::set<std::string> GetDriveAppIds() const; + void AddUninstalledDriveApp(const std::string& drive_app_id); + void RemoveUninstalledDriveApp(const std::string& drive_app_id); + bool IsUninstalledDriveApp(const std::string& drive_app_id) const; + private: + void GetUninstalledIdsFromPref(); + void UpdateUninstalledList(); + PrefService* prefs_; + std::set<std::string> uninstalled_app_ids_; DISALLOW_COPY_AND_ASSIGN(DriveAppMapping); }; diff --git a/chrome/browser/apps/drive/drive_app_mapping_unittest.cc b/chrome/browser/apps/drive/drive_app_mapping_unittest.cc index 1c50d48..0698d14 100644 --- a/chrome/browser/apps/drive/drive_app_mapping_unittest.cc +++ b/chrome/browser/apps/drive/drive_app_mapping_unittest.cc @@ -114,3 +114,13 @@ TEST_F(DriveAppMappingTest, Remove) { drive_app_ids = mapping()->GetDriveAppIds(); EXPECT_EQ(0u, drive_app_ids.size()); } + +TEST_F(DriveAppMappingTest, TrackUninstall) { + const std::string drive_app_id = "drive-1"; + + mapping()->AddUninstalledDriveApp(drive_app_id); + EXPECT_TRUE(mapping()->IsUninstalledDriveApp(drive_app_id)); + + mapping()->RemoveUninstalledDriveApp(drive_app_id); + EXPECT_FALSE(mapping()->IsUninstalledDriveApp(drive_app_id)); +} diff --git a/chrome/browser/apps/drive/drive_app_provider.cc b/chrome/browser/apps/drive/drive_app_provider.cc index dac04b5..e55b6d6 100644 --- a/chrome/browser/apps/drive/drive_app_provider.cc +++ b/chrome/browser/apps/drive/drive_app_provider.cc @@ -13,6 +13,7 @@ #include "base/stl_util.h" #include "chrome/browser/apps/drive/drive_app_converter.h" #include "chrome/browser/apps/drive/drive_app_mapping.h" +#include "chrome/browser/apps/drive/drive_app_uninstall_sync_service.h" #include "chrome/browser/apps/drive/drive_service_bridge.h" #include "chrome/browser/drive/drive_app_registry.h" #include "chrome/browser/extensions/extension_service.h" @@ -34,8 +35,11 @@ void IgnoreUninstallResult(google_apis::GDataErrorCode) { } // namespace -DriveAppProvider::DriveAppProvider(Profile* profile) +DriveAppProvider::DriveAppProvider( + Profile* profile, + DriveAppUninstallSyncService* uninstall_sync_service) : profile_(profile), + uninstall_sync_service_(uninstall_sync_service), service_bridge_(DriveServiceBridge::Create(profile).Pass()), mapping_(new DriveAppMapping(profile->GetPrefs())), weak_ptr_factory_(this) { @@ -62,6 +66,18 @@ void DriveAppProvider::SetDriveServiceBridgeForTest( service_bridge_->GetAppRegistry()->AddObserver(this); } +void DriveAppProvider::AddUninstalledDriveAppFromSync( + const std::string& drive_app_id) { + mapping_->AddUninstalledDriveApp(drive_app_id); + UpdateDriveApps(); +} + +void DriveAppProvider::RemoveUninstalledDriveAppFromSync( + const std::string& drive_app_id) { + mapping_->RemoveUninstalledDriveApp(drive_app_id); + UpdateDriveApps(); +} + void DriveAppProvider::UpdateMappingAndExtensionSystem( const std::string& drive_app_id, const Extension* new_app, @@ -98,6 +114,10 @@ void DriveAppProvider::ProcessDeferredOnExtensionInstalled( if (!app) return; + // Remove uninstall tracking for user installed app. + mapping_->RemoveUninstalledDriveApp(drive_app_id); + uninstall_sync_service_->UntrackUninstalledDriveApp(drive_app_id); + UpdateMappingAndExtensionSystem(drive_app_id, app, false); } @@ -201,12 +221,15 @@ void DriveAppProvider::ProcessRemovedDriveApp(const std::string& drive_app_id) { NULL); } -void DriveAppProvider::OnDriveAppRegistryUpdated() { +void DriveAppProvider::UpdateDriveApps() { service_bridge_->GetAppRegistry()->GetAppList(&drive_apps_); IdSet current_ids; - for (size_t i = 0; i < drive_apps_.size(); ++i) - current_ids.insert(drive_apps_[i].app_id); + for (size_t i = 0; i < drive_apps_.size(); ++i) { + const std::string& drive_app_id = drive_apps_[i].app_id; + if (!mapping_->IsUninstalledDriveApp(drive_app_id)) + current_ids.insert(drive_app_id); + } const IdSet existing_ids = mapping_->GetDriveAppIds(); const IdSet ids_to_remove = @@ -218,11 +241,16 @@ void DriveAppProvider::OnDriveAppRegistryUpdated() { } for (size_t i = 0; i < drive_apps_.size(); ++i) { - AddOrUpdateDriveApp(drive_apps_[i]); + if (!mapping_->IsUninstalledDriveApp(drive_apps_[i].app_id)) + AddOrUpdateDriveApp(drive_apps_[i]); } SchedulePendingConverters(); } +void DriveAppProvider::OnDriveAppRegistryUpdated() { + UpdateDriveApps(); +} + void DriveAppProvider::OnExtensionInstalled( content::BrowserContext* browser_context, const Extension* extension, @@ -267,6 +295,18 @@ void DriveAppProvider::OnExtensionUninstalled( if (drive_app_id.empty()) return; - service_bridge_->GetAppRegistry()->UninstallApp( - drive_app_id, base::Bind(&IgnoreUninstallResult)); + for (size_t i = 0; i < drive_apps_.size(); ++i) { + if (drive_apps_[i].app_id != drive_app_id) + continue; + + if (drive_apps_[i].is_removable) { + service_bridge_->GetAppRegistry()->UninstallApp( + drive_app_id, base::Bind(&IgnoreUninstallResult)); + } else { + mapping_->AddUninstalledDriveApp(drive_app_id); + uninstall_sync_service_->TrackUninstalledDriveApp(drive_app_id); + } + + return; + } } diff --git a/chrome/browser/apps/drive/drive_app_provider.h b/chrome/browser/apps/drive/drive_app_provider.h index e29db8e..7f07743 100644 --- a/chrome/browser/apps/drive/drive_app_provider.h +++ b/chrome/browser/apps/drive/drive_app_provider.h @@ -23,6 +23,7 @@ struct DriveAppInfo; class BrowserContextKeyedServiceFactory; class DriveAppConverter; class DriveAppMapping; +class DriveAppUninstallSyncService; class DriveServiceBridge; class ExtensionService; class Profile; @@ -35,7 +36,8 @@ class Profile; class DriveAppProvider : public drive::DriveAppRegistryObserver, public extensions::ExtensionRegistryObserver { public: - explicit DriveAppProvider(Profile* profile); + DriveAppProvider(Profile* profile, + DriveAppUninstallSyncService* uninstall_sync_service); virtual ~DriveAppProvider(); // Appends PKS factories this class depends on. @@ -44,6 +46,13 @@ class DriveAppProvider : public drive::DriveAppRegistryObserver, void SetDriveServiceBridgeForTest(scoped_ptr<DriveServiceBridge> test_bridge); + // Adds/removes uninstalled Drive app id from DriveAppUninstallSyncService. + // If a Drive app id is added as uninstalled Drive app, DriveAppProvider + // would not auto create the local URL app for it until the uninstall record + // is removed. + void AddUninstalledDriveAppFromSync(const std::string& drive_app_id); + void RemoveUninstalledDriveAppFromSync(const std::string& drive_app_id); + private: friend class DriveAppProviderTest; @@ -68,6 +77,8 @@ class DriveAppProvider : public drive::DriveAppRegistryObserver, void AddOrUpdateDriveApp(const drive::DriveAppInfo& drive_app); void ProcessRemovedDriveApp(const std::string& drive_app_id); + void UpdateDriveApps(); + // drive::DriveAppRegistryObserver overrides: virtual void OnDriveAppRegistryUpdated() override; @@ -82,6 +93,7 @@ class DriveAppProvider : public drive::DriveAppRegistryObserver, extensions::UninstallReason reason) override; Profile* profile_; + DriveAppUninstallSyncService* uninstall_sync_service_; scoped_ptr<DriveServiceBridge> service_bridge_; scoped_ptr<DriveAppMapping> mapping_; diff --git a/chrome/browser/apps/drive/drive_app_provider_browsertest.cc b/chrome/browser/apps/drive/drive_app_provider_browsertest.cc index 2647b1c..726d7b6 100644 --- a/chrome/browser/apps/drive/drive_app_provider_browsertest.cc +++ b/chrome/browser/apps/drive/drive_app_provider_browsertest.cc @@ -4,6 +4,9 @@ #include "chrome/browser/apps/drive/drive_app_provider.h" +#include <set> +#include <string> + #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" @@ -11,6 +14,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/timer/timer.h" #include "chrome/browser/apps/drive/drive_app_mapping.h" +#include "chrome/browser/apps/drive/drive_app_uninstall_sync_service.h" #include "chrome/browser/apps/drive/drive_service_bridge.h" #include "chrome/browser/drive/drive_app_registry.h" #include "chrome/browser/drive/fake_drive_service.h" @@ -56,6 +60,35 @@ class TestDriveServiceBridge : public DriveServiceBridge { DISALLOW_COPY_AND_ASSIGN(TestDriveServiceBridge); }; +class FakeUninstallSyncService : public DriveAppUninstallSyncService { + public: + FakeUninstallSyncService() {} + virtual ~FakeUninstallSyncService() {} + + bool IsUninstallTracked(const std::string& drive_app_id) const { + return uninstalled_app_ids_.find(drive_app_id) != + uninstalled_app_ids_.end(); + } + + // DriveAppUninstallSyncService + virtual void TrackUninstalledDriveApp( + const std::string& drive_app_id) override { + uninstalled_app_ids_.insert(drive_app_id); + } + virtual void UntrackUninstalledDriveApp( + const std::string& drive_app_id) override { + auto it = uninstalled_app_ids_.find(drive_app_id); + if (it == uninstalled_app_ids_.end()) + return; + uninstalled_app_ids_.erase(it); + } + + private: + std::set<std::string> uninstalled_app_ids_; + + DISALLOW_COPY_AND_ASSIGN(FakeUninstallSyncService); +}; + } // namespace class DriveAppProviderTest : public ExtensionBrowserTest, @@ -73,7 +106,10 @@ class DriveAppProviderTest : public ExtensionBrowserTest, apps_registry_.reset( new drive::DriveAppRegistry(fake_drive_service_.get())); - provider_.reset(new DriveAppProvider(profile())); + fake_uninstall_sync_service_.reset(new FakeUninstallSyncService); + + provider_.reset( + new DriveAppProvider(profile(), fake_uninstall_sync_service_.get())); provider_->SetDriveServiceBridgeForTest( make_scoped_ptr(new TestDriveServiceBridge(apps_registry_.get())) .PassAs<DriveServiceBridge>()); @@ -159,6 +195,9 @@ class DriveAppProviderTest : public ExtensionBrowserTest, drive::FakeDriveService* fake_drive_service() { return fake_drive_service_.get(); } + FakeUninstallSyncService* fake_uninstall_sync_service() { + return fake_uninstall_sync_service_.get(); + } DriveAppProvider* provider() { return provider_.get(); } DriveAppMapping* mapping() { return provider_->mapping_.get(); } @@ -175,6 +214,7 @@ class DriveAppProviderTest : public ExtensionBrowserTest, } scoped_ptr<drive::FakeDriveService> fake_drive_service_; + scoped_ptr<FakeUninstallSyncService> fake_uninstall_sync_service_; scoped_ptr<drive::DriveAppRegistry> apps_registry_; scoped_ptr<DriveAppProvider> provider_; @@ -194,7 +234,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, ExistingChromeApp) { // Prepare a Drive app that matches the chrome app id. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, chrome_app->id(), kLaunchUrl); + kDriveAppId, kDriveAppName, chrome_app->id(), kLaunchUrl, true); RefreshDriveAppRegistry(); EXPECT_FALSE(HasPendingConverters()); @@ -211,7 +251,8 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, ExistingChromeApp) { // A Drive app creates an URL app when no matching Chrome app presents. IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, CreateUrlApp) { // Prepare a Drive app with no underlying chrome app. - fake_drive_service()->AddApp(kDriveAppId, kDriveAppName, "", kLaunchUrl); + fake_drive_service()->AddApp( + kDriveAppId, kDriveAppName, "", kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -238,7 +279,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, CreateUrlApp) { IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, MatchingChromeAppInstalled) { // Prepare a Drive app that matches the not-yet-installed kChromeAppId. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -272,7 +313,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DisconnectDriveAppUninstallUrlApp) { // Prepare a Drive app that matches the not-yet-installed kChromeAppId. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -299,7 +340,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, // Prepare a Drive app that matches the chrome app id. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); EXPECT_FALSE(HasPendingConverters()); @@ -315,7 +356,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, KeepGeneratedFlagBetweenUpdates) { // Prepare a Drive app with no underlying chrome app. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -326,7 +367,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, KeepGeneratedFlagBetweenUpdates) { const char kChangedName[] = "Changed name"; fake_drive_service()->RemoveAppByProductId(kChromeAppId); fake_drive_service()->AddApp( - kDriveAppId, kChangedName, kChromeAppId, kLaunchUrl); + kDriveAppId, kChangedName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -340,7 +381,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, KeepGeneratedFlagBetweenUpdates) { IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DriveAppChanged) { // Prepare a Drive app with no underlying chrome app. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -361,7 +402,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DriveAppChanged) { const char kAnotherLaunchUrl[] = "http://example.com/another_end_point"; fake_drive_service()->RemoveAppByProductId(kChromeAppId); fake_drive_service()->AddApp( - kDriveAppId, kAnotherName, kChromeAppId, kAnotherLaunchUrl); + kDriveAppId, kAnotherName, kChromeAppId, kAnotherLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -391,7 +432,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, DriveAppChanged) { IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, NoChange) { // Prepare one Drive app. fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -420,7 +461,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledBeforeDriveApp) { InstallUserUrlApp(kLaunchUrl); fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -439,7 +480,7 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledBeforeDriveApp) { // installation happens after Drive app conversion. IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledAfterDriveApp) { fake_drive_service()->AddApp( - kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl); + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, true); RefreshDriveAppRegistry(); WaitForPendingDriveAppConverters(); @@ -458,3 +499,90 @@ IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UserInstalledAfterDriveApp) { EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById( url_app_id, ExtensionRegistry::EVERYTHING)); } + +// Tests that uninstalling of a unremovable Drive app is tracked in +// DriveAppUninstallSyncService. +IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UninstallUnremovableDriveApp) { + // Add an unremovable Drive app. + fake_drive_service()->AddApp( + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, false); + RefreshDriveAppRegistry(); + WaitForPendingDriveAppConverters(); + + const std::string chrome_app_id = mapping()->GetChromeApp(kDriveAppId); + ASSERT_FALSE(chrome_app_id.empty()); + + // Simulate user uninstall. + UninstallExtension(chrome_app_id); + EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById( + chrome_app_id, ExtensionRegistry::EVERYTHING)); + + // Trigger a refresh and the app should stay uninstalled. + RefreshDriveAppRegistry(); + WaitForPendingDriveAppConverters(); + EXPECT_TRUE(mapping()->GetChromeApp(kDriveAppId).empty()); + EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById( + chrome_app_id, ExtensionRegistry::EVERYTHING)); + + // Drive service still has the app. + EXPECT_TRUE(fake_drive_service()->HasApp(kDriveAppId)); + // Uninstall is tracked by DriveAppUninstallSyncService and mapping. + EXPECT_TRUE(fake_uninstall_sync_service()->IsUninstallTracked(kDriveAppId)); + EXPECT_TRUE(mapping()->IsUninstalledDriveApp(kDriveAppId)); +} + +// Tests that user install removes the uninstall tracking from +// DriveAppUninstallSyncService. +IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, + UserInstallResetsUninstallTracking) { + // Add an unremovable Drive app. + fake_drive_service()->AddApp( + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, false); + RefreshDriveAppRegistry(); + WaitForPendingDriveAppConverters(); + + const std::string chrome_app_id = mapping()->GetChromeApp(kDriveAppId); + ASSERT_FALSE(chrome_app_id.empty()); + + // Simulate user uninstall and uninstall should be tracked. + UninstallExtension(chrome_app_id); + EXPECT_TRUE(fake_uninstall_sync_service()->IsUninstallTracked(kDriveAppId)); + EXPECT_TRUE(mapping()->IsUninstalledDriveApp(kDriveAppId)); + + // Simulate user install and uninstall is no longer tracked. + InstallChromeApp(1); + EXPECT_FALSE(fake_uninstall_sync_service()->IsUninstallTracked(kDriveAppId)); + EXPECT_FALSE(mapping()->IsUninstalledDriveApp(kDriveAppId)); +} + +// Tests that Drive app is removed when uninstall is added from sync and +// added back when uninstall is removed from sync. +IN_PROC_BROWSER_TEST_F(DriveAppProviderTest, UninstallChangedFromSync) { + // Add an unremovable Drive app. + fake_drive_service()->AddApp( + kDriveAppId, kDriveAppName, kChromeAppId, kLaunchUrl, false); + RefreshDriveAppRegistry(); + WaitForPendingDriveAppConverters(); + + // The Drive app is present in the system. + std::string chrome_app_id = mapping()->GetChromeApp(kDriveAppId); + EXPECT_FALSE(chrome_app_id.empty()); + EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById( + chrome_app_id, ExtensionRegistry::EVERYTHING)); + + // Uninstall is added from sync and the app is removed. + provider()->AddUninstalledDriveAppFromSync(kDriveAppId); + WaitForPendingDriveAppConverters(); + chrome_app_id = mapping()->GetChromeApp(kDriveAppId); + EXPECT_TRUE(chrome_app_id.empty()); + EXPECT_FALSE(ExtensionRegistry::Get(profile())->GetExtensionById( + chrome_app_id, ExtensionRegistry::EVERYTHING)); + + // Uninstall is removed from sync and the app is added again. + provider()->RemoveUninstalledDriveAppFromSync(kDriveAppId); + WaitForPendingDriveAppConverters(); + chrome_app_id = mapping()->GetChromeApp(kDriveAppId); + EXPECT_FALSE(chrome_app_id.empty()); + EXPECT_TRUE(ExtensionRegistry::Get(profile())->GetExtensionById( + chrome_app_id, ExtensionRegistry::EVERYTHING)); +} diff --git a/chrome/browser/apps/drive/drive_app_uninstall_sync_service.h b/chrome/browser/apps/drive/drive_app_uninstall_sync_service.h new file mode 100644 index 0000000..1e89c25 --- /dev/null +++ b/chrome/browser/apps/drive/drive_app_uninstall_sync_service.h @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_UNINSTALL_SYNC_SERVICE_H_ +#define CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_UNINSTALL_SYNC_SERVICE_H_ + +class DriveAppUninstallSyncService { + public: + // Invoked when user uninstalls a default Drive app. + virtual void TrackUninstalledDriveApp( + const std::string& drive_app_id) = 0; + + // Invoked when user installs a default Drive app. + virtual void UntrackUninstalledDriveApp( + const std::string& drive_app_id) = 0; + + protected: + virtual ~DriveAppUninstallSyncService() {} +}; + +#endif // CHROME_BROWSER_APPS_DRIVE_DRIVE_APP_UNINSTALL_SYNC_SERVICE_H_ diff --git a/chrome/browser/drive/fake_drive_service.cc b/chrome/browser/drive/fake_drive_service.cc index ede7308..d13c1a8 100644 --- a/chrome/browser/drive/fake_drive_service.cc +++ b/chrome/browser/drive/fake_drive_service.cc @@ -236,7 +236,8 @@ bool FakeDriveService::LoadAppListForDriveApi( void FakeDriveService::AddApp(const std::string& app_id, const std::string& app_name, const std::string& product_id, - const std::string& create_url) { + const std::string& create_url, + bool is_removable) { if (app_json_template_.empty()) { base::FilePath path = test_util::GetTestFilePath("drive/applist_app_template.json"); @@ -248,6 +249,8 @@ void FakeDriveService::AddApp(const std::string& app_id, ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name); ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id); ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url); + ReplaceSubstringsAfterOffset( + &app_json, 0, "$Removable", is_removable ? "true" : "false"); JSONStringValueSerializer json(app_json); std::string error_message; diff --git a/chrome/browser/drive/fake_drive_service.h b/chrome/browser/drive/fake_drive_service.h index 856f80b..37ee215 100644 --- a/chrome/browser/drive/fake_drive_service.h +++ b/chrome/browser/drive/fake_drive_service.h @@ -44,7 +44,8 @@ class FakeDriveService : public DriveServiceInterface { void AddApp(const std::string& app_id, const std::string& app_name, const std::string& product_id, - const std::string& create_url); + const std::string& create_url, + bool is_removable); // Removes an app by product id. void RemoveAppByProductId(const std::string& product_id); diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc index 9333902..f0e7fcf 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.cc +++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/app_list/app_list_syncable_service.h" #include "base/command_line.h" +#include "base/strings/string_util.h" #include "chrome/browser/apps/drive/drive_app_provider.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service.h" @@ -47,6 +48,13 @@ namespace { const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263"; +// Prefix for a sync id of a Drive app. Drive app ids are in a different +// format and have to be used because a Drive app could have only an URL +// without a matching Chrome app. To differentiate the Drive app id from +// Chrome app ids, this prefix will be added to create the sync item id +// for a Drive app item. +const char kDriveAppSyncIdPrefix[] = "drive-app-"; + void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics, AppListSyncableService::SyncItem* item) { DCHECK_EQ(item->item_id, specifics.item_id()); @@ -137,6 +145,20 @@ bool GetAppListItemType(AppListItem* item, return true; } +bool IsDriveAppSyncId(const std::string& sync_id) { + return StartsWithASCII(sync_id, kDriveAppSyncIdPrefix, true); +} + +std::string GetDriveAppSyncId(const std::string& drive_app_id) { + return kDriveAppSyncIdPrefix + drive_app_id; +} + +std::string GetDriveAppIdFromSyncId(const std::string& sync_id) { + if (!IsDriveAppSyncId(sync_id)) + return std::string(); + return sync_id.substr(strlen(kDriveAppSyncIdPrefix)); +} + } // namespace // AppListSyncableService::SyncItem @@ -270,7 +292,7 @@ void AppListSyncableService::BuildModel() { new ModelPrefUpdater(AppListPrefs::Get(profile_), model_.get())); if (app_list::switches::IsDriveAppsInAppListEnabled()) - drive_app_provider_.reset(new DriveAppProvider(profile_)); + drive_app_provider_.reset(new DriveAppProvider(profile_, this)); } void AppListSyncableService::ResetDriveAppProviderForTest() { @@ -283,6 +305,33 @@ void AppListSyncableService::Shutdown() { drive_app_provider_.reset(); } +void AppListSyncableService::TrackUninstalledDriveApp( + const std::string& drive_app_id) { + const std::string sync_id = GetDriveAppSyncId(drive_app_id); + SyncItem* sync_item = FindSyncItem(sync_id); + if (sync_item) { + DCHECK_EQ(sync_item->item_type, + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP); + return; + } + + sync_item = CreateSyncItem( + sync_id, sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP); + SendSyncChange(sync_item, SyncChange::ACTION_ADD); +} + +void AppListSyncableService::UntrackUninstalledDriveApp( + const std::string& drive_app_id) { + const std::string sync_id = GetDriveAppSyncId(drive_app_id); + SyncItem* sync_item = FindSyncItem(sync_id); + if (!sync_item) + return; + + DCHECK_EQ(sync_item->item_type, + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP); + DeleteSyncItem(sync_item); +} + void AppListSyncableService::Observe( int type, const content::NotificationSource& source, @@ -716,8 +765,15 @@ void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) { } case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: { VLOG(1) << this << ": Uninstall: " << sync_item->ToString(); - UninstallExtension(extension_system_->extension_service(), - sync_item->item_id); + if (IsDriveAppSyncId(sync_item->item_id)) { + if (drive_app_provider_) { + drive_app_provider_->AddUninstalledDriveAppFromSync( + GetDriveAppIdFromSyncId(sync_item->item_id)); + } + } else { + UninstallExtension(extension_system_->extension_service(), + sync_item->item_id); + } return; } case sync_pb::AppListSpecifics::TYPE_FOLDER: { @@ -848,8 +904,14 @@ void AppListSyncableService::DeleteSyncItemSpecifics( sync_items_.erase(iter); // Only delete apps from the model. Folders will be deleted when all // children have been deleted. - if (item_type == sync_pb::AppListSpecifics::TYPE_APP) + if (item_type == sync_pb::AppListSpecifics::TYPE_APP) { model_->DeleteItem(item_id); + } else if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) { + if (IsDriveAppSyncId(item_id) && drive_app_provider_) { + drive_app_provider_->RemoveUninstalledDriveAppFromSync( + GetDriveAppIdFromSyncId(item_id)); + } + } } std::string AppListSyncableService::FindOrCreateOemFolder() { diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h index f929fb7..49ded4c 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.h +++ b/chrome/browser/ui/app_list/app_list_syncable_service.h @@ -8,6 +8,7 @@ #include <map> #include "base/memory/scoped_ptr.h" +#include "chrome/browser/apps/drive/drive_app_uninstall_sync_service.h" #include "chrome/browser/sync/glue/sync_start_util.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/notification_observer.h" @@ -41,6 +42,7 @@ class ModelPrefUpdater; // Keyed Service that owns, stores, and syncs an AppListModel for a profile. class AppListSyncableService : public syncer::SyncableService, public KeyedService, + public DriveAppUninstallSyncService, public content::NotificationObserver { public: struct SyncItem { @@ -106,6 +108,12 @@ class AppListSyncableService : public syncer::SyncableService, // KeyedService virtual void Shutdown() override; + // DriveAppUninstallSyncService + virtual void TrackUninstalledDriveApp( + const std::string& drive_app_id) override; + virtual void UntrackUninstalledDriveApp( + const std::string& drive_app_id) override; + // content::NotificationObserver virtual void Observe(int type, const content::NotificationSource& source, diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 97d2004..4a35e80 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -839,6 +839,7 @@ 'browser/apps/drive/drive_app_mapping.h', 'browser/apps/drive/drive_app_provider.cc', 'browser/apps/drive/drive_app_provider.h', + 'browser/apps/drive/drive_app_uninstall_sync_service.h', 'browser/apps/drive/drive_service_bridge.cc', 'browser/apps/drive/drive_service_bridge.h', ], diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 4d766bb..b28a53d 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -2291,6 +2291,10 @@ const char kShowAppLauncherPromo[] = "app_launcher.show_promo"; // is unsynable and used to track local mappings only. const char kAppLauncherDriveAppMapping[] = "apps.app_launcher.drive_app_mapping"; + +// A list of Drive app ids that tracks the uninstallable Drive apps. +const char kAppLauncherUninstalledDriveApps[] = + "apps.app_launcher.uninstalled_drive_apps"; #endif // If set, the user requested to launch the app with this extension id while diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index bf67943..0c98324 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -797,6 +797,7 @@ extern const char kAppLauncherIsEnabled[]; extern const char kAppLauncherShortcutVersion[]; extern const char kShowAppLauncherPromo[]; extern const char kAppLauncherDriveAppMapping[]; +extern const char kAppLauncherUninstalledDriveApps[]; #endif extern const char kAppLaunchForMetroRestart[]; diff --git a/chrome/test/data/drive/applist_app_template.json b/chrome/test/data/drive/applist_app_template.json index 3ce970b..daa5a4d 100644 --- a/chrome/test/data/drive/applist_app_template.json +++ b/chrome/test/data/drive/applist_app_template.json @@ -7,7 +7,7 @@ "supportsImport": false, "installed": true, "authorized": false, - "removable": false, + "removable": $Removable, "productUrl": "https://chrome.google.com/webstore/detail/$ProductId", "productId": "$ProductId", "createUrl": "$CreateUrl", |