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