1 // Copyright (c) 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 <cstddef> 8 #include <string> 9 10 #include "base/basictypes.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/pickle.h" 13 #include "base/strings/string16.h" 14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/time/time.h" 17 #include "content/public/browser/favicon_status.h" 18 #include "content/public/browser/navigation_entry.h" 19 #include "content/public/common/referrer.h" 20 #include "sync/protocol/session_specifics.pb.h" 21 #include "sync/util/time.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 #include "third_party/WebKit/public/platform/WebReferrerPolicy.h" 24 #include "ui/base/page_transition_types.h" 25 #include "url/gurl.h" 26 27 namespace sessions { 28 namespace { 29 30 const int kIndex = 3; 31 const int kUniqueID = 50; 32 const content::Referrer kReferrer = 33 content::Referrer(GURL("http://www.referrer.com"), 34 blink::WebReferrerPolicyAlways); 35 const GURL kVirtualURL("http://www.virtual-url.com"); 36 const base::string16 kTitle = base::ASCIIToUTF16("title"); 37 const content::PageState kPageState = 38 content::PageState::CreateFromEncodedData("page state"); 39 const ui::PageTransition kTransitionType = 40 ui::PageTransitionFromInt( 41 ui::PAGE_TRANSITION_AUTO_SUBFRAME | 42 ui::PAGE_TRANSITION_HOME_PAGE | 43 ui::PAGE_TRANSITION_CLIENT_REDIRECT); 44 const bool kHasPostData = true; 45 const int64 kPostID = 100; 46 const GURL kOriginalRequestURL("http://www.original-request.com"); 47 const bool kIsOverridingUserAgent = true; 48 const base::Time kTimestamp = syncer::ProtoTimeToTime(100); 49 const base::string16 kSearchTerms = base::ASCIIToUTF16("my search terms"); 50 const GURL kFaviconURL("http://virtual-url.com/favicon.ico"); 51 const int kHttpStatusCode = 404; 52 const GURL kRedirectURL0("http://go/redirect0"); 53 const GURL kRedirectURL1("http://go/redirect1"); 54 const GURL kOtherURL("http://other.com"); 55 56 const int kPageID = 10; 57 58 // Create a NavigationEntry from the constants above. 59 scoped_ptr<content::NavigationEntry> MakeNavigationEntryForTest() { 60 scoped_ptr<content::NavigationEntry> navigation_entry( 61 content::NavigationEntry::Create()); 62 navigation_entry->SetReferrer(kReferrer); 63 navigation_entry->SetVirtualURL(kVirtualURL); 64 navigation_entry->SetTitle(kTitle); 65 navigation_entry->SetPageState(kPageState); 66 navigation_entry->SetTransitionType(kTransitionType); 67 navigation_entry->SetHasPostData(kHasPostData); 68 navigation_entry->SetPostID(kPostID); 69 navigation_entry->SetOriginalRequestURL(kOriginalRequestURL); 70 navigation_entry->SetIsOverridingUserAgent(kIsOverridingUserAgent); 71 navigation_entry->SetTimestamp(kTimestamp); 72 navigation_entry->SetExtraData(kSearchTermsKey, kSearchTerms); 73 navigation_entry->GetFavicon().valid = true; 74 navigation_entry->GetFavicon().url = kFaviconURL; 75 navigation_entry->SetHttpStatusCode(kHttpStatusCode); 76 std::vector<GURL> redirect_chain; 77 redirect_chain.push_back(kRedirectURL0); 78 redirect_chain.push_back(kRedirectURL1); 79 redirect_chain.push_back(kVirtualURL); 80 navigation_entry->SetRedirectChain(redirect_chain); 81 return navigation_entry.Pass(); 82 } 83 84 // Create a sync_pb::TabNavigation from the constants above. 85 sync_pb::TabNavigation MakeSyncDataForTest() { 86 sync_pb::TabNavigation sync_data; 87 sync_data.set_virtual_url(kVirtualURL.spec()); 88 sync_data.set_referrer(kReferrer.url.spec()); 89 sync_data.set_referrer_policy(blink::WebReferrerPolicyOrigin); 90 sync_data.set_title(base::UTF16ToUTF8(kTitle)); 91 sync_data.set_state(kPageState.ToEncodedData()); 92 sync_data.set_page_transition( 93 sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME); 94 sync_data.set_unique_id(kUniqueID); 95 sync_data.set_timestamp_msec(syncer::TimeToProtoTime(kTimestamp)); 96 sync_data.set_redirect_type(sync_pb::SyncEnums::CLIENT_REDIRECT); 97 sync_data.set_navigation_home_page(true); 98 sync_data.set_search_terms(base::UTF16ToUTF8(kSearchTerms)); 99 sync_data.set_favicon_url(kFaviconURL.spec()); 100 sync_data.set_http_status_code(kHttpStatusCode); 101 // The redirect chain only syncs one way. 102 return sync_data; 103 } 104 105 // Create a default SerializedNavigationEntry. All its fields should be 106 // initialized to their respective default values. 107 TEST(SerializedNavigationEntryTest, DefaultInitializer) { 108 const SerializedNavigationEntry navigation; 109 EXPECT_EQ(-1, navigation.index()); 110 EXPECT_EQ(0, navigation.unique_id()); 111 EXPECT_EQ(GURL(), navigation.referrer().url); 112 EXPECT_EQ(blink::WebReferrerPolicyDefault, navigation.referrer().policy); 113 EXPECT_EQ(GURL(), navigation.virtual_url()); 114 EXPECT_TRUE(navigation.title().empty()); 115 EXPECT_FALSE(navigation.page_state().IsValid()); 116 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED, navigation.transition_type()); 117 EXPECT_FALSE(navigation.has_post_data()); 118 EXPECT_EQ(-1, navigation.post_id()); 119 EXPECT_EQ(GURL(), navigation.original_request_url()); 120 EXPECT_FALSE(navigation.is_overriding_user_agent()); 121 EXPECT_TRUE(navigation.timestamp().is_null()); 122 EXPECT_TRUE(navigation.search_terms().empty()); 123 EXPECT_FALSE(navigation.favicon_url().is_valid()); 124 EXPECT_EQ(0, navigation.http_status_code()); 125 EXPECT_EQ(0U, navigation.redirect_chain().size()); 126 } 127 128 // Create a SerializedNavigationEntry from a NavigationEntry. All its fields 129 // should match the NavigationEntry's. 130 TEST(SerializedNavigationEntryTest, FromNavigationEntry) { 131 const scoped_ptr<content::NavigationEntry> navigation_entry( 132 MakeNavigationEntryForTest()); 133 134 const SerializedNavigationEntry& navigation = 135 SerializedNavigationEntry::FromNavigationEntry(kIndex, *navigation_entry); 136 137 EXPECT_EQ(kIndex, navigation.index()); 138 139 EXPECT_EQ(navigation_entry->GetUniqueID(), navigation.unique_id()); 140 EXPECT_EQ(kReferrer.url, navigation.referrer().url); 141 EXPECT_EQ(kReferrer.policy, navigation.referrer().policy); 142 EXPECT_EQ(kVirtualURL, navigation.virtual_url()); 143 EXPECT_EQ(kTitle, navigation.title()); 144 EXPECT_EQ(kPageState, navigation.page_state()); 145 EXPECT_EQ(kTransitionType, navigation.transition_type()); 146 EXPECT_EQ(kHasPostData, navigation.has_post_data()); 147 EXPECT_EQ(kPostID, navigation.post_id()); 148 EXPECT_EQ(kOriginalRequestURL, navigation.original_request_url()); 149 EXPECT_EQ(kIsOverridingUserAgent, navigation.is_overriding_user_agent()); 150 EXPECT_EQ(kTimestamp, navigation.timestamp()); 151 EXPECT_EQ(kFaviconURL, navigation.favicon_url()); 152 EXPECT_EQ(kHttpStatusCode, navigation.http_status_code()); 153 ASSERT_EQ(3U, navigation.redirect_chain().size()); 154 EXPECT_EQ(kRedirectURL0, navigation.redirect_chain()[0]); 155 EXPECT_EQ(kRedirectURL1, navigation.redirect_chain()[1]); 156 EXPECT_EQ(kVirtualURL, navigation.redirect_chain()[2]); 157 } 158 159 // Create a SerializedNavigationEntry from a sync_pb::TabNavigation. All its 160 // fields should match the protocol buffer's if it exists there, and 161 // sbould be set to the default value otherwise. 162 TEST(SerializedNavigationEntryTest, FromSyncData) { 163 const sync_pb::TabNavigation sync_data = MakeSyncDataForTest(); 164 165 const SerializedNavigationEntry& navigation = 166 SerializedNavigationEntry::FromSyncData(kIndex, sync_data); 167 168 EXPECT_EQ(kIndex, navigation.index()); 169 EXPECT_EQ(kUniqueID, navigation.unique_id()); 170 EXPECT_EQ(kReferrer.url, navigation.referrer().url); 171 EXPECT_EQ(blink::WebReferrerPolicyOrigin, navigation.referrer().policy); 172 EXPECT_EQ(kVirtualURL, navigation.virtual_url()); 173 EXPECT_EQ(kTitle, navigation.title()); 174 EXPECT_EQ(kPageState, navigation.page_state()); 175 EXPECT_EQ(kTransitionType, navigation.transition_type()); 176 EXPECT_FALSE(navigation.has_post_data()); 177 EXPECT_EQ(-1, navigation.post_id()); 178 EXPECT_EQ(GURL(), navigation.original_request_url()); 179 EXPECT_FALSE(navigation.is_overriding_user_agent()); 180 EXPECT_TRUE(navigation.timestamp().is_null()); 181 EXPECT_EQ(kSearchTerms, navigation.search_terms()); 182 EXPECT_EQ(kFaviconURL, navigation.favicon_url()); 183 EXPECT_EQ(kHttpStatusCode, navigation.http_status_code()); 184 // The redirect chain only syncs one way. 185 } 186 187 // Create a SerializedNavigationEntry, pickle it, then create another one by 188 // unpickling. The new one should match the old one except for fields 189 // that aren't pickled, which should be set to default values. 190 TEST(SerializedNavigationEntryTest, Pickle) { 191 const SerializedNavigationEntry& old_navigation = 192 SerializedNavigationEntry::FromNavigationEntry( 193 kIndex, *MakeNavigationEntryForTest()); 194 195 Pickle pickle; 196 old_navigation.WriteToPickle(30000, &pickle); 197 198 SerializedNavigationEntry new_navigation; 199 PickleIterator pickle_iterator(pickle); 200 EXPECT_TRUE(new_navigation.ReadFromPickle(&pickle_iterator)); 201 202 EXPECT_EQ(kIndex, new_navigation.index()); 203 204 EXPECT_EQ(0, new_navigation.unique_id()); 205 EXPECT_EQ(kReferrer.url, new_navigation.referrer().url); 206 EXPECT_EQ(kReferrer.policy, new_navigation.referrer().policy); 207 EXPECT_EQ(kVirtualURL, new_navigation.virtual_url()); 208 EXPECT_EQ(kTitle, new_navigation.title()); 209 EXPECT_FALSE(new_navigation.page_state().IsValid()); 210 EXPECT_EQ(kTransitionType, new_navigation.transition_type()); 211 EXPECT_EQ(kHasPostData, new_navigation.has_post_data()); 212 EXPECT_EQ(-1, new_navigation.post_id()); 213 EXPECT_EQ(kOriginalRequestURL, new_navigation.original_request_url()); 214 EXPECT_EQ(kIsOverridingUserAgent, new_navigation.is_overriding_user_agent()); 215 EXPECT_EQ(kTimestamp, new_navigation.timestamp()); 216 EXPECT_EQ(kSearchTerms, new_navigation.search_terms()); 217 EXPECT_EQ(kHttpStatusCode, new_navigation.http_status_code()); 218 EXPECT_EQ(0U, new_navigation.redirect_chain().size()); 219 } 220 221 // Create a NavigationEntry, then create another one by converting to 222 // a SerializedNavigationEntry and back. The new one should match the old one 223 // except for fields that aren't preserved, which should be set to 224 // expected values. 225 TEST(SerializedNavigationEntryTest, ToNavigationEntry) { 226 const scoped_ptr<content::NavigationEntry> old_navigation_entry( 227 MakeNavigationEntryForTest()); 228 229 const SerializedNavigationEntry& navigation = 230 SerializedNavigationEntry::FromNavigationEntry(kIndex, 231 *old_navigation_entry); 232 233 const scoped_ptr<content::NavigationEntry> new_navigation_entry( 234 navigation.ToNavigationEntry(kPageID, NULL)); 235 236 EXPECT_EQ(kReferrer.url, new_navigation_entry->GetReferrer().url); 237 EXPECT_EQ(kReferrer.policy, new_navigation_entry->GetReferrer().policy); 238 EXPECT_EQ(kVirtualURL, new_navigation_entry->GetVirtualURL()); 239 EXPECT_EQ(kTitle, new_navigation_entry->GetTitle()); 240 EXPECT_EQ(kPageState, new_navigation_entry->GetPageState()); 241 EXPECT_EQ(kPageID, new_navigation_entry->GetPageID()); 242 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD, 243 new_navigation_entry->GetTransitionType()); 244 EXPECT_EQ(kHasPostData, new_navigation_entry->GetHasPostData()); 245 EXPECT_EQ(kPostID, new_navigation_entry->GetPostID()); 246 EXPECT_EQ(kOriginalRequestURL, 247 new_navigation_entry->GetOriginalRequestURL()); 248 EXPECT_EQ(kIsOverridingUserAgent, 249 new_navigation_entry->GetIsOverridingUserAgent()); 250 base::string16 search_terms; 251 new_navigation_entry->GetExtraData(kSearchTermsKey, &search_terms); 252 EXPECT_EQ(kSearchTerms, search_terms); 253 EXPECT_EQ(kHttpStatusCode, new_navigation_entry->GetHttpStatusCode()); 254 ASSERT_EQ(3U, new_navigation_entry->GetRedirectChain().size()); 255 EXPECT_EQ(kRedirectURL0, new_navigation_entry->GetRedirectChain()[0]); 256 EXPECT_EQ(kRedirectURL1, new_navigation_entry->GetRedirectChain()[1]); 257 EXPECT_EQ(kVirtualURL, new_navigation_entry->GetRedirectChain()[2]); 258 } 259 260 // Create a NavigationEntry, convert it to a SerializedNavigationEntry, then 261 // create a sync protocol buffer from it. The protocol buffer should 262 // have matching fields to the NavigationEntry (when applicable). 263 TEST(SerializedNavigationEntryTest, ToSyncData) { 264 const scoped_ptr<content::NavigationEntry> navigation_entry( 265 MakeNavigationEntryForTest()); 266 267 const SerializedNavigationEntry& navigation = 268 SerializedNavigationEntry::FromNavigationEntry(kIndex, *navigation_entry); 269 270 const sync_pb::TabNavigation sync_data = navigation.ToSyncData(); 271 272 EXPECT_EQ(kVirtualURL.spec(), sync_data.virtual_url()); 273 EXPECT_EQ(kReferrer.url.spec(), sync_data.referrer()); 274 EXPECT_EQ(kTitle, base::ASCIIToUTF16(sync_data.title())); 275 EXPECT_TRUE(sync_data.state().empty()); 276 EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME, 277 sync_data.page_transition()); 278 EXPECT_TRUE(sync_data.has_redirect_type()); 279 EXPECT_EQ(navigation_entry->GetUniqueID(), sync_data.unique_id()); 280 EXPECT_EQ(syncer::TimeToProtoTime(kTimestamp), sync_data.timestamp_msec()); 281 EXPECT_EQ(kTimestamp.ToInternalValue(), sync_data.global_id()); 282 EXPECT_EQ(kFaviconURL.spec(), sync_data.favicon_url()); 283 EXPECT_EQ(kHttpStatusCode, sync_data.http_status_code()); 284 // The proto navigation redirects don't include the final chain entry 285 // (because it didn't redirect) so the lengths should differ by 1. 286 ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1); 287 EXPECT_EQ(navigation_entry->GetRedirectChain()[0].spec(), 288 sync_data.navigation_redirect(0).url()); 289 EXPECT_EQ(navigation_entry->GetRedirectChain()[1].spec(), 290 sync_data.navigation_redirect(1).url()); 291 EXPECT_FALSE(sync_data.has_last_navigation_redirect_url()); 292 } 293 294 // Test that the last_navigation_redirect_url is set when needed. 295 // This test is just like the above, but with a different virtual_url. 296 // Create a NavigationEntry, convert it to a SerializedNavigationEntry, then 297 // create a sync protocol buffer from it. The protocol buffer should 298 // have a last_navigation_redirect_url. 299 TEST(SerializedNavigationEntryTest, LastNavigationRedirectUrl) { 300 const scoped_ptr<content::NavigationEntry> navigation_entry( 301 MakeNavigationEntryForTest()); 302 303 navigation_entry->SetVirtualURL(kOtherURL); 304 305 const SerializedNavigationEntry& navigation = 306 SerializedNavigationEntry::FromNavigationEntry(kIndex, *navigation_entry); 307 308 const sync_pb::TabNavigation sync_data = navigation.ToSyncData(); 309 310 EXPECT_TRUE(sync_data.has_last_navigation_redirect_url()); 311 EXPECT_EQ(kVirtualURL.spec(), sync_data.last_navigation_redirect_url()); 312 313 // The redirect chain should be the same as in the above test. 314 ASSERT_EQ(3, sync_data.navigation_redirect_size() + 1); 315 EXPECT_EQ(navigation_entry->GetRedirectChain()[0].spec(), 316 sync_data.navigation_redirect(0).url()); 317 EXPECT_EQ(navigation_entry->GetRedirectChain()[1].spec(), 318 sync_data.navigation_redirect(1).url()); 319 } 320 321 // Ensure all transition types and qualifiers are converted to/from the sync 322 // SerializedNavigationEntry representation properly. 323 TEST(SerializedNavigationEntryTest, TransitionTypes) { 324 scoped_ptr<content::NavigationEntry> navigation_entry( 325 MakeNavigationEntryForTest()); 326 for (uint32 core_type = ui::PAGE_TRANSITION_LINK; 327 core_type != ui::PAGE_TRANSITION_LAST_CORE; ++core_type) { 328 // Because qualifier is a uint32, left shifting will eventually overflow 329 // and hit zero again. SERVER_REDIRECT, as the last qualifier and also 330 // in place of the sign bit, is therefore the last transition before 331 // breaking. 332 for (uint32 qualifier = ui::PAGE_TRANSITION_FORWARD_BACK; 333 qualifier != 0; qualifier <<= 1) { 334 if (qualifier == 0x08000000) 335 continue; // 0x08000000 is not a valid qualifier. 336 ui::PageTransition transition = 337 ui::PageTransitionFromInt(core_type | qualifier); 338 339 navigation_entry->SetTransitionType(transition); 340 const SerializedNavigationEntry& navigation = 341 SerializedNavigationEntry::FromNavigationEntry(kIndex, 342 *navigation_entry); 343 const sync_pb::TabNavigation& sync_data = navigation.ToSyncData(); 344 const SerializedNavigationEntry& constructed_nav = 345 SerializedNavigationEntry::FromSyncData(kIndex, sync_data); 346 const ui::PageTransition constructed_transition = 347 constructed_nav.transition_type(); 348 349 EXPECT_EQ(transition, constructed_transition); 350 } 351 } 352 } 353 354 // Tests that the input data is sanitized when a SerializedNavigationEntry 355 // is created from a pickle format. 356 TEST(SerializedNavigationEntryTest, Sanitize) { 357 sync_pb::TabNavigation sync_data = MakeSyncDataForTest(); 358 359 sync_data.set_referrer_policy(blink::WebReferrerPolicyNever); 360 content::PageState page_state = 361 content::PageState::CreateFromURL(kVirtualURL); 362 sync_data.set_state(page_state.ToEncodedData()); 363 364 const SerializedNavigationEntry& navigation = 365 SerializedNavigationEntry::FromSyncData(kIndex, sync_data); 366 367 EXPECT_EQ(kIndex, navigation.index()); 368 EXPECT_EQ(kUniqueID, navigation.unique_id()); 369 EXPECT_EQ(GURL(), navigation.referrer().url); 370 EXPECT_EQ(blink::WebReferrerPolicyDefault, navigation.referrer().policy); 371 EXPECT_EQ(kVirtualURL, navigation.virtual_url()); 372 EXPECT_EQ(kTitle, navigation.title()); 373 EXPECT_EQ(page_state, navigation.page_state()); 374 EXPECT_EQ(kTransitionType, navigation.transition_type()); 375 EXPECT_FALSE(navigation.has_post_data()); 376 EXPECT_EQ(-1, navigation.post_id()); 377 EXPECT_EQ(GURL(), navigation.original_request_url()); 378 EXPECT_FALSE(navigation.is_overriding_user_agent()); 379 EXPECT_TRUE(navigation.timestamp().is_null()); 380 EXPECT_EQ(kSearchTerms, navigation.search_terms()); 381 EXPECT_EQ(kFaviconURL, navigation.favicon_url()); 382 EXPECT_EQ(kHttpStatusCode, navigation.http_status_code()); 383 384 content::PageState empty_state; 385 EXPECT_TRUE(empty_state.Equals(empty_state.RemoveReferrer())); 386 } 387 388 } // namespace 389 } // namespace sessions 390