Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/glue/extension_sync.h" | 5 #include "chrome/browser/sync/glue/extension_sync.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "chrome/browser/extensions/extension_updater.h" | |
| 11 #include "chrome/browser/extensions/extension_service.h" | 10 #include "chrome/browser/extensions/extension_service.h" |
| 12 #include "chrome/browser/extensions/extension_sync_data.h" | 11 #include "chrome/browser/extensions/extension_sync_data.h" |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/sync/engine/syncapi.h" | 12 #include "chrome/browser/sync/engine/syncapi.h" |
| 15 #include "chrome/browser/sync/glue/extension_data.h" | |
| 16 #include "chrome/browser/sync/glue/extension_sync_traits.h" | 13 #include "chrome/browser/sync/glue/extension_sync_traits.h" |
| 17 #include "chrome/browser/sync/glue/extension_util.h" | 14 #include "chrome/browser/sync/glue/extension_util.h" |
| 18 #include "chrome/browser/sync/profile_sync_service.h" | 15 #include "chrome/browser/sync/protocol/extension_specifics.pb.h" |
| 19 | 16 |
| 20 namespace browser_sync { | 17 namespace browser_sync { |
| 21 | 18 |
| 22 bool RootNodeHasChildren(const char* tag, | 19 bool RootNodeHasChildren(const char* tag, |
| 23 sync_api::UserShare* user_share, | 20 sync_api::UserShare* user_share, |
| 24 bool* has_children) { | 21 bool* has_children) { |
| 25 CHECK(has_children); | 22 CHECK(has_children); |
| 26 *has_children = false; | 23 *has_children = false; |
| 27 sync_api::ReadTransaction trans(user_share); | 24 sync_api::ReadTransaction trans(user_share); |
| 28 sync_api::ReadNode node(&trans); | 25 sync_api::ReadNode node(&trans); |
| 29 if (!node.InitByTagLookup(tag)) { | 26 if (!node.InitByTagLookup(tag)) { |
| 30 LOG(ERROR) << "Root node with tag " << tag << " does not exist"; | 27 LOG(ERROR) << "Root node with tag " << tag << " does not exist"; |
| 31 return false; | 28 return false; |
| 32 } | 29 } |
| 33 *has_children = node.GetFirstChildId() != sync_api::kInvalidId; | 30 *has_children = node.GetFirstChildId() != sync_api::kInvalidId; |
| 34 return true; | 31 return true; |
| 35 } | 32 } |
| 36 | 33 |
| 37 namespace { | 34 namespace { |
| 38 | 35 |
| 39 // Updates the value in |extension_data_map| from the given data, | |
| 40 // creating an entry if necessary. Returns a pointer to the | |
| 41 // updated/created ExtensionData object. | |
| 42 ExtensionData* SetOrCreateExtensionData( | |
| 43 ExtensionDataMap* extension_data_map, | |
| 44 ExtensionData::Source source, | |
| 45 bool merge_user_properties, | |
| 46 const sync_pb::ExtensionSpecifics& data) { | |
| 47 DcheckIsExtensionSpecificsValid(data); | |
| 48 const std::string& extension_id = data.id(); | |
| 49 std::pair<ExtensionDataMap::iterator, bool> result = | |
| 50 extension_data_map->insert( | |
| 51 std::make_pair(extension_id, | |
| 52 ExtensionData::FromData(source, data))); | |
| 53 ExtensionData* extension_data = &result.first->second; | |
| 54 if (result.second) { | |
| 55 // The value was just inserted, so it shouldn't need an update | |
| 56 // from source. | |
| 57 DCHECK(!extension_data->NeedsUpdate(source)); | |
| 58 } else { | |
| 59 extension_data->SetData(source, merge_user_properties, data); | |
| 60 } | |
| 61 return extension_data; | |
| 62 } | |
| 63 | |
| 64 // Fills in |extension_data_map| with data from | 36 // Fills in |extension_data_map| with data from |
| 65 // extension_service.GetSyncDataList(). | 37 // extension_service.GetSyncDataList(). |
| 66 void SlurpClientData( | 38 void SlurpClientData( |
| 67 IsValidAndSyncablePredicate is_valid_and_syncable, | 39 IsValidAndSyncablePredicate is_valid_and_syncable, |
| 68 const ExtensionServiceInterface& extension_service, | 40 const ExtensionServiceInterface& extension_service, |
| 69 ExtensionDataMap* extension_data_map) { | 41 ExtensionDataMap* extension_data_map) { |
| 70 std::vector<ExtensionSyncData> sync_data_list = | 42 std::vector<ExtensionSyncData> sync_data_list = |
| 71 extension_service.GetSyncDataList(is_valid_and_syncable); | 43 extension_service.GetSyncDataList(is_valid_and_syncable); |
| 72 for (std::vector<ExtensionSyncData>::const_iterator it = | 44 for (std::vector<ExtensionSyncData>::const_iterator it = |
| 73 sync_data_list.begin(); | 45 sync_data_list.begin(); |
| 74 it != sync_data_list.end(); ++it) { | 46 it != sync_data_list.end(); ++it) { |
| 75 sync_pb::ExtensionSpecifics client_specifics; | 47 std::pair<ExtensionDataMap::iterator, bool> result = |
| 76 SyncDataToSpecifics(*it, &client_specifics); | 48 extension_data_map->insert(std::make_pair(it->id, *it)); |
| 77 const ExtensionData& extension_data = | 49 if (!result.second) { |
| 78 *SetOrCreateExtensionData( | 50 // The value wasn't inserted, so merge it in. |
| 79 extension_data_map, ExtensionData::CLIENT, | 51 result.first->second.Merge(*it); |
|
asargent_no_longer_on_chrome
2011/04/27 16:45:19
It's not really clear to me why you need this Merg
akalin
2011/04/27 17:53:25
(The above is equivalent to "(*extension_data_map)
| |
| 80 true, client_specifics); | 52 } |
| 81 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); | |
| 82 } | 53 } |
| 83 } | 54 } |
| 84 | 55 |
| 85 // Gets the boilerplate error message for not being able to find a | 56 // Gets the boilerplate error message for not being able to find a |
| 86 // root node. | 57 // root node. |
| 87 // | 58 // |
| 88 // TODO(akalin): Put this somewhere where all data types can use it. | 59 // TODO(akalin): Put this somewhere where all data types can use it. |
| 89 std::string GetRootNodeDoesNotExistError(const char* root_node_tag) { | 60 std::string GetRootNodeDoesNotExistError(const char* root_node_tag) { |
| 90 return | 61 return |
| 91 std::string("Server did not create the top-level ") + | 62 std::string("Server did not create the top-level ") + |
| 92 root_node_tag + | 63 root_node_tag + |
| 93 " node. We might be running against an out-of-date server."; | 64 " node. We might be running against an out-of-date server."; |
| 94 } | 65 } |
| 95 | 66 |
| 96 // Gets the data from the server for extensions to be synced and | 67 // Gets the data from the server for extensions to be synced and |
| 97 // updates |extension_data_map|. Skips all extensions in | 68 // updates |extension_data_map|. |
| 98 // |unsynced_extensions|. | |
| 99 bool SlurpServerData( | 69 bool SlurpServerData( |
| 100 const char* root_node_tag, | 70 const char* root_node_tag, |
| 101 const ExtensionSpecificsGetter extension_specifics_getter, | 71 const ExtensionSpecificsGetter extension_specifics_getter, |
| 102 sync_api::UserShare* user_share, | 72 sync_api::UserShare* user_share, |
| 103 ExtensionDataMap* extension_data_map) { | 73 ExtensionDataMap* extension_data_map) { |
| 104 sync_api::WriteTransaction trans(user_share); | 74 sync_api::WriteTransaction trans(user_share); |
| 105 sync_api::ReadNode root(&trans); | 75 sync_api::ReadNode root(&trans); |
| 106 if (!root.InitByTagLookup(root_node_tag)) { | 76 if (!root.InitByTagLookup(root_node_tag)) { |
| 107 LOG(ERROR) << GetRootNodeDoesNotExistError(root_node_tag); | 77 LOG(ERROR) << GetRootNodeDoesNotExistError(root_node_tag); |
| 108 return false; | 78 return false; |
| 109 } | 79 } |
| 110 | 80 |
| 111 int64 id = root.GetFirstChildId(); | 81 int64 id = root.GetFirstChildId(); |
| 112 while (id != sync_api::kInvalidId) { | 82 while (id != sync_api::kInvalidId) { |
| 113 sync_api::ReadNode sync_node(&trans); | 83 sync_api::ReadNode sync_node(&trans); |
| 114 if (!sync_node.InitByIdLookup(id)) { | 84 if (!sync_node.InitByIdLookup(id)) { |
| 115 LOG(ERROR) << "Failed to fetch sync node for id " << id; | 85 LOG(ERROR) << "Failed to fetch sync node for id " << id; |
| 116 return false; | 86 return false; |
| 117 } | 87 } |
| 118 const sync_pb::ExtensionSpecifics& server_data = | 88 const sync_pb::ExtensionSpecifics& server_data = |
| 119 (*extension_specifics_getter)(sync_node); | 89 (*extension_specifics_getter)(sync_node); |
| 120 if (!IsExtensionSpecificsValid(server_data)) { | 90 ExtensionSyncData sync_data; |
| 91 if (!SpecificsToSyncData(server_data, &sync_data)) { | |
| 121 LOG(ERROR) << "Invalid extensions specifics for id " << id; | 92 LOG(ERROR) << "Invalid extensions specifics for id " << id; |
| 122 return false; | 93 return false; |
| 123 } | 94 } |
| 124 // Pass in false for merge_user_properties so client user | 95 (*extension_data_map)[sync_data.id] = sync_data; |
| 125 // settings always take precedence. | |
| 126 const ExtensionData& extension_data = | |
| 127 *SetOrCreateExtensionData( | |
| 128 extension_data_map, ExtensionData::SERVER, false, server_data); | |
| 129 DcheckIsExtensionSpecificsValid(extension_data.merged_data()); | |
| 130 id = sync_node.GetSuccessorId(); | 96 id = sync_node.GetSuccessorId(); |
| 131 } | 97 } |
| 132 return true; | 98 return true; |
| 133 } | 99 } |
| 134 | 100 |
| 135 } // namespace | 101 } // namespace |
| 136 | 102 |
| 137 bool SlurpExtensionData(const ExtensionSyncTraits& traits, | 103 bool SlurpExtensionData(const ExtensionSyncTraits& traits, |
| 138 const ExtensionServiceInterface& extension_service, | 104 const ExtensionServiceInterface& extension_service, |
| 139 sync_api::UserShare* user_share, | 105 sync_api::UserShare* user_share, |
| 140 ExtensionDataMap* extension_data_map) { | 106 ExtensionDataMap* extension_data_map) { |
| 141 // Read client-side data first so server data takes precedence. | 107 // Read server-side data first so client user settings take |
| 142 SlurpClientData( | 108 // precedence. |
| 143 traits.is_valid_and_syncable, extension_service, | |
| 144 extension_data_map); | |
| 145 | |
| 146 if (!SlurpServerData( | 109 if (!SlurpServerData( |
| 147 traits.root_node_tag, traits.extension_specifics_getter, | 110 traits.root_node_tag, traits.extension_specifics_getter, |
| 148 user_share, extension_data_map)) { | 111 user_share, extension_data_map)) { |
| 149 return false; | 112 return false; |
| 150 } | 113 } |
| 114 | |
| 115 SlurpClientData( | |
| 116 traits.is_valid_and_syncable, extension_service, | |
| 117 extension_data_map); | |
| 151 return true; | 118 return true; |
| 152 } | 119 } |
| 153 | 120 |
| 154 namespace { | 121 namespace { |
| 155 | 122 |
| 156 // Updates the server data from the given extension data. | 123 // Updates the server data from the given extension data. Returns |
| 157 // extension_data->ServerNeedsUpdate() must hold before this function | 124 // whether or not the update was successful. |
| 158 // is called. Returns whether or not the update was successful. If | |
| 159 // the update was successful, extension_data->ServerNeedsUpdate() will | |
| 160 // be false after this function is called. This function leaves | |
| 161 // extension_data->ClientNeedsUpdate() unchanged. | |
| 162 bool UpdateServer( | 125 bool UpdateServer( |
| 163 const ExtensionSyncTraits& traits, | 126 const ExtensionSyncTraits& traits, |
| 164 ExtensionData* extension_data, | 127 const ExtensionSyncData& data, |
| 165 sync_api::WriteTransaction* trans) { | 128 sync_api::WriteTransaction* trans) { |
| 166 DCHECK(extension_data->NeedsUpdate(ExtensionData::SERVER)); | 129 sync_pb::ExtensionSpecifics specifics; |
| 167 const sync_pb::ExtensionSpecifics& specifics = | 130 SyncDataToSpecifics(data, &specifics); |
| 168 extension_data->merged_data(); | 131 const std::string& id = data.id; |
| 169 const std::string& id = specifics.id(); | |
| 170 sync_api::WriteNode write_node(trans); | 132 sync_api::WriteNode write_node(trans); |
| 171 if (write_node.InitByClientTagLookup(traits.model_type, id)) { | 133 if (write_node.InitByClientTagLookup(traits.model_type, id)) { |
| 172 (*traits.extension_specifics_setter)(specifics, &write_node); | 134 (*traits.extension_specifics_setter)(specifics, &write_node); |
| 173 } else { | 135 } else { |
| 174 sync_api::ReadNode root(trans); | 136 sync_api::ReadNode root(trans); |
| 175 if (!root.InitByTagLookup(traits.root_node_tag)) { | 137 if (!root.InitByTagLookup(traits.root_node_tag)) { |
| 176 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); | 138 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); |
| 177 return false; | 139 return false; |
| 178 } | 140 } |
| 179 sync_api::WriteNode create_node(trans); | 141 sync_api::WriteNode create_node(trans); |
| 180 if (!create_node.InitUniqueByCreation(traits.model_type, root, id)) { | 142 if (!create_node.InitUniqueByCreation(traits.model_type, root, id)) { |
| 181 LOG(ERROR) << "Could not create node for extension " << id; | 143 LOG(ERROR) << "Could not create node for extension " << id; |
| 182 return false; | 144 return false; |
| 183 } | 145 } |
| 184 (*traits.extension_specifics_setter)(specifics, &create_node); | 146 (*traits.extension_specifics_setter)(specifics, &create_node); |
| 185 } | 147 } |
| 186 bool old_client_needs_update = | |
| 187 extension_data->NeedsUpdate(ExtensionData::CLIENT); | |
| 188 extension_data->ResolveData(ExtensionData::SERVER); | |
| 189 DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER)); | |
| 190 DCHECK_EQ(extension_data->NeedsUpdate(ExtensionData::CLIENT), | |
| 191 old_client_needs_update); | |
| 192 return true; | 148 return true; |
| 193 } | 149 } |
| 194 | 150 |
| 195 } // namespace | 151 } // namespace |
| 196 | 152 |
| 197 bool FlushExtensionData(const ExtensionSyncTraits& traits, | 153 bool FlushExtensionData(const ExtensionSyncTraits& traits, |
| 198 const ExtensionDataMap& extension_data_map, | 154 const ExtensionDataMap& extension_data_map, |
| 199 ExtensionServiceInterface* extension_service, | 155 ExtensionServiceInterface* extension_service, |
| 200 sync_api::UserShare* user_share) { | 156 sync_api::UserShare* user_share) { |
| 201 sync_api::WriteTransaction trans(user_share); | 157 sync_api::WriteTransaction trans(user_share); |
| 202 sync_api::ReadNode root(&trans); | 158 sync_api::ReadNode root(&trans); |
| 203 if (!root.InitByTagLookup(traits.root_node_tag)) { | 159 if (!root.InitByTagLookup(traits.root_node_tag)) { |
| 204 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); | 160 LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag); |
| 205 return false; | 161 return false; |
| 206 } | 162 } |
| 207 | 163 |
| 208 // Update server and client as necessary. | 164 // Update server and client as necessary. |
| 209 for (ExtensionDataMap::const_iterator it = extension_data_map.begin(); | 165 for (ExtensionDataMap::const_iterator it = extension_data_map.begin(); |
| 210 it != extension_data_map.end(); ++it) { | 166 it != extension_data_map.end(); ++it) { |
| 211 ExtensionData extension_data = it->second; | 167 const ExtensionSyncData& extension_data = it->second; |
| 212 // Update server first. | 168 if (!UpdateServer(traits, extension_data, &trans)) { |
| 213 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { | 169 LOG(ERROR) << "Could not update server data for extension " |
| 214 if (!UpdateServer(traits, &extension_data, &trans)) { | 170 << it->first; |
| 215 LOG(ERROR) << "Could not update server data for extension " | |
| 216 << it->first; | |
| 217 return false; | |
| 218 } | |
| 219 } | |
| 220 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); | |
| 221 ExtensionSyncData sync_data; | |
| 222 if (!SpecificsToSyncData(extension_data.merged_data(), &sync_data)) { | |
| 223 // TODO(akalin): Should probably recover or drop. | |
| 224 NOTREACHED(); | |
| 225 return false; | 171 return false; |
| 226 } | 172 } |
| 227 extension_service->ProcessSyncData(sync_data, | 173 extension_service->ProcessSyncData(extension_data, |
| 228 traits.is_valid_and_syncable); | 174 traits.is_valid_and_syncable); |
| 229 } | 175 } |
| 230 return true; | 176 return true; |
| 231 } | 177 } |
| 232 | 178 |
| 233 bool UpdateServerData(const ExtensionSyncTraits& traits, | 179 bool UpdateServerData(const ExtensionSyncTraits& traits, |
| 234 const std::string& id, | 180 const std::string& id, |
| 235 const ExtensionServiceInterface& extension_service, | 181 const ExtensionServiceInterface& extension_service, |
| 236 sync_api::UserShare* user_share, | 182 sync_api::UserShare* user_share, |
| 237 std::string* error) { | 183 std::string* error) { |
| 238 ExtensionSyncData data; | 184 ExtensionSyncData data; |
| 239 if (!extension_service.GetSyncData( | 185 if (!extension_service.GetSyncData( |
| 240 id, traits.is_valid_and_syncable, &data)) { | 186 id, traits.is_valid_and_syncable, &data)) { |
| 241 *error = | 187 *error = |
| 242 std::string("UpdateServerData() called for invalid or " | 188 std::string("UpdateServerData() called for invalid or " |
| 243 "unsyncable extension ") + id; | 189 "unsyncable extension ") + id; |
| 244 LOG(DFATAL) << *error; | 190 LOG(DFATAL) << *error; |
| 245 return false; | 191 return false; |
| 246 } | 192 } |
| 247 sync_pb::ExtensionSpecifics client_data; | |
| 248 SyncDataToSpecifics(data, &client_data); | |
| 249 DcheckIsExtensionSpecificsValid(client_data); | |
| 250 ExtensionData extension_data = | |
| 251 ExtensionData::FromData(ExtensionData::CLIENT, client_data); | |
| 252 | 193 |
| 253 sync_api::WriteTransaction trans(user_share); | 194 sync_api::WriteTransaction trans(user_share); |
| 254 | 195 if (!UpdateServer(traits, data, &trans)) { |
| 255 sync_api::ReadNode node(&trans); | 196 *error = |
| 256 if (node.InitByClientTagLookup(traits.model_type, id)) { | 197 std::string("Could not update server data for extension ") + id; |
| 257 sync_pb::ExtensionSpecifics server_data = | 198 LOG(ERROR) << *error; |
| 258 (*traits.extension_specifics_getter)(node); | 199 return false; |
| 259 if (IsExtensionSpecificsValid(server_data)) { | |
| 260 // If server node exists and is valid, update |extension_data| | |
| 261 // from it (but with it taking precedence). | |
| 262 extension_data = | |
| 263 ExtensionData::FromData(ExtensionData::SERVER, server_data); | |
| 264 extension_data.SetData(ExtensionData::CLIENT, true, client_data); | |
| 265 } else { | |
| 266 LOG(ERROR) << "Invalid extensions specifics for id " << id | |
| 267 << "; treating as empty"; | |
| 268 } | |
| 269 } | 200 } |
| 270 | |
| 271 if (extension_data.NeedsUpdate(ExtensionData::SERVER)) { | |
| 272 if (!UpdateServer(traits, &extension_data, &trans)) { | |
| 273 *error = | |
| 274 std::string("Could not update server data for extension ") + id; | |
| 275 LOG(ERROR) << *error; | |
| 276 return false; | |
| 277 } | |
| 278 } | |
| 279 DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER)); | |
| 280 // Client may still need updating, e.g. if we disable an extension | |
| 281 // while it's being auto-updated. If so, then we'll be called | |
| 282 // again once the auto-update is finished. | |
| 283 // | |
| 284 // TODO(akalin): Figure out a way to tell when the above happens, | |
| 285 // so we know exactly what NeedsUpdate(CLIENT) should return. | |
| 286 return true; | 201 return true; |
| 287 } | 202 } |
| 288 | 203 |
| 289 void RemoveServerData(const ExtensionSyncTraits& traits, | 204 void RemoveServerData(const ExtensionSyncTraits& traits, |
| 290 const std::string& id, | 205 const std::string& id, |
| 291 sync_api::UserShare* user_share) { | 206 sync_api::UserShare* user_share) { |
| 292 sync_api::WriteTransaction trans(user_share); | 207 sync_api::WriteTransaction trans(user_share); |
| 293 sync_api::WriteNode write_node(&trans); | 208 sync_api::WriteNode write_node(&trans); |
| 294 if (write_node.InitByClientTagLookup(traits.model_type, id)) { | 209 if (write_node.InitByClientTagLookup(traits.model_type, id)) { |
| 295 write_node.Remove(); | 210 write_node.Remove(); |
| 296 } else { | 211 } else { |
| 297 LOG(ERROR) << "Server data does not exist for extension " << id; | 212 LOG(ERROR) << "Server data does not exist for extension " << id; |
| 298 } | 213 } |
| 299 } | 214 } |
| 300 | 215 |
| 301 } // namespace browser_sync | 216 } // namespace browser_sync |
| OLD | NEW |