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/theme_change_processor.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/engine/syncapi.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/glue/theme_util.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service_factory.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync {
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochThemeChangeProcessor::ThemeChangeProcessor(
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnrecoverableErrorHandler* error_handler)
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : ChangeProcessor(error_handler),
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_(NULL) {
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(error_handler);
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochThemeChangeProcessor::~ThemeChangeProcessor() {}
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::Observe(NotificationType type,
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const NotificationSource& source,
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const NotificationDetails& details) {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(running());
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteTransaction trans(share_handle());
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_api::WriteNode node(&trans);
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!node.InitByClientTagLookup(syncable::THEMES,
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  kCurrentThemeClientTag)) {
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string err = "Could not create node with client tag: ";
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE,
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          err + kCurrentThemeClientTag);
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::ThemeSpecifics old_theme_specifics = node.GetThemeSpecifics();
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure to base new_theme_specifics on old_theme_specifics so
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we preserve the state of use_system_theme_by_default.
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::ThemeSpecifics new_theme_specifics = old_theme_specifics;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetThemeSpecificsFromCurrentTheme(profile_, &new_theme_specifics);
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Do a write only if something actually changed so as to guard
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // against cycles.
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!AreThemeSpecificsEqual(old_theme_specifics, new_theme_specifics)) {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    node.SetThemeSpecifics(new_theme_specifics);
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return;
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::ApplyChangesFromSyncModel(
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::BaseTransaction* trans,
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const sync_api::SyncManager::ChangeRecord* changes,
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int change_count) {
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!running()) {
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(akalin): Normally, we should only have a single change and
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // it should be an update.  However, the syncapi may occasionally
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // generates multiple changes.  When we fix syncapi to not do that,
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we can remove the extra logic below.  See:
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // http://code.google.com/p/chromium/issues/detail?id=41696 .
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (change_count < 1) {
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string err("Unexpected change_count: ");
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    err += change_count;
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE, err);
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (change_count > 1) {
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << change_count << " theme changes detected; "
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 << "only applying the last one";
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const sync_api::SyncManager::ChangeRecord& change =
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      changes[change_count - 1];
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (change.action != sync_api::SyncManager::ChangeRecord::ACTION_UPDATE &&
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      change.action != sync_api::SyncManager::ChangeRecord::ACTION_DELETE) {
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string err = "strange theme change.action " + change.action;
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    error_handler()->OnUnrecoverableError(FROM_HERE, err);
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sync_pb::ThemeSpecifics theme_specifics;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the action is a delete, simply use the default values for
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // ThemeSpecifics, which would cause the default theme to be set.
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (change.action != sync_api::SyncManager::ChangeRecord::ACTION_DELETE) {
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sync_api::ReadNode node(trans);
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!node.InitByIdLookup(change.id)) {
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      error_handler()->OnUnrecoverableError(FROM_HERE,
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            "Theme node lookup failed.");
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK_EQ(node.GetModelType(), syncable::THEMES);
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(profile_);
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    theme_specifics = node.GetThemeSpecifics();
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopObserving();
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetCurrentThemeFromThemeSpecificsIfNecessary(theme_specifics, profile_);
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartObserving();
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::StartImpl(Profile* profile) {
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = profile;
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartObserving();
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::StopImpl() {
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopObserving();
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  profile_ = NULL;
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::StartObserving() {
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  VLOG(1) << "Observing BROWSER_THEME_CHANGED";
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.Add(
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      this, NotificationType::BROWSER_THEME_CHANGED,
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Source<ThemeService>(
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          ThemeServiceFactory::GetForProfile(profile_)));
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ThemeChangeProcessor::StopObserving() {
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(profile_);
130731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  VLOG(1) << "Unobserving all notifications";
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  notification_registrar_.RemoveAll();
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace browser_sync
135