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