• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
IdToHash(const std::string 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 
ExtensionBackedDataTypeController(syncer::ModelType type,const std::string & extension_hash,sync_driver::SyncApiComponentFactory * sync_factory,Profile * profile)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 
~ExtensionBackedDataTypeController()49 ExtensionBackedDataTypeController::~ExtensionBackedDataTypeController() {
50   extensions::ExtensionRegistry* registry =
51       extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
52   registry->RemoveObserver(this);
53 }
54 
ReadyForStart() const55 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 
OnExtensionLoaded(content::BrowserContext * browser_context,const extensions::Extension * extension)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 
OnExtensionUnloaded(content::BrowserContext * browser_context,const extensions::Extension * extension,extensions::UnloadedExtensionInfo::Reason reason)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 
IsSyncingExtensionEnabled() const89 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 
DoesExtensionMatch(const extensions::Extension & extension) const109 bool ExtensionBackedDataTypeController::DoesExtensionMatch(
110     const extensions::Extension& extension) const {
111   return IdToHash(extension.id()) == extension_hash_;
112 }
113 
StartModels()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