普通文本  |  202行  |  7.32 KB

// Copyright (c) 2011 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_change_processor.h"

#include <sstream>
#include <string>

#include "base/logging.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/extension_sync.h"
#include "chrome/browser/sync/glue/extension_util.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/common/extensions/extension.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"

namespace browser_sync {

ExtensionChangeProcessor::ExtensionChangeProcessor(
    const ExtensionSyncTraits& traits,
    UnrecoverableErrorHandler* error_handler)
    : ChangeProcessor(error_handler),
      traits_(traits),
      profile_(NULL),
      extension_service_(NULL),
      user_share_(NULL) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(error_handler);
}

ExtensionChangeProcessor::~ExtensionChangeProcessor() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

// TODO(akalin): We need to make sure events we receive from either
// the browser or the syncapi are done in order; this is tricky since
// some events (e.g., extension installation) are done asynchronously.

void ExtensionChangeProcessor::Observe(NotificationType type,
                                       const NotificationSource& source,
                                       const NotificationDetails& details) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(running());
  DCHECK(profile_);
  if ((type != NotificationType::EXTENSION_INSTALLED) &&
      (type != NotificationType::EXTENSION_UNINSTALLED) &&
      (type != NotificationType::EXTENSION_LOADED) &&
      (type != NotificationType::EXTENSION_UPDATE_DISABLED) &&
      (type != NotificationType::EXTENSION_UNLOADED)) {
    LOG(DFATAL) << "Received unexpected notification of type "
                << type.value;
    return;
  }

  DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
  if (type == NotificationType::EXTENSION_UNINSTALLED) {
    const UninstalledExtensionInfo* uninstalled_extension_info =
        Details<UninstalledExtensionInfo>(details).ptr();
    CHECK(uninstalled_extension_info);
    if (traits_.should_handle_extension_uninstall(
            *uninstalled_extension_info)) {
      const std::string& id = uninstalled_extension_info->extension_id;
      VLOG(1) << "Removing server data for uninstalled extension " << id
              << " of type " << uninstalled_extension_info->extension_type;
      RemoveServerData(traits_, id, user_share_);
    }
  } else {
    const Extension* extension = NULL;
    if (type == NotificationType::EXTENSION_UNLOADED) {
      extension = Details<UnloadedExtensionInfo>(details)->extension;
    } else {
      extension = Details<const Extension>(details).ptr();
    }
    CHECK(extension);
    VLOG(1) << "Updating server data for extension " << extension->id()
            << " (notification type = " << type.value << ")";
    if (!traits_.is_valid_and_syncable(*extension)) {
      return;
    }
    std::string error;
    if (!UpdateServerData(traits_, *extension, *extension_service_,
                          user_share_, &error)) {
      error_handler()->OnUnrecoverableError(FROM_HERE, error);
    }
  }
}

void ExtensionChangeProcessor::ApplyChangesFromSyncModel(
    const sync_api::BaseTransaction* trans,
    const sync_api::SyncManager::ChangeRecord* changes,
    int change_count) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!running()) {
    return;
  }
  for (int i = 0; i < change_count; ++i) {
    const sync_api::SyncManager::ChangeRecord& change = changes[i];
    sync_pb::ExtensionSpecifics specifics;
    switch (change.action) {
      case sync_api::SyncManager::ChangeRecord::ACTION_ADD:
      case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
        sync_api::ReadNode node(trans);
        if (!node.InitByIdLookup(change.id)) {
          std::stringstream error;
          error << "Extension node lookup failed for change " << change.id
                << " of action type " << change.action;
          error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
          return;
        }
        DCHECK_EQ(node.GetModelType(), traits_.model_type);
        specifics = (*traits_.extension_specifics_getter)(node);
        break;
      }
      case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: {
        if (!(*traits_.extension_specifics_entity_getter)(
                change.specifics, &specifics)) {
          std::stringstream error;
          error << "Could not get extension specifics from deleted node "
                << change.id;
          error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
          LOG(DFATAL) << error.str();
        }
        break;
      }
    }
    ExtensionSyncData sync_data;
    if (!GetExtensionSyncData(specifics, &sync_data)) {
      // TODO(akalin): Should probably recover or drop.
      std::string error =
          std::string("Invalid server specifics: ") +
          ExtensionSpecificsToString(specifics);
      error_handler()->OnUnrecoverableError(FROM_HERE, error);
      return;
    }
    sync_data.uninstalled =
        (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE);
    StopObserving();
    extension_service_->ProcessSyncData(sync_data,
                                        traits_.is_valid_and_syncable);
    StartObserving();
  }
}

void ExtensionChangeProcessor::StartImpl(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  profile_ = profile;
  extension_service_ = profile_->GetExtensionService();
  user_share_ = profile_->GetProfileSyncService()->GetUserShare();
  DCHECK(profile_);
  DCHECK(extension_service_);
  DCHECK(user_share_);
  StartObserving();
}

void ExtensionChangeProcessor::StopImpl() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  StopObserving();
  profile_ = NULL;
  extension_service_ = NULL;
  user_share_ = NULL;
}

void ExtensionChangeProcessor::StartObserving() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(profile_);
  notification_registrar_.Add(
      this, NotificationType::EXTENSION_INSTALLED,
      Source<Profile>(profile_));
  notification_registrar_.Add(
      this, NotificationType::EXTENSION_UNINSTALLED,
      Source<Profile>(profile_));

  notification_registrar_.Add(
      this, NotificationType::EXTENSION_LOADED,
      Source<Profile>(profile_));
  // Despite the name, this notification is exactly like
  // EXTENSION_LOADED but with an initial state of DISABLED.
  notification_registrar_.Add(
      this, NotificationType::EXTENSION_UPDATE_DISABLED,
      Source<Profile>(profile_));

  notification_registrar_.Add(
      this, NotificationType::EXTENSION_UNLOADED,
      Source<Profile>(profile_));
}

void ExtensionChangeProcessor::StopObserving() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(profile_);
  VLOG(1) << "Unobserving all notifications";
  notification_registrar_.RemoveAll();
}

}  // namespace browser_sync