• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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_change_processor.h"
6 
7 #include <sstream>
8 #include <string>
9 
10 #include "base/logging.h"
11 #include "base/stl_util-inl.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_sync_data.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sync/glue/extension_sync.h"
16 #include "chrome/browser/sync/glue/extension_util.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/protocol/extension_specifics.pb.h"
19 #include "chrome/common/extensions/extension.h"
20 #include "content/browser/browser_thread.h"
21 #include "content/common/notification_details.h"
22 #include "content/common/notification_source.h"
23 
24 namespace browser_sync {
25 
ExtensionChangeProcessor(const ExtensionSyncTraits & traits,UnrecoverableErrorHandler * error_handler)26 ExtensionChangeProcessor::ExtensionChangeProcessor(
27     const ExtensionSyncTraits& traits,
28     UnrecoverableErrorHandler* error_handler)
29     : ChangeProcessor(error_handler),
30       traits_(traits),
31       profile_(NULL),
32       extension_service_(NULL),
33       user_share_(NULL) {
34   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
35   DCHECK(error_handler);
36 }
37 
~ExtensionChangeProcessor()38 ExtensionChangeProcessor::~ExtensionChangeProcessor() {
39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40 }
41 
42 // TODO(akalin): We need to make sure events we receive from either
43 // the browser or the syncapi are done in order; this is tricky since
44 // some events (e.g., extension installation) are done asynchronously.
45 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)46 void ExtensionChangeProcessor::Observe(NotificationType type,
47                                        const NotificationSource& source,
48                                        const NotificationDetails& details) {
49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
50   DCHECK(running());
51   DCHECK(profile_);
52   if ((type != NotificationType::EXTENSION_INSTALLED) &&
53       (type != NotificationType::EXTENSION_UNINSTALLED) &&
54       (type != NotificationType::EXTENSION_LOADED) &&
55       (type != NotificationType::EXTENSION_UPDATE_DISABLED) &&
56       (type != NotificationType::EXTENSION_UNLOADED)) {
57     LOG(DFATAL) << "Received unexpected notification of type "
58                 << type.value;
59     return;
60   }
61 
62   DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
63   if (type == NotificationType::EXTENSION_UNINSTALLED) {
64     const UninstalledExtensionInfo* uninstalled_extension_info =
65         Details<UninstalledExtensionInfo>(details).ptr();
66     CHECK(uninstalled_extension_info);
67     if (traits_.should_handle_extension_uninstall(
68             *uninstalled_extension_info)) {
69       const std::string& id = uninstalled_extension_info->extension_id;
70       VLOG(1) << "Removing server data for uninstalled extension " << id
71               << " of type " << uninstalled_extension_info->extension_type;
72       RemoveServerData(traits_, id, user_share_);
73     }
74   } else {
75     const Extension* extension = NULL;
76     if (type == NotificationType::EXTENSION_UNLOADED) {
77       extension = Details<UnloadedExtensionInfo>(details)->extension;
78     } else {
79       extension = Details<const Extension>(details).ptr();
80     }
81     CHECK(extension);
82     VLOG(1) << "Updating server data for extension " << extension->id()
83             << " (notification type = " << type.value << ")";
84     if (!traits_.is_valid_and_syncable(*extension)) {
85       return;
86     }
87     std::string error;
88     if (!UpdateServerData(traits_, *extension, *extension_service_,
89                           user_share_, &error)) {
90       error_handler()->OnUnrecoverableError(FROM_HERE, error);
91     }
92   }
93 }
94 
ApplyChangesFromSyncModel(const sync_api::BaseTransaction * trans,const sync_api::SyncManager::ChangeRecord * changes,int change_count)95 void ExtensionChangeProcessor::ApplyChangesFromSyncModel(
96     const sync_api::BaseTransaction* trans,
97     const sync_api::SyncManager::ChangeRecord* changes,
98     int change_count) {
99   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100   if (!running()) {
101     return;
102   }
103   for (int i = 0; i < change_count; ++i) {
104     const sync_api::SyncManager::ChangeRecord& change = changes[i];
105     sync_pb::ExtensionSpecifics specifics;
106     switch (change.action) {
107       case sync_api::SyncManager::ChangeRecord::ACTION_ADD:
108       case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
109         sync_api::ReadNode node(trans);
110         if (!node.InitByIdLookup(change.id)) {
111           std::stringstream error;
112           error << "Extension node lookup failed for change " << change.id
113                 << " of action type " << change.action;
114           error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
115           return;
116         }
117         DCHECK_EQ(node.GetModelType(), traits_.model_type);
118         specifics = (*traits_.extension_specifics_getter)(node);
119         break;
120       }
121       case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: {
122         if (!(*traits_.extension_specifics_entity_getter)(
123                 change.specifics, &specifics)) {
124           std::stringstream error;
125           error << "Could not get extension specifics from deleted node "
126                 << change.id;
127           error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
128           LOG(DFATAL) << error.str();
129         }
130         break;
131       }
132     }
133     ExtensionSyncData sync_data;
134     if (!GetExtensionSyncData(specifics, &sync_data)) {
135       // TODO(akalin): Should probably recover or drop.
136       std::string error =
137           std::string("Invalid server specifics: ") +
138           ExtensionSpecificsToString(specifics);
139       error_handler()->OnUnrecoverableError(FROM_HERE, error);
140       return;
141     }
142     sync_data.uninstalled =
143         (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE);
144     StopObserving();
145     extension_service_->ProcessSyncData(sync_data,
146                                         traits_.is_valid_and_syncable);
147     StartObserving();
148   }
149 }
150 
StartImpl(Profile * profile)151 void ExtensionChangeProcessor::StartImpl(Profile* profile) {
152   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153   profile_ = profile;
154   extension_service_ = profile_->GetExtensionService();
155   user_share_ = profile_->GetProfileSyncService()->GetUserShare();
156   DCHECK(profile_);
157   DCHECK(extension_service_);
158   DCHECK(user_share_);
159   StartObserving();
160 }
161 
StopImpl()162 void ExtensionChangeProcessor::StopImpl() {
163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164   StopObserving();
165   profile_ = NULL;
166   extension_service_ = NULL;
167   user_share_ = NULL;
168 }
169 
StartObserving()170 void ExtensionChangeProcessor::StartObserving() {
171   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172   DCHECK(profile_);
173   notification_registrar_.Add(
174       this, NotificationType::EXTENSION_INSTALLED,
175       Source<Profile>(profile_));
176   notification_registrar_.Add(
177       this, NotificationType::EXTENSION_UNINSTALLED,
178       Source<Profile>(profile_));
179 
180   notification_registrar_.Add(
181       this, NotificationType::EXTENSION_LOADED,
182       Source<Profile>(profile_));
183   // Despite the name, this notification is exactly like
184   // EXTENSION_LOADED but with an initial state of DISABLED.
185   notification_registrar_.Add(
186       this, NotificationType::EXTENSION_UPDATE_DISABLED,
187       Source<Profile>(profile_));
188 
189   notification_registrar_.Add(
190       this, NotificationType::EXTENSION_UNLOADED,
191       Source<Profile>(profile_));
192 }
193 
StopObserving()194 void ExtensionChangeProcessor::StopObserving() {
195   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
196   DCHECK(profile_);
197   VLOG(1) << "Unobserving all notifications";
198   notification_registrar_.RemoveAll();
199 }
200 
201 }  // namespace browser_sync
202