| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "components/sessions/serialized_navigation_entry.h" | |
| 6 | |
| 7 #include "base/pickle.h" | |
| 8 #include "base/strings/utf_string_conversions.h" | |
| 9 #include "components/sessions/core/serialized_navigation_driver.h" | |
| 10 #include "sync/protocol/session_specifics.pb.h" | |
| 11 #include "sync/util/time.h" | |
| 12 | |
| 13 namespace sessions { | |
| 14 | |
| 15 const char kSearchTermsKey[] = "search_terms"; | |
| 16 | |
| 17 SerializedNavigationEntry::SerializedNavigationEntry() | |
| 18 : index_(-1), | |
| 19 unique_id_(0), | |
| 20 transition_type_(ui::PAGE_TRANSITION_TYPED), | |
| 21 has_post_data_(false), | |
| 22 post_id_(-1), | |
| 23 is_overriding_user_agent_(false), | |
| 24 http_status_code_(0), | |
| 25 is_restored_(false), | |
| 26 blocked_state_(STATE_INVALID) { | |
| 27 referrer_policy_ = | |
| 28 SerializedNavigationDriver::Get()->GetDefaultReferrerPolicy(); | |
| 29 } | |
| 30 | |
| 31 SerializedNavigationEntry::~SerializedNavigationEntry() {} | |
| 32 | |
| 33 SerializedNavigationEntry SerializedNavigationEntry::FromSyncData( | |
| 34 int index, | |
| 35 const sync_pb::TabNavigation& sync_data) { | |
| 36 SerializedNavigationEntry navigation; | |
| 37 navigation.index_ = index; | |
| 38 navigation.unique_id_ = sync_data.unique_id(); | |
| 39 navigation.encoded_page_state_ = sync_data.state(); | |
| 40 if (sync_data.has_correct_referrer_policy()) { | |
| 41 navigation.referrer_url_ = GURL(sync_data.referrer()); | |
| 42 navigation.referrer_policy_ = sync_data.correct_referrer_policy(); | |
| 43 } else { | |
| 44 int mapped_referrer_policy; | |
| 45 if (SerializedNavigationDriver::Get()->MapReferrerPolicyToNewValues( | |
| 46 sync_data.obsolete_referrer_policy(), &mapped_referrer_policy)) { | |
| 47 navigation.referrer_url_ = GURL(sync_data.referrer()); | |
| 48 } else { | |
| 49 navigation.referrer_url_ = GURL(); | |
| 50 } | |
| 51 navigation.referrer_policy_ = mapped_referrer_policy; | |
| 52 navigation.encoded_page_state_ = | |
| 53 SerializedNavigationDriver::Get()->StripReferrerFromPageState( | |
| 54 navigation.encoded_page_state_); | |
| 55 } | |
| 56 navigation.virtual_url_ = GURL(sync_data.virtual_url()); | |
| 57 navigation.title_ = base::UTF8ToUTF16(sync_data.title()); | |
| 58 | |
| 59 uint32 transition = 0; | |
| 60 if (sync_data.has_page_transition()) { | |
| 61 switch (sync_data.page_transition()) { | |
| 62 case sync_pb::SyncEnums_PageTransition_LINK: | |
| 63 transition = ui::PAGE_TRANSITION_LINK; | |
| 64 break; | |
| 65 case sync_pb::SyncEnums_PageTransition_TYPED: | |
| 66 transition = ui::PAGE_TRANSITION_TYPED; | |
| 67 break; | |
| 68 case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK: | |
| 69 transition = ui::PAGE_TRANSITION_AUTO_BOOKMARK; | |
| 70 break; | |
| 71 case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME: | |
| 72 transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME; | |
| 73 break; | |
| 74 case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME: | |
| 75 transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME; | |
| 76 break; | |
| 77 case sync_pb::SyncEnums_PageTransition_GENERATED: | |
| 78 transition = ui::PAGE_TRANSITION_GENERATED; | |
| 79 break; | |
| 80 case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL: | |
| 81 transition = ui::PAGE_TRANSITION_AUTO_TOPLEVEL; | |
| 82 break; | |
| 83 case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT: | |
| 84 transition = ui::PAGE_TRANSITION_FORM_SUBMIT; | |
| 85 break; | |
| 86 case sync_pb::SyncEnums_PageTransition_RELOAD: | |
| 87 transition = ui::PAGE_TRANSITION_RELOAD; | |
| 88 break; | |
| 89 case sync_pb::SyncEnums_PageTransition_KEYWORD: | |
| 90 transition = ui::PAGE_TRANSITION_KEYWORD; | |
| 91 break; | |
| 92 case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED: | |
| 93 transition = ui::PAGE_TRANSITION_KEYWORD_GENERATED; | |
| 94 break; | |
| 95 default: | |
| 96 transition = ui::PAGE_TRANSITION_LINK; | |
| 97 break; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 if (sync_data.has_redirect_type()) { | |
| 102 switch (sync_data.redirect_type()) { | |
| 103 case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT: | |
| 104 transition |= ui::PAGE_TRANSITION_CLIENT_REDIRECT; | |
| 105 break; | |
| 106 case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT: | |
| 107 transition |= ui::PAGE_TRANSITION_SERVER_REDIRECT; | |
| 108 break; | |
| 109 } | |
| 110 } | |
| 111 if (sync_data.navigation_forward_back()) | |
| 112 transition |= ui::PAGE_TRANSITION_FORWARD_BACK; | |
| 113 if (sync_data.navigation_from_address_bar()) | |
| 114 transition |= ui::PAGE_TRANSITION_FROM_ADDRESS_BAR; | |
| 115 if (sync_data.navigation_home_page()) | |
| 116 transition |= ui::PAGE_TRANSITION_HOME_PAGE; | |
| 117 if (sync_data.navigation_chain_start()) | |
| 118 transition |= ui::PAGE_TRANSITION_CHAIN_START; | |
| 119 if (sync_data.navigation_chain_end()) | |
| 120 transition |= ui::PAGE_TRANSITION_CHAIN_END; | |
| 121 | |
| 122 navigation.transition_type_ = static_cast<ui::PageTransition>(transition); | |
| 123 | |
| 124 navigation.timestamp_ = base::Time(); | |
| 125 navigation.search_terms_ = base::UTF8ToUTF16(sync_data.search_terms()); | |
| 126 if (sync_data.has_favicon_url()) | |
| 127 navigation.favicon_url_ = GURL(sync_data.favicon_url()); | |
| 128 | |
| 129 navigation.http_status_code_ = sync_data.http_status_code(); | |
| 130 | |
| 131 SerializedNavigationDriver::Get()->Sanitize(&navigation); | |
| 132 | |
| 133 navigation.is_restored_ = true; | |
| 134 | |
| 135 return navigation; | |
| 136 } | |
| 137 | |
| 138 namespace { | |
| 139 | |
| 140 // Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to | |
| 141 // |pickle|, if and only if |str| fits within (|max_bytes| - | |
| 142 // |*bytes_written|). |bytes_written| is incremented to reflect the | |
| 143 // data written. | |
| 144 // | |
| 145 // TODO(akalin): Unify this with the same function in | |
| 146 // base_session_service.cc. | |
| 147 void WriteStringToPickle(base::Pickle* pickle, | |
| 148 int* bytes_written, | |
| 149 int max_bytes, | |
| 150 const std::string& str) { | |
| 151 int num_bytes = str.size() * sizeof(char); | |
| 152 if (*bytes_written + num_bytes < max_bytes) { | |
| 153 *bytes_written += num_bytes; | |
| 154 pickle->WriteString(str); | |
| 155 } else { | |
| 156 pickle->WriteString(std::string()); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 // base::string16 version of WriteStringToPickle. | |
| 161 // | |
| 162 // TODO(akalin): Unify this, too. | |
| 163 void WriteString16ToPickle(base::Pickle* pickle, | |
| 164 int* bytes_written, | |
| 165 int max_bytes, | |
| 166 const base::string16& str) { | |
| 167 int num_bytes = str.size() * sizeof(base::char16); | |
| 168 if (*bytes_written + num_bytes < max_bytes) { | |
| 169 *bytes_written += num_bytes; | |
| 170 pickle->WriteString16(str); | |
| 171 } else { | |
| 172 pickle->WriteString16(base::string16()); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 // A mask used for arbitrary boolean values needed to represent a | |
| 177 // NavigationEntry. Currently only contains HAS_POST_DATA. | |
| 178 // | |
| 179 // NOTE(akalin): We may want to just serialize |has_post_data_| | |
| 180 // directly. Other bools (|is_overriding_user_agent_|) haven't been | |
| 181 // added to this mask. | |
| 182 enum TypeMask { | |
| 183 HAS_POST_DATA = 1 | |
| 184 }; | |
| 185 | |
| 186 } // namespace | |
| 187 | |
| 188 // Pickle order: | |
| 189 // | |
| 190 // index_ | |
| 191 // virtual_url_ | |
| 192 // title_ | |
| 193 // encoded_page_state_ | |
| 194 // transition_type_ | |
| 195 // | |
| 196 // Added on later: | |
| 197 // | |
| 198 // type_mask (has_post_data_) | |
| 199 // referrer_url_ | |
| 200 // referrer_policy_ (broken, crbug.com/450589) | |
| 201 // original_request_url_ | |
| 202 // is_overriding_user_agent_ | |
| 203 // timestamp_ | |
| 204 // search_terms_ | |
| 205 // http_status_code_ | |
| 206 // referrer_policy_ | |
| 207 | |
| 208 void SerializedNavigationEntry::WriteToPickle(int max_size, | |
| 209 base::Pickle* pickle) const { | |
| 210 pickle->WriteInt(index_); | |
| 211 | |
| 212 int bytes_written = 0; | |
| 213 | |
| 214 WriteStringToPickle(pickle, &bytes_written, max_size, | |
| 215 virtual_url_.spec()); | |
| 216 | |
| 217 WriteString16ToPickle(pickle, &bytes_written, max_size, title_); | |
| 218 | |
| 219 const std::string encoded_page_state = | |
| 220 SerializedNavigationDriver::Get()->GetSanitizedPageStateForPickle(this); | |
| 221 WriteStringToPickle(pickle, &bytes_written, max_size, encoded_page_state); | |
| 222 | |
| 223 pickle->WriteInt(transition_type_); | |
| 224 | |
| 225 const int type_mask = has_post_data_ ? HAS_POST_DATA : 0; | |
| 226 pickle->WriteInt(type_mask); | |
| 227 | |
| 228 int mapped_referrer_policy; | |
| 229 if (SerializedNavigationDriver::Get()->MapReferrerPolicyToOldValues( | |
| 230 referrer_policy_, &mapped_referrer_policy) && | |
| 231 referrer_url_.is_valid()) { | |
| 232 WriteStringToPickle(pickle, &bytes_written, max_size, referrer_url_.spec()); | |
| 233 } else { | |
| 234 WriteStringToPickle(pickle, &bytes_written, max_size, std::string()); | |
| 235 } | |
| 236 pickle->WriteInt(mapped_referrer_policy); | |
| 237 | |
| 238 // Save info required to override the user agent. | |
| 239 WriteStringToPickle( | |
| 240 pickle, &bytes_written, max_size, | |
| 241 original_request_url_.is_valid() ? | |
| 242 original_request_url_.spec() : std::string()); | |
| 243 pickle->WriteBool(is_overriding_user_agent_); | |
| 244 pickle->WriteInt64(timestamp_.ToInternalValue()); | |
| 245 | |
| 246 WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_); | |
| 247 | |
| 248 pickle->WriteInt(http_status_code_); | |
| 249 | |
| 250 pickle->WriteInt(referrer_policy_); | |
| 251 } | |
| 252 | |
| 253 bool SerializedNavigationEntry::ReadFromPickle(base::PickleIterator* iterator) { | |
| 254 *this = SerializedNavigationEntry(); | |
| 255 std::string virtual_url_spec; | |
| 256 int transition_type_int = 0; | |
| 257 if (!iterator->ReadInt(&index_) || | |
| 258 !iterator->ReadString(&virtual_url_spec) || | |
| 259 !iterator->ReadString16(&title_) || | |
| 260 !iterator->ReadString(&encoded_page_state_) || | |
| 261 !iterator->ReadInt(&transition_type_int)) | |
| 262 return false; | |
| 263 virtual_url_ = GURL(virtual_url_spec); | |
| 264 transition_type_ = ui::PageTransitionFromInt(transition_type_int); | |
| 265 | |
| 266 // type_mask did not always exist in the written stream. As such, we | |
| 267 // don't fail if it can't be read. | |
| 268 int type_mask = 0; | |
| 269 bool has_type_mask = iterator->ReadInt(&type_mask); | |
| 270 | |
| 271 if (has_type_mask) { | |
| 272 has_post_data_ = type_mask & HAS_POST_DATA; | |
| 273 // the "referrer" property was added after type_mask to the written | |
| 274 // stream. As such, we don't fail if it can't be read. | |
| 275 std::string referrer_spec; | |
| 276 if (!iterator->ReadString(&referrer_spec)) | |
| 277 referrer_spec = std::string(); | |
| 278 referrer_url_ = GURL(referrer_spec); | |
| 279 | |
| 280 // The "referrer policy" property was added even later, so we fall back to | |
| 281 // the default policy if the property is not present. | |
| 282 // | |
| 283 // Note: due to crbug.com/450589 this value might be incorrect, and a | |
| 284 // corrected version is stored later in the pickle. | |
| 285 if (!iterator->ReadInt(&referrer_policy_)) { | |
| 286 referrer_policy_ = | |
| 287 SerializedNavigationDriver::Get()->GetDefaultReferrerPolicy(); | |
| 288 } | |
| 289 | |
| 290 // If the original URL can't be found, leave it empty. | |
| 291 std::string original_request_url_spec; | |
| 292 if (!iterator->ReadString(&original_request_url_spec)) | |
| 293 original_request_url_spec = std::string(); | |
| 294 original_request_url_ = GURL(original_request_url_spec); | |
| 295 | |
| 296 // Default to not overriding the user agent if we don't have info. | |
| 297 if (!iterator->ReadBool(&is_overriding_user_agent_)) | |
| 298 is_overriding_user_agent_ = false; | |
| 299 | |
| 300 int64 timestamp_internal_value = 0; | |
| 301 if (iterator->ReadInt64(×tamp_internal_value)) { | |
| 302 timestamp_ = base::Time::FromInternalValue(timestamp_internal_value); | |
| 303 } else { | |
| 304 timestamp_ = base::Time(); | |
| 305 } | |
| 306 | |
| 307 // If the search terms field can't be found, leave it empty. | |
| 308 if (!iterator->ReadString16(&search_terms_)) | |
| 309 search_terms_.clear(); | |
| 310 | |
| 311 if (!iterator->ReadInt(&http_status_code_)) | |
| 312 http_status_code_ = 0; | |
| 313 | |
| 314 // Correct referrer policy (if present). | |
| 315 int correct_referrer_policy; | |
| 316 if (iterator->ReadInt(&correct_referrer_policy)) { | |
| 317 referrer_policy_ = correct_referrer_policy; | |
| 318 } else { | |
| 319 int mapped_referrer_policy; | |
| 320 if (!SerializedNavigationDriver::Get()->MapReferrerPolicyToNewValues( | |
| 321 referrer_policy_, &mapped_referrer_policy)) { | |
| 322 referrer_url_ = GURL(); | |
| 323 } | |
| 324 referrer_policy_ = mapped_referrer_policy; | |
| 325 encoded_page_state_ = | |
| 326 SerializedNavigationDriver::Get()->StripReferrerFromPageState( | |
| 327 encoded_page_state_); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 SerializedNavigationDriver::Get()->Sanitize(this); | |
| 332 | |
| 333 is_restored_ = true; | |
| 334 | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 // TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well? | |
| 339 // See http://crbug.com/67068. | |
| 340 sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const { | |
| 341 sync_pb::TabNavigation sync_data; | |
| 342 sync_data.set_virtual_url(virtual_url_.spec()); | |
| 343 int mapped_referrer_policy; | |
| 344 if (SerializedNavigationDriver::Get()->MapReferrerPolicyToOldValues( | |
| 345 referrer_policy_, &mapped_referrer_policy)) { | |
| 346 sync_data.set_referrer(referrer_url_.spec()); | |
| 347 } else { | |
| 348 sync_data.set_referrer(std::string()); | |
| 349 } | |
| 350 sync_data.set_obsolete_referrer_policy(mapped_referrer_policy); | |
| 351 sync_data.set_correct_referrer_policy(referrer_policy_); | |
| 352 sync_data.set_title(base::UTF16ToUTF8(title_)); | |
| 353 | |
| 354 // Page transition core. | |
| 355 static_assert(ui::PAGE_TRANSITION_LAST_CORE == | |
| 356 ui::PAGE_TRANSITION_KEYWORD_GENERATED, | |
| 357 "PAGE_TRANSITION_LAST_CORE must equal " | |
| 358 "PAGE_TRANSITION_KEYWORD_GENERATED"); | |
| 359 switch (ui::PageTransitionStripQualifier(transition_type_)) { | |
| 360 case ui::PAGE_TRANSITION_LINK: | |
| 361 sync_data.set_page_transition( | |
| 362 sync_pb::SyncEnums_PageTransition_LINK); | |
| 363 break; | |
| 364 case ui::PAGE_TRANSITION_TYPED: | |
| 365 sync_data.set_page_transition( | |
| 366 sync_pb::SyncEnums_PageTransition_TYPED); | |
| 367 break; | |
| 368 case ui::PAGE_TRANSITION_AUTO_BOOKMARK: | |
| 369 sync_data.set_page_transition( | |
| 370 sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK); | |
| 371 break; | |
| 372 case ui::PAGE_TRANSITION_AUTO_SUBFRAME: | |
| 373 sync_data.set_page_transition( | |
| 374 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME); | |
| 375 break; | |
| 376 case ui::PAGE_TRANSITION_MANUAL_SUBFRAME: | |
| 377 sync_data.set_page_transition( | |
| 378 sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME); | |
| 379 break; | |
| 380 case ui::PAGE_TRANSITION_GENERATED: | |
| 381 sync_data.set_page_transition( | |
| 382 sync_pb::SyncEnums_PageTransition_GENERATED); | |
| 383 break; | |
| 384 case ui::PAGE_TRANSITION_AUTO_TOPLEVEL: | |
| 385 sync_data.set_page_transition( | |
| 386 sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL); | |
| 387 break; | |
| 388 case ui::PAGE_TRANSITION_FORM_SUBMIT: | |
| 389 sync_data.set_page_transition( | |
| 390 sync_pb::SyncEnums_PageTransition_FORM_SUBMIT); | |
| 391 break; | |
| 392 case ui::PAGE_TRANSITION_RELOAD: | |
| 393 sync_data.set_page_transition( | |
| 394 sync_pb::SyncEnums_PageTransition_RELOAD); | |
| 395 break; | |
| 396 case ui::PAGE_TRANSITION_KEYWORD: | |
| 397 sync_data.set_page_transition( | |
| 398 sync_pb::SyncEnums_PageTransition_KEYWORD); | |
| 399 break; | |
| 400 case ui::PAGE_TRANSITION_KEYWORD_GENERATED: | |
| 401 sync_data.set_page_transition( | |
| 402 sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED); | |
| 403 break; | |
| 404 default: | |
| 405 NOTREACHED(); | |
| 406 } | |
| 407 | |
| 408 // Page transition qualifiers. | |
| 409 if (ui::PageTransitionIsRedirect(transition_type_)) { | |
| 410 if (transition_type_ & ui::PAGE_TRANSITION_CLIENT_REDIRECT) { | |
| 411 sync_data.set_redirect_type( | |
| 412 sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT); | |
| 413 } else if (transition_type_ & ui::PAGE_TRANSITION_SERVER_REDIRECT) { | |
| 414 sync_data.set_redirect_type( | |
| 415 sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT); | |
| 416 } | |
| 417 } | |
| 418 sync_data.set_navigation_forward_back( | |
| 419 (transition_type_ & ui::PAGE_TRANSITION_FORWARD_BACK) != 0); | |
| 420 sync_data.set_navigation_from_address_bar( | |
| 421 (transition_type_ & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0); | |
| 422 sync_data.set_navigation_home_page( | |
| 423 (transition_type_ & ui::PAGE_TRANSITION_HOME_PAGE) != 0); | |
| 424 sync_data.set_navigation_chain_start( | |
| 425 (transition_type_ & ui::PAGE_TRANSITION_CHAIN_START) != 0); | |
| 426 sync_data.set_navigation_chain_end( | |
| 427 (transition_type_ & ui::PAGE_TRANSITION_CHAIN_END) != 0); | |
| 428 | |
| 429 sync_data.set_unique_id(unique_id_); | |
| 430 sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_)); | |
| 431 // The full-resolution timestamp works as a global ID. | |
| 432 sync_data.set_global_id(timestamp_.ToInternalValue()); | |
| 433 | |
| 434 sync_data.set_search_terms(base::UTF16ToUTF8(search_terms_)); | |
| 435 | |
| 436 sync_data.set_http_status_code(http_status_code_); | |
| 437 | |
| 438 if (favicon_url_.is_valid()) | |
| 439 sync_data.set_favicon_url(favicon_url_.spec()); | |
| 440 | |
| 441 if (blocked_state_ != STATE_INVALID) { | |
| 442 sync_data.set_blocked_state( | |
| 443 static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_)); | |
| 444 } | |
| 445 | |
| 446 for (std::set<std::string>::const_iterator it = | |
| 447 content_pack_categories_.begin(); | |
| 448 it != content_pack_categories_.end(); ++it) { | |
| 449 sync_data.add_content_pack_categories(*it); | |
| 450 } | |
| 451 | |
| 452 // Copy all redirect chain entries except the last URL (which should match | |
| 453 // the virtual_url). | |
| 454 if (redirect_chain_.size() > 1) { // Single entry chains have no redirection. | |
| 455 size_t last_entry = redirect_chain_.size() - 1; | |
| 456 for (size_t i = 0; i < last_entry; i++) { | |
| 457 sync_pb::NavigationRedirect* navigation_redirect = | |
| 458 sync_data.add_navigation_redirect(); | |
| 459 navigation_redirect->set_url(redirect_chain_[i].spec()); | |
| 460 } | |
| 461 // If the last URL didn't match the virtual_url, record it separately. | |
| 462 if (sync_data.virtual_url() != redirect_chain_[last_entry].spec()) { | |
| 463 sync_data.set_last_navigation_redirect_url( | |
| 464 redirect_chain_[last_entry].spec()); | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 sync_data.set_is_restored(is_restored_); | |
| 469 | |
| 470 return sync_data; | |
| 471 } | |
| 472 | |
| 473 } // namespace sessions | |
| OLD | NEW |