Home | History | Annotate | Download | only in glue
      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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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