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
27namespace sessions {
28namespace {
29
30const int kIndex = 3;
31const int kUniqueID = 50;
32const content::Referrer kReferrer =
33    content::Referrer(GURL("http://www.referrer.com"),
34                      blink::WebReferrerPolicyAlways);
35const GURL kVirtualURL("http://www.virtual-url.com");
36const base::string16 kTitle = base::ASCIIToUTF16("title");
37const content::PageState kPageState =
38    content::PageState::CreateFromEncodedData("page state");
39const ui::PageTransition kTransitionType =
40    ui::PageTransitionFromInt(
41        ui::PAGE_TRANSITION_AUTO_SUBFRAME |
42        ui::PAGE_TRANSITION_HOME_PAGE |
43        ui::PAGE_TRANSITION_CLIENT_REDIRECT);
44const bool kHasPostData = true;
45const int64 kPostID = 100;
46const GURL kOriginalRequestURL("http://www.original-request.com");
47const bool kIsOverridingUserAgent = true;
48const base::Time kTimestamp = syncer::ProtoTimeToTime(100);
49const base::string16 kSearchTerms = base::ASCIIToUTF16("my search terms");
50const GURL kFaviconURL("http://virtual-url.com/favicon.ico");
51const int kHttpStatusCode = 404;
52const GURL kRedirectURL0("http://go/redirect0");
53const GURL kRedirectURL1("http://go/redirect1");
54const GURL kOtherURL("http://other.com");
55
56const int kPageID = 10;
57
58// Create a NavigationEntry from the constants above.
59scoped_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.
85sync_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.
107TEST(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.
130TEST(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.
162TEST(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.
190TEST(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.
225TEST(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).
263TEST(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.
299TEST(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.
323TEST(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.
356TEST(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