blob: de372bc9e3b8214095d81d1ecfaf6f05bef89493 [file] [log] [blame]
// 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.
#include "chrome/browser/sync/glue/extension_backed_data_type_controller.h"
#include "base/sha1.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
using content::BrowserThread;
namespace browser_sync {
namespace {
// Helper method to generate a hash from an extension id.
std::string IdToHash(const std::string extension_id) {
std::string hash = base::SHA1HashString(extension_id);
hash = base::HexEncode(hash.c_str(), hash.length());
return hash;
}
} // namespace
ExtensionBackedDataTypeController::ExtensionBackedDataTypeController(
const DisableTypeCallback& disable_callback,
syncer::ModelType type,
const std::string& extension_hash,
SyncApiComponentFactory* sync_factory,
Profile* profile)
: UIDataTypeController(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
base::Bind(&ChromeReportUnrecoverableError),
disable_callback,
type,
sync_factory),
extension_hash_(extension_hash),
profile_(profile) {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
registry->AddObserver(this);
}
ExtensionBackedDataTypeController::~ExtensionBackedDataTypeController() {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
registry->RemoveObserver(this);
}
bool ExtensionBackedDataTypeController::ReadyForStart() const {
// TODO(zea): consider checking if the extension was uninstalled without
// sync noticing, in which case the datatype should be purged.
return IsSyncingExtensionEnabled();
}
void ExtensionBackedDataTypeController::OnExtensionLoaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension) {
if (DoesExtensionMatch(*extension)) {
ProfileSyncService* sync_service =
ProfileSyncServiceFactory::GetForProfile(profile_);
sync_service->ReenableDatatype(type());
}
}
void ExtensionBackedDataTypeController::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionInfo::Reason reason) {
if (DoesExtensionMatch(*extension)) {
ProfileSyncService* sync_service =
ProfileSyncServiceFactory::GetForProfile(profile_);
// This will trigger purging the datatype from the sync directory. This
// may not always be the right choice, especially in the face of transient
// unloads (e.g. for permission changes). If that becomes a large enough
// issue, we should consider using the extension unload reason to just
// trigger a reconfiguration without disabling (type will be unready).
sync_service->DisableDatatype(type(),
FROM_HERE,
"Extension unloaded");
}
}
bool ExtensionBackedDataTypeController::IsSyncingExtensionEnabled() const {
if (extension_hash_.empty())
return true; // For use while extension is in development.
// TODO(synced notifications): rather than rely on a hash of the extension
// id, look through the manifests to see if the extension has permissions
// for this model type.
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
const extensions::ExtensionSet& enabled_extensions(
registry->enabled_extensions());
for (extensions::ExtensionSet::const_iterator iter =
enabled_extensions.begin();
iter != enabled_extensions.end(); ++iter) {
if (DoesExtensionMatch(*iter->get()))
return true;
}
return false;
}
bool ExtensionBackedDataTypeController::DoesExtensionMatch(
const extensions::Extension& extension) const {
return IdToHash(extension.id()) == extension_hash_;
}
bool ExtensionBackedDataTypeController::StartModels() {
if (IsSyncingExtensionEnabled())
return true;
// If the extension is not currently enabled, it means that it has been
// disabled since the call to ReadyForStart(), and the notification
// observer should have already posted a call to disable the type.
return false;
}
} // namespace browser_sync