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/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 
     20 ThemeChangeProcessor::ThemeChangeProcessor(
     21     UnrecoverableErrorHandler* error_handler)
     22     : ChangeProcessor(error_handler),
     23       profile_(NULL) {
     24   DCHECK(error_handler);
     25 }
     26 
     27 ThemeChangeProcessor::~ThemeChangeProcessor() {}
     28 
     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 
     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 
    108 void ThemeChangeProcessor::StartImpl(Profile* profile) {
    109   DCHECK(profile);
    110   profile_ = profile;
    111   StartObserving();
    112 }
    113 
    114 void ThemeChangeProcessor::StopImpl() {
    115   StopObserving();
    116   profile_ = NULL;
    117 }
    118 
    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 
    128 void ThemeChangeProcessor::StopObserving() {
    129   DCHECK(profile_);
    130   VLOG(1) << "Unobserving all notifications";
    131   notification_registrar_.RemoveAll();
    132 }
    133 
    134 }  // namespace browser_sync
    135