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