| Index: chrome/browser/extensions/extension_service.cc
|
| diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
|
| index c2f64316ba7f11e6c16f6035067e36f1cc6b359a..cc9435e82d406aa311c490455f41b6207eb6cfb0 100644
|
| --- a/chrome/browser/extensions/extension_service.cc
|
| +++ b/chrome/browser/extensions/extension_service.cc
|
| @@ -53,6 +53,7 @@
|
| #include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/search_engines/template_url_service.h"
|
| #include "chrome/browser/search_engines/template_url_service_factory.h"
|
| +#include "chrome/browser/sync/api/sync_change.h"
|
| #include "chrome/browser/themes/theme_service.h"
|
| #include "chrome/browser/themes/theme_service_factory.h"
|
| #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
|
| @@ -151,6 +152,14 @@ static void ForceShutdownPlugin(const FilePath& plugin_path) {
|
| plugin->ForceShutdown();
|
| }
|
|
|
| +static bool IsSyncableExtension(const Extension& extension) {
|
| + return extension.GetSyncType() == Extension::SYNC_TYPE_EXTENSION;
|
| +}
|
| +
|
| +static bool IsSyncableApp(const Extension& extension) {
|
| + return extension.GetSyncType() == Extension::SYNC_TYPE_APP;
|
| +}
|
| +
|
| // Manages an ExtensionInstallUI for a particular extension.
|
| class SimpleExtensionLoadPrompt : public ExtensionInstallUI::Delegate {
|
| public:
|
| @@ -517,8 +526,9 @@ const Extension* ExtensionService::GetInstalledAppForRenderer(
|
|
|
| // static
|
| // This function is used to implement the command-line switch
|
| -// --uninstall-extension. The LOG statements within this function are used to
|
| -// inform the user if the uninstall cannot be done.
|
| +// --uninstall-extension, and to uninstall an extension via sync. The LOG
|
| +// statements within this function are used to inform the user if the uninstall
|
| +// cannot be done.
|
| bool ExtensionService::UninstallExtensionHelper(
|
| ExtensionService* extensions_service,
|
| const std::string& extension_id) {
|
| @@ -855,6 +865,18 @@ bool ExtensionService::UninstallExtension(
|
| return false;
|
| }
|
|
|
| + // Extract the data we need for sync now, but don't actually sync until we've
|
| + // completed the uninstallation.
|
| + SyncBundle* sync_bundle = GetSyncBundleForExtension(*extension);
|
| +
|
| + SyncChange sync_change;
|
| + if (sync_bundle) {
|
| + ExtensionSyncData extension_sync_data(*extension,
|
| + IsExtensionEnabled(extension_id),
|
| + IsIncognitoEnabled(extension_id));
|
| + sync_change = extension_sync_data.GetSyncChange(SyncChange::ACTION_DELETE);
|
| + }
|
| +
|
| UninstalledExtensionInfo uninstalled_extension_info(*extension);
|
|
|
| UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallType",
|
| @@ -894,6 +916,12 @@ bool ExtensionService::UninstallExtension(
|
| Source<Profile>(profile_),
|
| Details<UninstalledExtensionInfo>(&uninstalled_extension_info));
|
|
|
| + if (sync_bundle && sync_bundle->HasExtensionId(extension_id)) {
|
| + sync_bundle->sync_processor->ProcessSyncChanges(
|
| + FROM_HERE, SyncChangeList(1, sync_change));
|
| + sync_bundle->synced_extensions.erase(extension_id);
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| @@ -952,6 +980,8 @@ void ExtensionService::EnableExtension(const std::string& extension_id) {
|
| extension_prefs_->SetBrowserActionVisibility(extension, true);
|
|
|
| NotifyExtensionLoaded(extension);
|
| +
|
| + SyncExtensionChangeIfNeeded(*extension);
|
| }
|
|
|
| void ExtensionService::DisableExtension(const std::string& extension_id) {
|
| @@ -988,6 +1018,8 @@ void ExtensionService::DisableExtension(const std::string& extension_id) {
|
| }
|
|
|
| NotifyExtensionUnloaded(extension, extension_misc::UNLOAD_REASON_DISABLE);
|
| +
|
| + SyncExtensionChangeIfNeeded(*extension);
|
| }
|
|
|
| void ExtensionService::GrantPermissions(const Extension* extension) {
|
| @@ -1620,60 +1652,224 @@ void ExtensionService::CheckForUpdatesSoon() {
|
| }
|
| }
|
|
|
| -ExtensionSyncData ExtensionService::GetSyncDataHelper(
|
| - const Extension& extension) const {
|
| - const std::string& id = extension.id();
|
| - ExtensionSyncData data;
|
| - data.id = id;
|
| - data.uninstalled = false;
|
| - data.enabled = IsExtensionEnabled(id);
|
| - data.incognito_enabled = IsIncognitoEnabled(id);
|
| - data.version = *extension.version();
|
| - data.update_url = extension.update_url();
|
| - data.name = extension.name();
|
| - return data;
|
| +namespace {
|
| + bool IsSyncableNone(const Extension& extension) { return false; }
|
| +} // namespace
|
| +
|
| +ExtensionService::SyncBundle::SyncBundle()
|
| + : filter(IsSyncableNone),
|
| + sync_processor(NULL) {
|
| }
|
|
|
| -bool ExtensionService::GetSyncData(
|
| - const Extension& extension,
|
| - ExtensionFilter filter,
|
| - ExtensionSyncData* extension_sync_data) const {
|
| - if (!(*filter)(extension)) {
|
| - return false;
|
| +ExtensionService::SyncBundle::~SyncBundle() {
|
| +}
|
| +
|
| +bool ExtensionService::SyncBundle::HasExtensionId(const std::string& id) const {
|
| + return synced_extensions.find(id) != synced_extensions.end();
|
| +}
|
| +
|
| +bool ExtensionService::SyncBundle::HasPendingExtensionId(const std::string& id)
|
| + const {
|
| + return pending_sync_data.find(id) != pending_sync_data.end();
|
| +}
|
| +
|
| +void ExtensionService::SyncExtensionChangeIfNeeded(const Extension& extension) {
|
| + SyncBundle* sync_bundle = GetSyncBundleForExtension(extension);
|
| + if (sync_bundle) {
|
| + ExtensionSyncData extension_sync_data(extension,
|
| + IsExtensionEnabled(extension.id()),
|
| + IsIncognitoEnabled(extension.id()));
|
| +
|
| + SyncChangeList sync_change_list(1, extension_sync_data.GetSyncChange(
|
| + sync_bundle->HasExtensionId(extension.id()) ?
|
| + SyncChange::ACTION_UPDATE : SyncChange::ACTION_ADD));
|
| + sync_bundle->sync_processor->ProcessSyncChanges(
|
| + FROM_HERE, sync_change_list);
|
| + sync_bundle->synced_extensions.insert(extension.id());
|
| + sync_bundle->pending_sync_data.erase(extension.id());
|
| }
|
| - *extension_sync_data = GetSyncDataHelper(extension);
|
| - return true;
|
| +}
|
| +
|
| +ExtensionService::SyncBundle* ExtensionService::GetSyncBundleForExtension(
|
| + const Extension& extension) {
|
| + if (app_sync_bundle_.filter(extension))
|
| + return &app_sync_bundle_;
|
| + else if (extension_sync_bundle_.filter(extension))
|
| + return &extension_sync_bundle_;
|
| + else
|
| + return NULL;
|
| +}
|
| +
|
| +ExtensionService::SyncBundle*
|
| + ExtensionService::GetSyncBundleForExtensionSyncData(
|
| + const ExtensionSyncData& extension_sync_data) {
|
| + switch (extension_sync_data.type()) {
|
| + case Extension::SYNC_TYPE_APP:
|
| + return &app_sync_bundle_;
|
| + case Extension::SYNC_TYPE_EXTENSION:
|
| + return &extension_sync_bundle_;
|
| + default:
|
| + NOTREACHED();
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +#define GET_SYNC_BUNDLE_FOR_MODEL_TYPE_BODY() \
|
| + do { \
|
| + switch (type) { \
|
| + case syncable::APPS: \
|
| + return &app_sync_bundle_; \
|
| + case syncable::EXTENSIONS: \
|
| + return &extension_sync_bundle_; \
|
| + default: \
|
| + NOTREACHED(); \
|
| + return NULL; \
|
| + } \
|
| + } while (0)
|
| +
|
| +const ExtensionService::SyncBundle*
|
| + ExtensionService::GetSyncBundleForModelTypeConst(
|
| + syncable::ModelType type) const {
|
| + GET_SYNC_BUNDLE_FOR_MODEL_TYPE_BODY();
|
| +}
|
| +
|
| +ExtensionService::SyncBundle* ExtensionService::GetSyncBundleForModelType(
|
| + syncable::ModelType type) {
|
| + GET_SYNC_BUNDLE_FOR_MODEL_TYPE_BODY();
|
| +}
|
| +
|
| +#undef GET_SYNC_BUNDLE_FOR_MODEL_TYPE_BODY
|
| +
|
| +SyncError ExtensionService::MergeDataAndStartSyncing(
|
| + syncable::ModelType type,
|
| + const SyncDataList& initial_sync_data,
|
| + SyncChangeProcessor* sync_processor) {
|
| + CHECK(sync_processor);
|
| +
|
| + SyncBundle* bundle = NULL;
|
| +
|
| + switch (type) {
|
| + case syncable::EXTENSIONS:
|
| + bundle = &extension_sync_bundle_;
|
| + bundle->filter = IsSyncableExtension;
|
| + break;
|
| +
|
| + case syncable::APPS:
|
| + bundle = &app_sync_bundle_;
|
| + bundle->filter = IsSyncableApp;
|
| + break;
|
| +
|
| + default:
|
| + LOG(FATAL) << "Got " << type << " ModelType";
|
| + }
|
| +
|
| + bundle->sync_processor = sync_processor;
|
| +
|
| + for (SyncDataList::const_iterator i = initial_sync_data.begin();
|
| + i != initial_sync_data.end();
|
| + ++i) {
|
| + ExtensionSyncData extension_sync_data = ExtensionSyncData(*i);
|
| + bundle->synced_extensions.insert(extension_sync_data.id());
|
| + ProcessExtensionSyncData(extension_sync_data, *bundle);
|
| + }
|
| +
|
| + SyncDataList sync_data_list = GetAllSyncData(type);
|
| + SyncChangeList sync_change_list;
|
| + for (SyncDataList::const_iterator i = sync_data_list.begin();
|
| + i != sync_data_list.end();
|
| + ++i) {
|
| + if (bundle->HasExtensionId(i->GetTag()))
|
| + sync_change_list.push_back(SyncChange(SyncChange::ACTION_UPDATE, *i));
|
| + else
|
| + sync_change_list.push_back(SyncChange(SyncChange::ACTION_ADD, *i));
|
| + }
|
| + bundle->sync_processor->ProcessSyncChanges(FROM_HERE, sync_change_list);
|
| +
|
| + return SyncError();
|
| +}
|
| +
|
| +void ExtensionService::StopSyncing(syncable::ModelType type) {
|
| + SyncBundle* bundle = GetSyncBundleForModelType(type);
|
| + CHECK(bundle);
|
| + // This is the simplest way to clear out the bundle.
|
| + *bundle = SyncBundle();
|
| +}
|
| +
|
| +SyncDataList ExtensionService::GetAllSyncData(syncable::ModelType type) const {
|
| + const SyncBundle* bundle = GetSyncBundleForModelTypeConst(type);
|
| + CHECK(bundle);
|
| + std::vector<ExtensionSyncData> extension_sync_data = GetSyncDataList(*bundle);
|
| + SyncDataList result(extension_sync_data.size());
|
| + for (int i = 0; i < static_cast<int>(extension_sync_data.size()); ++i) {
|
| + result[i] = extension_sync_data[i].GetSyncData();
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +SyncError ExtensionService::ProcessSyncChanges(
|
| + const tracked_objects::Location& from_here,
|
| + const SyncChangeList& change_list) {
|
| + for (SyncChangeList::const_iterator i = change_list.begin();
|
| + i != change_list.end();
|
| + ++i) {
|
| + ExtensionSyncData extension_sync_data = ExtensionSyncData(*i);
|
| + SyncBundle* bundle = GetSyncBundleForExtensionSyncData(extension_sync_data);
|
| + CHECK(bundle);
|
| +
|
| + if (extension_sync_data.uninstalled())
|
| + bundle->synced_extensions.erase(extension_sync_data.id());
|
| + else
|
| + bundle->synced_extensions.insert(extension_sync_data.id());
|
| + ProcessExtensionSyncData(extension_sync_data, *bundle);
|
| + }
|
| +
|
| + return SyncError();
|
| }
|
|
|
| void ExtensionService::GetSyncDataListHelper(
|
| const ExtensionList& extensions,
|
| - ExtensionFilter filter,
|
| + const SyncBundle& bundle,
|
| std::vector<ExtensionSyncData>* sync_data_list) const {
|
| for (ExtensionList::const_iterator it = extensions.begin();
|
| it != extensions.end(); ++it) {
|
| const Extension& extension = **it;
|
| - if ((*filter)(extension)) {
|
| - sync_data_list->push_back(GetSyncDataHelper(extension));
|
| + if (bundle.filter(extension) &&
|
| + // If we have pending extension data for this extension, then this
|
| + // version is out of date. We'll sync back the version we got from
|
| + // sync.
|
| + !bundle.HasPendingExtensionId(extension.id())) {
|
| + sync_data_list->push_back(
|
| + ExtensionSyncData(extension,
|
| + IsExtensionEnabled(extension.id()),
|
| + IsIncognitoEnabled(extension.id())));
|
| }
|
| }
|
| }
|
|
|
| std::vector<ExtensionSyncData> ExtensionService::GetSyncDataList(
|
| - ExtensionFilter filter) const {
|
| - std::vector<ExtensionSyncData> sync_data_list;
|
| - GetSyncDataListHelper(extensions_, filter, &sync_data_list);
|
| - GetSyncDataListHelper(disabled_extensions_, filter, &sync_data_list);
|
| - GetSyncDataListHelper(terminated_extensions_, filter, &sync_data_list);
|
| - return sync_data_list;
|
| + const SyncBundle& bundle) const {
|
| + std::vector<ExtensionSyncData> extension_sync_list;
|
| + GetSyncDataListHelper(extensions_, bundle, &extension_sync_list);
|
| + GetSyncDataListHelper(disabled_extensions_, bundle, &extension_sync_list);
|
| + GetSyncDataListHelper(terminated_extensions_, bundle, &extension_sync_list);
|
| +
|
| + for (std::map<std::string, ExtensionSyncData>::const_iterator i =
|
| + bundle.pending_sync_data.begin();
|
| + i != bundle.pending_sync_data.end();
|
| + ++i) {
|
| + extension_sync_list.push_back(i->second);
|
| + }
|
| +
|
| + return extension_sync_list;
|
| }
|
|
|
| -void ExtensionService::ProcessSyncData(
|
| +void ExtensionService::ProcessExtensionSyncData(
|
| const ExtensionSyncData& extension_sync_data,
|
| - ExtensionFilter filter) {
|
| - const std::string& id = extension_sync_data.id;
|
| + SyncBundle& bundle) {
|
| + const std::string& id = extension_sync_data.id();
|
|
|
| // Handle uninstalls first.
|
| - if (extension_sync_data.uninstalled) {
|
| + if (extension_sync_data.uninstalled()) {
|
| std::string error;
|
| if (!UninstallExtensionHelper(this, id)) {
|
| LOG(WARNING) << "Could not uninstall extension " << id
|
| @@ -1683,25 +1879,21 @@ void ExtensionService::ProcessSyncData(
|
| }
|
|
|
| // Set user settings.
|
| - if (extension_sync_data.enabled) {
|
| + if (extension_sync_data.enabled()) {
|
| EnableExtension(id);
|
| } else {
|
| DisableExtension(id);
|
| }
|
| - SetIsIncognitoEnabled(id, extension_sync_data.incognito_enabled);
|
| + SetIsIncognitoEnabled(id, extension_sync_data.incognito_enabled());
|
|
|
| const Extension* extension = GetInstalledExtension(id);
|
| if (extension) {
|
| // If the extension is already installed, check if it's outdated.
|
| - int result = extension->version()->CompareTo(extension_sync_data.version);
|
| + int result = extension->version()->CompareTo(extension_sync_data.version());
|
| if (result < 0) {
|
| // Extension is outdated.
|
| + bundle.pending_sync_data[extension_sync_data.id()] = extension_sync_data;
|
| CheckForUpdatesSoon();
|
| - } else if (result > 0) {
|
| - // Sync version is outdated. Do nothing for now, as sync code
|
| - // in other places will eventually update the sync data.
|
| - //
|
| - // TODO(akalin): Move that code here.
|
| }
|
| } else {
|
| // TODO(akalin): Replace silent update with a list of enabled
|
| @@ -1709,12 +1901,17 @@ void ExtensionService::ProcessSyncData(
|
| const bool kInstallSilently = true;
|
| if (!pending_extension_manager()->AddFromSync(
|
| id,
|
| - extension_sync_data.update_url,
|
| - filter,
|
| + extension_sync_data.update_url(),
|
| + bundle.filter,
|
| kInstallSilently)) {
|
| LOG(WARNING) << "Could not add pending extension for " << id;
|
| - return;
|
| + // This means that the extension is already pending installation, with a
|
| + // non-INTERNAL location. Add to pending_sync_data, even though it will
|
| + // never be removed (we'll never install a syncable version of the
|
| + // extension), so that GetAllSyncData() continues to send it.
|
| }
|
| + // Track pending extensions so that we can return them in GetAllSyncData().
|
| + bundle.pending_sync_data[extension_sync_data.id()] = extension_sync_data;
|
| CheckForUpdatesSoon();
|
| }
|
| }
|
| @@ -1757,6 +1954,9 @@ void ExtensionService::SetIsIncognitoEnabled(
|
| enabled_extension, extension_misc::UNLOAD_REASON_DISABLE);
|
| NotifyExtensionLoaded(enabled_extension);
|
| }
|
| +
|
| + if (extension)
|
| + SyncExtensionChangeIfNeeded(*extension);
|
| }
|
|
|
| bool ExtensionService::CanCrossIncognito(const Extension* extension) {
|
| @@ -2026,10 +2226,12 @@ void ExtensionService::AddExtension(const Extension* extension) {
|
| chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
|
| Source<Profile>(profile_),
|
| Details<const Extension>(extension));
|
| + SyncExtensionChangeIfNeeded(*extension);
|
| return;
|
| }
|
|
|
| extensions_.push_back(scoped_extension);
|
| + SyncExtensionChangeIfNeeded(*extension);
|
| NotifyExtensionLoaded(extension);
|
| }
|
|
|
|
|