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.
7 #include "base/base64.h"
8 #include "base/file_util.h"
9 #include "base/path_service.h"
10 #include "base/pickle.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "content/common/page_state_serialization.h"
15 #include "content/public/common/content_paths.h"
16 #include "testing/gtest/include/gtest/gtest.h"
22 inline bool isnan(double num
) { return !!_isnan(num
); }
25 base::NullableString16
NS16(const char* s
) {
26 return s
? base::NullableString16(ASCIIToUTF16(s
), false) :
27 base::NullableString16();
30 //-----------------------------------------------------------------------------
33 void ExpectEquality(const T
& a
, const T
& b
) {
38 void ExpectEquality(const std::vector
<T
>& a
, const std::vector
<T
>& b
) {
39 EXPECT_EQ(a
.size(), b
.size());
40 for (size_t i
= 0; i
< std::min(a
.size(), b
.size()); ++i
)
41 ExpectEquality(a
[i
], b
[i
]);
45 void ExpectEquality(const ExplodedHttpBodyElement
& a
,
46 const ExplodedHttpBodyElement
& b
) {
47 EXPECT_EQ(a
.type
, b
.type
);
48 EXPECT_EQ(a
.data
, b
.data
);
49 EXPECT_EQ(a
.file_path
, b
.file_path
);
50 EXPECT_EQ(a
.filesystem_url
, b
.filesystem_url
);
51 EXPECT_EQ(a
.file_start
, b
.file_start
);
52 EXPECT_EQ(a
.file_length
, b
.file_length
);
53 if (!(isnan(a
.file_modification_time
) && isnan(b
.file_modification_time
)))
54 EXPECT_DOUBLE_EQ(a
.file_modification_time
, b
.file_modification_time
);
55 EXPECT_EQ(a
.deprecated_blob_url
, b
.deprecated_blob_url
);
59 void ExpectEquality(const ExplodedHttpBody
& a
, const ExplodedHttpBody
& b
) {
60 EXPECT_EQ(a
.http_content_type
, b
.http_content_type
);
61 EXPECT_EQ(a
.identifier
, b
.identifier
);
62 EXPECT_EQ(a
.contains_passwords
, b
.contains_passwords
);
63 EXPECT_EQ(a
.is_null
, b
.is_null
);
64 ExpectEquality(a
.elements
, b
.elements
);
68 void ExpectEquality(const ExplodedFrameState
& a
, const ExplodedFrameState
& b
) {
69 EXPECT_EQ(a
.url_string
, b
.url_string
);
70 EXPECT_EQ(a
.original_url_string
, b
.original_url_string
);
71 EXPECT_EQ(a
.referrer
, b
.referrer
);
72 EXPECT_EQ(a
.target
, b
.target
);
73 EXPECT_EQ(a
.parent
, b
.parent
);
74 EXPECT_EQ(a
.title
, b
.title
);
75 EXPECT_EQ(a
.alternate_title
, b
.alternate_title
);
76 EXPECT_EQ(a
.state_object
, b
.state_object
);
77 ExpectEquality(a
.document_state
, b
.document_state
);
78 EXPECT_EQ(a
.scroll_offset
, b
.scroll_offset
);
79 EXPECT_EQ(a
.item_sequence_number
, b
.item_sequence_number
);
80 EXPECT_EQ(a
.document_sequence_number
, b
.document_sequence_number
);
81 EXPECT_EQ(a
.visit_count
, b
.visit_count
);
82 EXPECT_EQ(a
.visited_time
, b
.visited_time
);
83 EXPECT_EQ(a
.page_scale_factor
, b
.page_scale_factor
);
84 EXPECT_EQ(a
.is_target_item
, b
.is_target_item
);
85 ExpectEquality(a
.http_body
, b
.http_body
);
86 ExpectEquality(a
.children
, b
.children
);
89 void ExpectEquality(const ExplodedPageState
& a
, const ExplodedPageState
& b
) {
90 ExpectEquality(a
.referenced_files
, b
.referenced_files
);
91 ExpectEquality(a
.top
, b
.top
);
94 //-----------------------------------------------------------------------------
96 class PageStateSerializationTest
: public testing::Test
{
98 void PopulateFrameState(ExplodedFrameState
* frame_state
) {
99 // Invent some data for the various fields.
100 frame_state
->url_string
= NS16("http://dev.chromium.org/");
101 frame_state
->original_url_string
= frame_state
->url_string
;
102 frame_state
->referrer
= NS16("https://www.google.com/search?q=dev.chromium.org");
103 frame_state
->target
= NS16("foo");
104 frame_state
->parent
= NS16("bar");
105 frame_state
->title
= NS16("The Chromium Projects");
106 frame_state
->alternate_title
= NS16(NULL
);
107 frame_state
->state_object
= NS16(NULL
);
108 frame_state
->document_state
.push_back(NS16("1"));
109 frame_state
->document_state
.push_back(NS16("q"));
110 frame_state
->document_state
.push_back(NS16("text"));
111 frame_state
->document_state
.push_back(NS16("dev.chromium.org"));
112 frame_state
->scroll_offset
= gfx::Point(0, 100);
113 frame_state
->item_sequence_number
= 1;
114 frame_state
->document_sequence_number
= 2;
115 frame_state
->visit_count
= 10;
116 frame_state
->visited_time
= 12345.0;
117 frame_state
->page_scale_factor
= 2.0;
118 frame_state
->is_target_item
= true;
121 void PopulateHttpBody(ExplodedHttpBody
* http_body
,
122 std::vector
<base::NullableString16
>* referenced_files
) {
123 http_body
->is_null
= false;
124 http_body
->identifier
= 12345;
125 http_body
->contains_passwords
= false;
126 http_body
->http_content_type
= NS16("text/foo");
128 ExplodedHttpBodyElement e1
;
129 e1
.type
= WebKit::WebHTTPBody::Element::TypeData
;
131 http_body
->elements
.push_back(e1
);
133 ExplodedHttpBodyElement e2
;
134 e2
.type
= WebKit::WebHTTPBody::Element::TypeFile
;
135 e2
.file_path
= NS16("file.txt");
137 e2
.file_length
= 1024;
138 e2
.file_modification_time
= 9999.0;
139 http_body
->elements
.push_back(e2
);
141 referenced_files
->push_back(e2
.file_path
);
144 void PopulateFrameStateForBackwardsCompatTest(
145 ExplodedFrameState
* frame_state
,
147 frame_state
->url_string
= NS16("http://chromium.org/");
148 frame_state
->original_url_string
= frame_state
->url_string
;
149 frame_state
->referrer
= NS16("http://google.com/");
151 frame_state
->target
= NS16("target");
152 frame_state
->parent
= NS16("parent");
153 frame_state
->title
= NS16("title");
154 frame_state
->alternate_title
= NS16("alternateTitle");
155 frame_state
->scroll_offset
= gfx::Point(42, -42);
156 frame_state
->item_sequence_number
= 123;
157 frame_state
->document_sequence_number
= 456;
158 frame_state
->visit_count
= 42*42;
159 frame_state
->visited_time
= 13.37;
160 frame_state
->page_scale_factor
= 2.0f
;
161 frame_state
->is_target_item
= true;
163 frame_state
->document_state
.push_back(
164 NS16("\n\r?% WebKit serialized form state version 8 \n\r=&"));
165 frame_state
->document_state
.push_back(NS16("form key"));
166 frame_state
->document_state
.push_back(NS16("1"));
167 frame_state
->document_state
.push_back(NS16("foo"));
168 frame_state
->document_state
.push_back(NS16("file"));
169 frame_state
->document_state
.push_back(NS16("2"));
170 frame_state
->document_state
.push_back(NS16("file.txt"));
171 frame_state
->document_state
.push_back(NS16("displayName"));
174 frame_state
->http_body
.http_content_type
= NS16("foo/bar");
175 frame_state
->http_body
.identifier
= 789;
176 frame_state
->http_body
.is_null
= false;
178 ExplodedHttpBodyElement e1
;
179 e1
.type
= WebKit::WebHTTPBody::Element::TypeData
;
180 e1
.data
= "first data block";
181 frame_state
->http_body
.elements
.push_back(e1
);
183 ExplodedHttpBodyElement e2
;
184 e2
.type
= WebKit::WebHTTPBody::Element::TypeFile
;
185 e2
.file_path
= NS16("file.txt");
186 frame_state
->http_body
.elements
.push_back(e2
);
188 ExplodedHttpBodyElement e3
;
189 e3
.type
= WebKit::WebHTTPBody::Element::TypeData
;
190 e3
.data
= "data the second";
191 frame_state
->http_body
.elements
.push_back(e3
);
193 ExplodedFrameState child_state
;
194 PopulateFrameStateForBackwardsCompatTest(&child_state
, true);
195 frame_state
->children
.push_back(child_state
);
199 void PopulatePageStateForBackwardsCompatTest(ExplodedPageState
* page_state
) {
200 page_state
->referenced_files
.push_back(NS16("file.txt"));
201 PopulateFrameStateForBackwardsCompatTest(&page_state
->top
, false);
204 void TestBackwardsCompat(int version
) {
205 const char* suffix
= "";
207 #if defined(OS_ANDROID)
208 // Unfortunately, the format of version 11 is different on Android, so we
209 // need to use a special reference file.
215 PathService::Get(content::DIR_TEST_DATA
, &path
);
216 path
= path
.AppendASCII("page_state").AppendASCII(
217 base::StringPrintf("serialized_v%d%s.dat", version
, suffix
));
219 std::string file_contents
;
220 if (!base::ReadFileToString(path
, &file_contents
)) {
221 ADD_FAILURE() << "File not found: " << path
.value();
225 std::string trimmed_contents
;
226 EXPECT_TRUE(RemoveChars(file_contents
, "\r\n", &trimmed_contents
));
229 EXPECT_TRUE(base::Base64Decode(trimmed_contents
, &encoded
));
231 ExplodedPageState output
;
232 #if defined(OS_ANDROID)
233 // Because version 11 of the file format unfortunately bakes in the device
234 // scale factor on Android, perform this test by assuming a preset device
235 // scale factor, ignoring the device scale factor of the current device.
236 const float kPresetDeviceScaleFactor
= 2.0f
;
237 EXPECT_TRUE(DecodePageStateWithDeviceScaleFactorForTesting(
239 kPresetDeviceScaleFactor
,
242 EXPECT_TRUE(DecodePageState(encoded
, &output
));
245 ExplodedPageState expected
;
246 PopulatePageStateForBackwardsCompatTest(&expected
);
248 ExpectEquality(expected
, output
);
252 TEST_F(PageStateSerializationTest
, BasicEmpty
) {
253 ExplodedPageState input
;
256 EXPECT_TRUE(EncodePageState(input
, &encoded
));
258 ExplodedPageState output
;
259 EXPECT_TRUE(DecodePageState(encoded
, &output
));
261 ExpectEquality(input
, output
);
264 TEST_F(PageStateSerializationTest
, BasicFrame
) {
265 ExplodedPageState input
;
266 PopulateFrameState(&input
.top
);
269 EXPECT_TRUE(EncodePageState(input
, &encoded
));
271 ExplodedPageState output
;
272 EXPECT_TRUE(DecodePageState(encoded
, &output
));
274 ExpectEquality(input
, output
);
277 TEST_F(PageStateSerializationTest
, BasicFramePOST
) {
278 ExplodedPageState input
;
279 PopulateFrameState(&input
.top
);
280 PopulateHttpBody(&input
.top
.http_body
, &input
.referenced_files
);
283 EXPECT_TRUE(EncodePageState(input
, &encoded
));
285 ExplodedPageState output
;
286 EXPECT_TRUE(DecodePageState(encoded
, &output
));
288 ExpectEquality(input
, output
);
291 TEST_F(PageStateSerializationTest
, BasicFrameSet
) {
292 ExplodedPageState input
;
293 PopulateFrameState(&input
.top
);
295 // Add some child frames.
296 for (int i
= 0; i
< 4; ++i
) {
297 ExplodedFrameState child_state
;
298 PopulateFrameState(&child_state
);
299 input
.top
.children
.push_back(child_state
);
303 EXPECT_TRUE(EncodePageState(input
, &encoded
));
305 ExplodedPageState output
;
306 EXPECT_TRUE(DecodePageState(encoded
, &output
));
308 ExpectEquality(input
, output
);
311 TEST_F(PageStateSerializationTest
, BasicFrameSetPOST
) {
312 ExplodedPageState input
;
313 PopulateFrameState(&input
.top
);
315 // Add some child frames.
316 for (int i
= 0; i
< 4; ++i
) {
317 ExplodedFrameState child_state
;
318 PopulateFrameState(&child_state
);
320 // Simulate a form POST on a subframe.
322 PopulateHttpBody(&child_state
.http_body
, &input
.referenced_files
);
324 input
.top
.children
.push_back(child_state
);
328 EncodePageState(input
, &encoded
);
330 ExplodedPageState output
;
331 DecodePageState(encoded
, &output
);
333 ExpectEquality(input
, output
);
336 TEST_F(PageStateSerializationTest
, BadMessagesTest1
) {
341 for (int i
= 0; i
< 6; ++i
)
346 std::string
s(static_cast<const char*>(p
.data()), p
.size());
348 ExplodedPageState output
;
349 EXPECT_FALSE(DecodePageState(s
, &output
));
352 TEST_F(PageStateSerializationTest
, BadMessagesTest2
) {
358 for (int i
= 0; i
< 6; ++i
)
361 p
.WriteData(reinterpret_cast<const char*>(&d
), sizeof(d
));
370 p
.WriteInt(WebKit::WebHTTPBody::Element::TypeData
);
372 std::string
s(static_cast<const char*>(p
.data()), p
.size());
374 ExplodedPageState output
;
375 EXPECT_FALSE(DecodePageState(s
, &output
));
378 TEST_F(PageStateSerializationTest
, DumpExpectedPageStateForBackwardsCompat
) {
379 // Comment out this return statement to enable this code. Use this code to
380 // generate data, based on the current serialization format, for the
381 // BackwardsCompat_vXX tests.
384 ExplodedPageState state
;
385 PopulatePageStateForBackwardsCompatTest(&state
);
388 EXPECT_TRUE(EncodePageState(state
, &encoded
));
391 EXPECT_TRUE(base::Base64Encode(encoded
, &base64
));
394 PathService::Get(base::DIR_TEMP
, &path
);
395 path
= path
.AppendASCII("expected.dat");
397 FILE* fp
= file_util::OpenFile(path
, "wb");
400 const size_t kRowSize
= 76;
401 for (size_t offset
= 0; offset
< base64
.size(); offset
+= kRowSize
) {
402 size_t length
= std::min(base64
.size() - offset
, kRowSize
);
403 std::string
segment(&base64
[offset
], length
);
404 segment
.push_back('\n');
405 fwrite(segment
.data(), segment
.size(), 1, fp
);
411 #if !defined(OS_ANDROID)
412 // TODO(darin): Re-enable for Android once this test accounts for systems with
413 // a device scale factor not equal to 2.
414 TEST_F(PageStateSerializationTest
, BackwardsCompat_v11
) {
415 TestBackwardsCompat(11);
419 TEST_F(PageStateSerializationTest
, BackwardsCompat_v12
) {
420 TestBackwardsCompat(12);
423 TEST_F(PageStateSerializationTest
, BackwardsCompat_v13
) {
424 TestBackwardsCompat(13);
427 TEST_F(PageStateSerializationTest
, BackwardsCompat_v14
) {
428 TestBackwardsCompat(14);
432 } // namespace content