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/extension_util.h"
      6 
      7 #include <sstream>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/stl_util-inl.h"
     12 #include "base/version.h"
     13 #include "chrome/browser/extensions/extension_prefs.h"
     14 #include "chrome/browser/extensions/extension_service.h"
     15 #include "chrome/browser/extensions/extension_sync_data.h"
     16 #include "chrome/browser/sync/protocol/extension_specifics.pb.h"
     17 #include "chrome/common/extensions/extension.h"
     18 #include "chrome/common/extensions/extension_constants.h"
     19 
     20 namespace browser_sync {
     21 
     22 bool IsExtensionValid(const Extension& extension) {
     23   // TODO(akalin): Figure out if we need to allow some other types.
     24   if (extension.location() != Extension::INTERNAL) {
     25     // We have a non-standard location.
     26     return false;
     27   }
     28 
     29   // Disallow extensions with non-gallery auto-update URLs for now.
     30   //
     31   // TODO(akalin): Relax this restriction once we've put in UI to
     32   // approve synced extensions.
     33   if (!extension.update_url().is_empty() &&
     34       (extension.update_url() != Extension::GalleryUpdateUrl(false)) &&
     35       (extension.update_url() != Extension::GalleryUpdateUrl(true))) {
     36     return false;
     37   }
     38 
     39   // Disallow extensions with native code plugins.
     40   //
     41   // TODO(akalin): Relax this restriction once we've put in UI to
     42   // approve synced extensions.
     43   if (!extension.plugins().empty()) {
     44     return false;
     45   }
     46 
     47   return true;
     48 }
     49 
     50 std::string ExtensionSpecificsToString(
     51     const sync_pb::ExtensionSpecifics& specifics) {
     52   std::stringstream ss;
     53   ss << "{ ";
     54   ss << "id: "                   << specifics.id()                << ", ";
     55   ss << "version: "              << specifics.version()           << ", ";
     56   ss << "update_url: "           << specifics.update_url()        << ", ";
     57   ss << "enabled: "              << specifics.enabled()           << ", ";
     58   ss << "incognito_enabled: "    << specifics.incognito_enabled() << ", ";
     59   ss << "name: "                 << specifics.name();
     60   ss << " }";
     61   return ss.str();
     62 }
     63 
     64 bool IsExtensionSpecificsValid(
     65     const sync_pb::ExtensionSpecifics& specifics) {
     66   if (!Extension::IdIsValid(specifics.id())) {
     67     return false;
     68   }
     69 
     70   scoped_ptr<Version> version(
     71       Version::GetVersionFromString(specifics.version()));
     72   if (!version.get()) {
     73     return false;
     74   }
     75 
     76   // The update URL must be either empty or valid.
     77   GURL update_url(specifics.update_url());
     78   if (!update_url.is_empty() && !update_url.is_valid()) {
     79     return false;
     80   }
     81 
     82   return true;
     83 }
     84 
     85 void DcheckIsExtensionSpecificsValid(
     86     const sync_pb::ExtensionSpecifics& specifics) {
     87   DCHECK(IsExtensionSpecificsValid(specifics))
     88       << ExtensionSpecificsToString(specifics);
     89 }
     90 
     91 bool AreExtensionSpecificsEqual(const sync_pb::ExtensionSpecifics& a,
     92                                 const sync_pb::ExtensionSpecifics& b) {
     93   // TODO(akalin): Figure out if we have to worry about version/URL
     94   // strings that are not identical but map to the same object.
     95   return ((a.id() == b.id()) &&
     96           (a.version() == b.version()) &&
     97           (a.update_url() == b.update_url()) &&
     98           (a.enabled() == b.enabled()) &&
     99           (a.incognito_enabled() == b.incognito_enabled()) &&
    100           (a.name() == b.name()));
    101 }
    102 
    103 bool IsExtensionSpecificsUnset(
    104     const sync_pb::ExtensionSpecifics& specifics) {
    105   return AreExtensionSpecificsEqual(specifics,
    106                                     sync_pb::ExtensionSpecifics());
    107 }
    108 
    109 void CopyUserProperties(
    110     const sync_pb::ExtensionSpecifics& specifics,
    111     sync_pb::ExtensionSpecifics* dest_specifics) {
    112   DCHECK(dest_specifics);
    113   dest_specifics->set_enabled(specifics.enabled());
    114   dest_specifics->set_incognito_enabled(specifics.incognito_enabled());
    115 }
    116 
    117 void CopyNonUserProperties(
    118     const sync_pb::ExtensionSpecifics& specifics,
    119     sync_pb::ExtensionSpecifics* dest_specifics) {
    120   DCHECK(dest_specifics);
    121   sync_pb::ExtensionSpecifics old_dest_specifics(*dest_specifics);
    122   *dest_specifics = specifics;
    123   CopyUserProperties(old_dest_specifics, dest_specifics);
    124 }
    125 
    126 bool AreExtensionSpecificsUserPropertiesEqual(
    127     const sync_pb::ExtensionSpecifics& a,
    128     const sync_pb::ExtensionSpecifics& b) {
    129   sync_pb::ExtensionSpecifics a_user_properties, b_user_properties;
    130   CopyUserProperties(a, &a_user_properties);
    131   CopyUserProperties(b, &b_user_properties);
    132   return AreExtensionSpecificsEqual(a_user_properties, b_user_properties);
    133 }
    134 
    135 bool AreExtensionSpecificsNonUserPropertiesEqual(
    136     const sync_pb::ExtensionSpecifics& a,
    137     const sync_pb::ExtensionSpecifics& b) {
    138   sync_pb::ExtensionSpecifics a_non_user_properties, b_non_user_properties;
    139   CopyNonUserProperties(a, &a_non_user_properties);
    140   CopyNonUserProperties(b, &b_non_user_properties);
    141   return AreExtensionSpecificsEqual(
    142       a_non_user_properties, b_non_user_properties);
    143 }
    144 
    145 void GetExtensionSpecifics(const Extension& extension,
    146                            const ExtensionServiceInterface& extension_service,
    147                            sync_pb::ExtensionSpecifics* specifics) {
    148   DCHECK(IsExtensionValid(extension));
    149   const std::string& id = extension.id();
    150   bool enabled = extension_service.IsExtensionEnabled(id);
    151   bool incognito_enabled = extension_service.IsIncognitoEnabled(id);
    152   specifics->set_id(id);
    153   specifics->set_version(extension.VersionString());
    154   specifics->set_update_url(extension.update_url().spec());
    155   specifics->set_enabled(enabled);
    156   specifics->set_incognito_enabled(incognito_enabled);
    157   specifics->set_name(extension.name());
    158   DcheckIsExtensionSpecificsValid(*specifics);
    159 }
    160 
    161 void MergeExtensionSpecifics(
    162     const sync_pb::ExtensionSpecifics& specifics,
    163     bool merge_user_properties,
    164     sync_pb::ExtensionSpecifics* merged_specifics) {
    165   DcheckIsExtensionSpecificsValid(*merged_specifics);
    166   DcheckIsExtensionSpecificsValid(specifics);
    167   DCHECK_EQ(specifics.id(), merged_specifics->id());
    168   // TODO(akalin): Merge enabled permissions when we sync those.
    169   scoped_ptr<Version> version(
    170       Version::GetVersionFromString(specifics.version()));
    171   CHECK(version.get());
    172   scoped_ptr<Version> merged_version(
    173       Version::GetVersionFromString(merged_specifics->version()));
    174   CHECK(merged_version.get());
    175   if (version->CompareTo(*merged_version) >= 0) {
    176     // |specifics| has a more recent or the same version, so merge it
    177     // in.
    178     CopyNonUserProperties(specifics, merged_specifics);
    179     if (merge_user_properties) {
    180       CopyUserProperties(specifics, merged_specifics);
    181     }
    182   }
    183 }
    184 
    185 bool GetExtensionSyncData(
    186     const sync_pb::ExtensionSpecifics& specifics,
    187     ExtensionSyncData* sync_data) {
    188   if (!Extension::IdIsValid(specifics.id())) {
    189     return false;
    190   }
    191 
    192   scoped_ptr<Version> version(
    193       Version::GetVersionFromString(specifics.version()));
    194   if (!version.get()) {
    195     return false;
    196   }
    197 
    198   // The update URL must be either empty or valid.
    199   GURL update_url(specifics.update_url());
    200   if (!update_url.is_empty() && !update_url.is_valid()) {
    201     return false;
    202   }
    203 
    204   sync_data->id = specifics.id();
    205   sync_data->update_url = update_url;
    206   sync_data->version = *version;
    207   sync_data->enabled = specifics.enabled();
    208   sync_data->incognito_enabled = specifics.incognito_enabled();
    209   return true;
    210 }
    211 
    212 }  // namespace browser_sync
    213