Home | History | Annotate | Download | only in glue
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sync/glue/extension_backed_data_type_controller.h"
      6 
      7 #include "base/sha1.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
     11 #include "chrome/browser/sync/profile_sync_service.h"
     12 #include "chrome/browser/sync/profile_sync_service_factory.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "extensions/browser/extension_registry.h"
     15 #include "extensions/browser/extension_registry_factory.h"
     16 
     17 using content::BrowserThread;
     18 
     19 namespace browser_sync {
     20 
     21 namespace {
     22 
     23 // Helper method to generate a hash from an extension id.
     24 std::string IdToHash(const std::string extension_id) {
     25   std::string hash = base::SHA1HashString(extension_id);
     26   hash = base::HexEncode(hash.c_str(), hash.length());
     27   return hash;
     28 }
     29 
     30 }  // namespace
     31 
     32 ExtensionBackedDataTypeController::ExtensionBackedDataTypeController(
     33     syncer::ModelType type,
     34     const std::string& extension_hash,
     35     sync_driver::SyncApiComponentFactory* sync_factory,
     36     Profile* profile)
     37     : UIDataTypeController(
     38           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
     39           base::Bind(&ChromeReportUnrecoverableError),
     40           type,
     41           sync_factory),
     42       extension_hash_(extension_hash),
     43       profile_(profile) {
     44   extensions::ExtensionRegistry* registry =
     45       extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
     46   registry->AddObserver(this);
     47 }
     48 
     49 ExtensionBackedDataTypeController::~ExtensionBackedDataTypeController() {
     50   extensions::ExtensionRegistry* registry =
     51       extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
     52   registry->RemoveObserver(this);
     53 }
     54 
     55 bool ExtensionBackedDataTypeController::ReadyForStart() const {
     56   // TODO(zea): consider checking if the extension was uninstalled without
     57   // sync noticing, in which case the datatype should be purged.
     58   return IsSyncingExtensionEnabled();
     59 }
     60 
     61 void ExtensionBackedDataTypeController::OnExtensionLoaded(
     62     content::BrowserContext* browser_context,
     63     const extensions::Extension* extension) {
     64   if (DoesExtensionMatch(*extension)) {
     65     ProfileSyncService* sync_service =
     66         ProfileSyncServiceFactory::GetForProfile(profile_);
     67     sync_service->ReenableDatatype(type());
     68   }
     69 }
     70 
     71 void ExtensionBackedDataTypeController::OnExtensionUnloaded(
     72     content::BrowserContext* browser_context,
     73     const extensions::Extension* extension,
     74     extensions::UnloadedExtensionInfo::Reason reason) {
     75   if (DoesExtensionMatch(*extension)) {
     76     // This will trigger purging the datatype from the sync directory. This
     77     // may not always be the right choice, especially in the face of transient
     78     // unloads (e.g. for permission changes). If that becomes a large enough
     79     // issue, we should consider using the extension unload reason to just
     80     // trigger a reconfiguration without disabling (type will be unready).
     81     syncer::SyncError error(FROM_HERE,
     82                             syncer::SyncError::DATATYPE_POLICY_ERROR,
     83                             "Extension unloaded",
     84                             type());
     85     OnSingleDataTypeUnrecoverableError(error);
     86   }
     87 }
     88 
     89 bool ExtensionBackedDataTypeController::IsSyncingExtensionEnabled() const {
     90   if (extension_hash_.empty())
     91     return true;  // For use while extension is in development.
     92 
     93   // TODO(synced notifications): rather than rely on a hash of the extension
     94   // id, look through the manifests to see if the extension has permissions
     95   // for this model type.
     96   extensions::ExtensionRegistry* registry =
     97       extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
     98   const extensions::ExtensionSet& enabled_extensions(
     99       registry->enabled_extensions());
    100   for (extensions::ExtensionSet::const_iterator iter =
    101            enabled_extensions.begin();
    102        iter != enabled_extensions.end(); ++iter) {
    103     if (DoesExtensionMatch(*iter->get()))
    104       return true;
    105   }
    106   return false;
    107 }
    108 
    109 bool ExtensionBackedDataTypeController::DoesExtensionMatch(
    110     const extensions::Extension& extension) const {
    111   return IdToHash(extension.id()) == extension_hash_;
    112 }
    113 
    114 bool ExtensionBackedDataTypeController::StartModels() {
    115   if (IsSyncingExtensionEnabled())
    116     return true;
    117 
    118   // If the extension is not currently enabled, it means that it has been
    119   // disabled since the call to ReadyForStart(), and the notification
    120   // observer should have already posted a call to disable the type.
    121   return false;
    122 }
    123 
    124 }  // namespace browser_sync
    125