summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping.cc44
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping.h8
-rw-r--r--chrome/browser/apps/drive/drive_app_mapping_unittest.cc10
-rw-r--r--chrome/browser/apps/drive/drive_app_provider.cc54
-rw-r--r--chrome/browser/apps/drive/drive_app_provider.h14
-rw-r--r--chrome/browser/apps/drive/drive_app_provider_browsertest.cc154
-rw-r--r--chrome/browser/apps/drive/drive_app_uninstall_sync_service.h22
-rw-r--r--chrome/browser/drive/fake_drive_service.cc5
-rw-r--r--chrome/browser/drive/fake_drive_service.h3
-rw-r--r--chrome/browser/ui/app_list/app_list_syncable_service.cc70
-rw-r--r--chrome/browser/ui/app_list/app_list_syncable_service.h8
-rw-r--r--chrome/chrome_browser_extensions.gypi1
-rw-r--r--chrome/common/pref_names.cc4
-rw-r--r--chrome/common/pref_names.h1
-rw-r--r--chrome/test/data/drive/applist_app_template.json2
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",