1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/extension_change_processor.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sstream>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
11731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/stl_util-inl.h"
1221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/extensions/extension_sync_data.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/profiles/profile.h"
153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/sync/glue/extension_sync.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/extension_util.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/sync/profile_sync_service.h"
183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
20dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync {
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionChangeProcessor::ExtensionChangeProcessor(
273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const ExtensionSyncTraits& traits,
283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    UnrecoverableErrorHandler* error_handler)
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : ChangeProcessor(error_handler),
303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      traits_(traits),
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      profile_(NULL),
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      extension_service_(NULL),
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      user_share_(NULL) {
34731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(error_handler);
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionChangeProcessor::~ExtensionChangeProcessor() {
39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(akalin): We need to make sure events we receive from either
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the browser or the syncapi are done in order; this is tricky since
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// some events (e.g., extension installation) are done asynchronously.
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::Observe(NotificationType type,
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       const NotificationSource& source,
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       const NotificationDetails& details) {
49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(running());
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if ((type != NotificationType::EXTENSION_INSTALLED) &&
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      (type != NotificationType::EXTENSION_UNINSTALLED) &&
54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      (type != NotificationType::EXTENSION_LOADED) &&
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      (type != NotificationType::EXTENSION_UPDATE_DISABLED) &&
5621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      (type != NotificationType::EXTENSION_UNLOADED)) {
573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LOG(DFATAL) << "Received unexpected notification of type "
583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                << type.value;
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (type == NotificationType::EXTENSION_UNINSTALLED) {
64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const UninstalledExtensionInfo* uninstalled_extension_info =
65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        Details<UninstalledExtensionInfo>(details).ptr();
66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    CHECK(uninstalled_extension_info);
6721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (traits_.should_handle_extension_uninstall(
6821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen            *uninstalled_extension_info)) {
69731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      const std::string& id = uninstalled_extension_info->extension_id;
70731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      VLOG(1) << "Removing server data for uninstalled extension " << id
7121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen              << " of type " << uninstalled_extension_info->extension_type;
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      RemoveServerData(traits_, id, user_share_);
73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
7521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const Extension* extension = NULL;
7621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (type == NotificationType::EXTENSION_UNLOADED) {
7721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      extension = Details<UnloadedExtensionInfo>(details)->extension;
7821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    } else {
7921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      extension = Details<const Extension>(details).ptr();
8021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    }
81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    CHECK(extension);
82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    VLOG(1) << "Updating server data for extension " << extension->id()
83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick            << " (notification type = " << type.value << ")";
8421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (!traits_.is_valid_and_syncable(*extension)) {
85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return;
86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    std::string error;
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!UpdateServerData(traits_, *extension, *extension_service_,
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          user_share_, &error)) {
903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      error_handler()->OnUnrecoverableError(FROM_HERE, error);
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::ApplyChangesFromSyncModel(
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::BaseTransaction* trans,
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::SyncManager::ChangeRecord* changes,
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int change_count) {
99731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!running()) {
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < change_count; ++i) {
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::SyncManager::ChangeRecord& change = changes[i];
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    sync_pb::ExtensionSpecifics specifics;
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    switch (change.action) {
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      case sync_api::SyncManager::ChangeRecord::ACTION_ADD:
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        sync_api::ReadNode node(trans);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!node.InitByIdLookup(change.id)) {
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          std::stringstream error;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          error << "Extension node lookup failed for change " << change.id
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                << " of action type " << change.action;
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return;
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        DCHECK_EQ(node.GetModelType(), traits_.model_type);
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        specifics = (*traits_.extension_specifics_getter)(node);
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        break;
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: {
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (!(*traits_.extension_specifics_entity_getter)(
1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                change.specifics, &specifics)) {
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          std::stringstream error;
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          error << "Could not get extension specifics from deleted node "
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                << change.id;
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          LOG(DFATAL) << error.str();
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        break;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ExtensionSyncData sync_data;
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!GetExtensionSyncData(specifics, &sync_data)) {
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // TODO(akalin): Should probably recover or drop.
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      std::string error =
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          std::string("Invalid server specifics: ") +
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          ExtensionSpecificsToString(specifics);
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      error_handler()->OnUnrecoverableError(FROM_HERE, error);
140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    sync_data.uninstalled =
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE);
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    StopObserving();
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    extension_service_->ProcessSyncData(sync_data,
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                        traits_.is_valid_and_syncable);
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    StartObserving();
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StartImpl(Profile* profile) {
152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = profile;
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  extension_service_ = profile_->GetExtensionService();
155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  user_share_ = profile_->GetProfileSyncService()->GetUserShare();
156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(profile_);
157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(extension_service_);
158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(user_share_);
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartObserving();
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StopImpl() {
163731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopObserving();
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = NULL;
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  extension_service_ = NULL;
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  user_share_ = NULL;
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StartObserving() {
171731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
173731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  notification_registrar_.Add(
174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      this, NotificationType::EXTENSION_INSTALLED,
175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      Source<Profile>(profile_));
176731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  notification_registrar_.Add(
177731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      this, NotificationType::EXTENSION_UNINSTALLED,
178731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      Source<Profile>(profile_));
179731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      this, NotificationType::EXTENSION_LOADED,
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Source<Profile>(profile_));
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Despite the name, this notification is exactly like
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // EXTENSION_LOADED but with an initial state of DISABLED.
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      this, NotificationType::EXTENSION_UPDATE_DISABLED,
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Source<Profile>(profile_));
188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      this, NotificationType::EXTENSION_UNLOADED,
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Source<Profile>(profile_));
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionChangeProcessor::StopObserving() {
195731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
197731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  VLOG(1) << "Unobserving all notifications";
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.RemoveAll();
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace browser_sync
202