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 <math.h> 6 7#include "base/base64.h" 8#include "base/files/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" 17 18namespace content { 19namespace { 20 21#if defined(OS_WIN) 22inline bool isnan(double num) { return !!_isnan(num); } 23#endif 24 25base::NullableString16 NS16(const char* s) { 26 return s ? base::NullableString16(base::ASCIIToUTF16(s), false) : 27 base::NullableString16(); 28} 29 30//----------------------------------------------------------------------------- 31 32template <typename T> 33void ExpectEquality(const T& a, const T& b) { 34 EXPECT_EQ(a, b); 35} 36 37template <typename T> 38void 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]); 42} 43 44template <> 45void 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.blob_uuid, b.blob_uuid); 56} 57 58template <> 59void 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); 65} 66 67template <> 68void ExpectEquality(const ExplodedFrameState& a, const ExplodedFrameState& b) { 69 EXPECT_EQ(a.url_string, b.url_string); 70 EXPECT_EQ(a.referrer, b.referrer); 71 EXPECT_EQ(a.referrer_policy, b.referrer_policy); 72 EXPECT_EQ(a.target, b.target); 73 EXPECT_EQ(a.state_object, b.state_object); 74 ExpectEquality(a.document_state, b.document_state); 75 EXPECT_EQ(a.pinch_viewport_scroll_offset, b.pinch_viewport_scroll_offset); 76 EXPECT_EQ(a.scroll_offset, b.scroll_offset); 77 EXPECT_EQ(a.item_sequence_number, b.item_sequence_number); 78 EXPECT_EQ(a.document_sequence_number, b.document_sequence_number); 79 EXPECT_EQ(a.page_scale_factor, b.page_scale_factor); 80 ExpectEquality(a.http_body, b.http_body); 81 ExpectEquality(a.children, b.children); 82} 83 84void ExpectEquality(const ExplodedPageState& a, const ExplodedPageState& b) { 85 ExpectEquality(a.referenced_files, b.referenced_files); 86 ExpectEquality(a.top, b.top); 87} 88 89//----------------------------------------------------------------------------- 90 91class PageStateSerializationTest : public testing::Test { 92 public: 93 void PopulateFrameState(ExplodedFrameState* frame_state) { 94 // Invent some data for the various fields. 95 frame_state->url_string = NS16("http://dev.chromium.org/"); 96 frame_state->referrer = NS16("https://www.google.com/search?q=dev.chromium.org"); 97 frame_state->referrer_policy = blink::WebReferrerPolicyAlways; 98 frame_state->target = NS16("foo"); 99 frame_state->state_object = NS16(NULL); 100 frame_state->document_state.push_back(NS16("1")); 101 frame_state->document_state.push_back(NS16("q")); 102 frame_state->document_state.push_back(NS16("text")); 103 frame_state->document_state.push_back(NS16("dev.chromium.org")); 104 frame_state->pinch_viewport_scroll_offset = gfx::PointF(10, 15); 105 frame_state->scroll_offset = gfx::Point(0, 100); 106 frame_state->item_sequence_number = 1; 107 frame_state->document_sequence_number = 2; 108 frame_state->frame_sequence_number = 3; 109 frame_state->page_scale_factor = 2.0; 110 } 111 112 void PopulateHttpBody(ExplodedHttpBody* http_body, 113 std::vector<base::NullableString16>* referenced_files) { 114 http_body->is_null = false; 115 http_body->identifier = 12345; 116 http_body->contains_passwords = false; 117 http_body->http_content_type = NS16("text/foo"); 118 119 ExplodedHttpBodyElement e1; 120 e1.type = blink::WebHTTPBody::Element::TypeData; 121 e1.data = "foo"; 122 http_body->elements.push_back(e1); 123 124 ExplodedHttpBodyElement e2; 125 e2.type = blink::WebHTTPBody::Element::TypeFile; 126 e2.file_path = NS16("file.txt"); 127 e2.file_start = 100; 128 e2.file_length = 1024; 129 e2.file_modification_time = 9999.0; 130 http_body->elements.push_back(e2); 131 132 referenced_files->push_back(e2.file_path); 133 } 134 135 void PopulateFrameStateForBackwardsCompatTest( 136 ExplodedFrameState* frame_state, 137 bool is_child) { 138 frame_state->url_string = NS16("http://chromium.org/"); 139 frame_state->referrer = NS16("http://google.com/"); 140 frame_state->referrer_policy = blink::WebReferrerPolicyDefault; 141 if (!is_child) 142 frame_state->target = NS16("target"); 143 frame_state->pinch_viewport_scroll_offset = gfx::PointF(-1, -1); 144 frame_state->scroll_offset = gfx::Point(42, -42); 145 frame_state->item_sequence_number = 123; 146 frame_state->document_sequence_number = 456; 147 frame_state->frame_sequence_number = 789; 148 frame_state->page_scale_factor = 2.0f; 149 150 frame_state->document_state.push_back( 151 NS16("\n\r?% WebKit serialized form state version 8 \n\r=&")); 152 frame_state->document_state.push_back(NS16("form key")); 153 frame_state->document_state.push_back(NS16("1")); 154 frame_state->document_state.push_back(NS16("foo")); 155 frame_state->document_state.push_back(NS16("file")); 156 frame_state->document_state.push_back(NS16("2")); 157 frame_state->document_state.push_back(NS16("file.txt")); 158 frame_state->document_state.push_back(NS16("displayName")); 159 160 if (!is_child) { 161 frame_state->http_body.http_content_type = NS16("foo/bar"); 162 frame_state->http_body.identifier = 789; 163 frame_state->http_body.is_null = false; 164 165 ExplodedHttpBodyElement e1; 166 e1.type = blink::WebHTTPBody::Element::TypeData; 167 e1.data = "first data block"; 168 frame_state->http_body.elements.push_back(e1); 169 170 ExplodedHttpBodyElement e2; 171 e2.type = blink::WebHTTPBody::Element::TypeFile; 172 e2.file_path = NS16("file.txt"); 173 frame_state->http_body.elements.push_back(e2); 174 175 ExplodedHttpBodyElement e3; 176 e3.type = blink::WebHTTPBody::Element::TypeData; 177 e3.data = "data the second"; 178 frame_state->http_body.elements.push_back(e3); 179 180 ExplodedFrameState child_state; 181 PopulateFrameStateForBackwardsCompatTest(&child_state, true); 182 frame_state->children.push_back(child_state); 183 } 184 } 185 186 void PopulatePageStateForBackwardsCompatTest(ExplodedPageState* page_state) { 187 page_state->referenced_files.push_back(NS16("file.txt")); 188 PopulateFrameStateForBackwardsCompatTest(&page_state->top, false); 189 } 190 191 void TestBackwardsCompat(int version) { 192 const char* suffix = ""; 193 194#if defined(OS_ANDROID) 195 // Unfortunately, the format of version 11 is different on Android, so we 196 // need to use a special reference file. 197 if (version == 11) 198 suffix = "_android"; 199#endif 200 201 base::FilePath path; 202 PathService::Get(content::DIR_TEST_DATA, &path); 203 path = path.AppendASCII("page_state").AppendASCII( 204 base::StringPrintf("serialized_v%d%s.dat", version, suffix)); 205 206 std::string file_contents; 207 if (!base::ReadFileToString(path, &file_contents)) { 208 ADD_FAILURE() << "File not found: " << path.value(); 209 return; 210 } 211 212 std::string trimmed_contents; 213 EXPECT_TRUE(base::RemoveChars(file_contents, "\r\n", &trimmed_contents)); 214 215 std::string encoded; 216 EXPECT_TRUE(base::Base64Decode(trimmed_contents, &encoded)); 217 218 ExplodedPageState output; 219#if defined(OS_ANDROID) 220 // Because version 11 of the file format unfortunately bakes in the device 221 // scale factor on Android, perform this test by assuming a preset device 222 // scale factor, ignoring the device scale factor of the current device. 223 const float kPresetDeviceScaleFactor = 2.0f; 224 EXPECT_TRUE(DecodePageStateWithDeviceScaleFactorForTesting( 225 encoded, 226 kPresetDeviceScaleFactor, 227 &output)); 228#else 229 EXPECT_TRUE(DecodePageState(encoded, &output)); 230#endif 231 232 ExplodedPageState expected; 233 PopulatePageStateForBackwardsCompatTest(&expected); 234 235 ExpectEquality(expected, output); 236 } 237}; 238 239TEST_F(PageStateSerializationTest, BasicEmpty) { 240 ExplodedPageState input; 241 242 std::string encoded; 243 EXPECT_TRUE(EncodePageState(input, &encoded)); 244 245 ExplodedPageState output; 246 EXPECT_TRUE(DecodePageState(encoded, &output)); 247 248 ExpectEquality(input, output); 249} 250 251TEST_F(PageStateSerializationTest, BasicFrame) { 252 ExplodedPageState input; 253 PopulateFrameState(&input.top); 254 255 std::string encoded; 256 EXPECT_TRUE(EncodePageState(input, &encoded)); 257 258 ExplodedPageState output; 259 EXPECT_TRUE(DecodePageState(encoded, &output)); 260 261 ExpectEquality(input, output); 262} 263 264TEST_F(PageStateSerializationTest, BasicFramePOST) { 265 ExplodedPageState input; 266 PopulateFrameState(&input.top); 267 PopulateHttpBody(&input.top.http_body, &input.referenced_files); 268 269 std::string encoded; 270 EXPECT_TRUE(EncodePageState(input, &encoded)); 271 272 ExplodedPageState output; 273 EXPECT_TRUE(DecodePageState(encoded, &output)); 274 275 ExpectEquality(input, output); 276} 277 278TEST_F(PageStateSerializationTest, BasicFrameSet) { 279 ExplodedPageState input; 280 PopulateFrameState(&input.top); 281 282 // Add some child frames. 283 for (int i = 0; i < 4; ++i) { 284 ExplodedFrameState child_state; 285 PopulateFrameState(&child_state); 286 input.top.children.push_back(child_state); 287 } 288 289 std::string encoded; 290 EXPECT_TRUE(EncodePageState(input, &encoded)); 291 292 ExplodedPageState output; 293 EXPECT_TRUE(DecodePageState(encoded, &output)); 294 295 ExpectEquality(input, output); 296} 297 298TEST_F(PageStateSerializationTest, BasicFrameSetPOST) { 299 ExplodedPageState input; 300 PopulateFrameState(&input.top); 301 302 // Add some child frames. 303 for (int i = 0; i < 4; ++i) { 304 ExplodedFrameState child_state; 305 PopulateFrameState(&child_state); 306 307 // Simulate a form POST on a subframe. 308 if (i == 2) 309 PopulateHttpBody(&child_state.http_body, &input.referenced_files); 310 311 input.top.children.push_back(child_state); 312 } 313 314 std::string encoded; 315 EncodePageState(input, &encoded); 316 317 ExplodedPageState output; 318 DecodePageState(encoded, &output); 319 320 ExpectEquality(input, output); 321} 322 323TEST_F(PageStateSerializationTest, BadMessagesTest1) { 324 Pickle p; 325 // Version 14 326 p.WriteInt(14); 327 // Empty strings. 328 for (int i = 0; i < 6; ++i) 329 p.WriteInt(-1); 330 // Bad real number. 331 p.WriteInt(-1); 332 333 std::string s(static_cast<const char*>(p.data()), p.size()); 334 335 ExplodedPageState output; 336 EXPECT_FALSE(DecodePageState(s, &output)); 337} 338 339TEST_F(PageStateSerializationTest, BadMessagesTest2) { 340 double d = 0; 341 Pickle p; 342 // Version 14 343 p.WriteInt(14); 344 // Empty strings. 345 for (int i = 0; i < 6; ++i) 346 p.WriteInt(-1); 347 // More misc fields. 348 p.WriteData(reinterpret_cast<const char*>(&d), sizeof(d)); 349 p.WriteInt(1); 350 p.WriteInt(1); 351 p.WriteInt(0); 352 p.WriteInt(0); 353 p.WriteInt(-1); 354 p.WriteInt(0); 355 // WebForm 356 p.WriteInt(1); 357 p.WriteInt(blink::WebHTTPBody::Element::TypeData); 358 359 std::string s(static_cast<const char*>(p.data()), p.size()); 360 361 ExplodedPageState output; 362 EXPECT_FALSE(DecodePageState(s, &output)); 363} 364 365TEST_F(PageStateSerializationTest, DumpExpectedPageStateForBackwardsCompat) { 366 // Change to #if 1 to enable this code. Use this code to generate data, based 367 // on the current serialization format, for the BackwardsCompat_vXX tests. 368#if 0 369 ExplodedPageState state; 370 PopulatePageStateForBackwardsCompatTest(&state); 371 372 std::string encoded; 373 EXPECT_TRUE(EncodePageState(state, &encoded)); 374 375 std::string base64; 376 base::Base64Encode(encoded, &base64); 377 378 base::FilePath path; 379 PathService::Get(base::DIR_TEMP, &path); 380 path = path.AppendASCII("expected.dat"); 381 382 FILE* fp = base::OpenFile(path, "wb"); 383 ASSERT_TRUE(fp); 384 385 const size_t kRowSize = 76; 386 for (size_t offset = 0; offset < base64.size(); offset += kRowSize) { 387 size_t length = std::min(base64.size() - offset, kRowSize); 388 std::string segment(&base64[offset], length); 389 segment.push_back('\n'); 390 ASSERT_EQ(1U, fwrite(segment.data(), segment.size(), 1, fp)); 391 } 392 393 fclose(fp); 394#endif 395} 396 397#if !defined(OS_ANDROID) 398// TODO(darin): Re-enable for Android once this test accounts for systems with 399// a device scale factor not equal to 2. 400TEST_F(PageStateSerializationTest, BackwardsCompat_v11) { 401 TestBackwardsCompat(11); 402} 403#endif 404 405TEST_F(PageStateSerializationTest, BackwardsCompat_v12) { 406 TestBackwardsCompat(12); 407} 408 409TEST_F(PageStateSerializationTest, BackwardsCompat_v13) { 410 TestBackwardsCompat(13); 411} 412 413TEST_F(PageStateSerializationTest, BackwardsCompat_v14) { 414 TestBackwardsCompat(14); 415} 416 417TEST_F(PageStateSerializationTest, BackwardsCompat_v15) { 418 TestBackwardsCompat(15); 419} 420 421TEST_F(PageStateSerializationTest, BackwardsCompat_v16) { 422 TestBackwardsCompat(16); 423} 424 425TEST_F(PageStateSerializationTest, BackwardsCompat_v18) { 426 TestBackwardsCompat(18); 427} 428 429TEST_F(PageStateSerializationTest, BackwardsCompat_v20) { 430 TestBackwardsCompat(20); 431} 432 433} // namespace 434} // namespace content 435