Home | History | Annotate | Download | only in common
      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/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 
     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.org");
    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 #if defined(OS_ANDROID)
    232     // Because version 11 of the file format unfortunately bakes in the device
    233     // scale factor on Android, perform this test by assuming a preset device
    234     // scale factor, ignoring the device scale factor of the current device.
    235     const float kPresetDeviceScaleFactor = 2.0f;
    236     EXPECT_TRUE(DecodePageStateWithDeviceScaleFactorForTesting(
    237         encoded,
    238         kPresetDeviceScaleFactor,
    239         &output));
    240 #else
    241     EXPECT_TRUE(DecodePageState(encoded, &output));
    242 #endif
    243 
    244     ExplodedPageState expected;
    245     PopulatePageStateForBackwardsCompatTest(&expected);
    246 
    247     ExpectEquality(expected, output);
    248   }
    249 };
    250 
    251 TEST_F(PageStateSerializationTest, BasicEmpty) {
    252   ExplodedPageState input;
    253 
    254   std::string encoded;
    255   EXPECT_TRUE(EncodePageState(input, &encoded));
    256 
    257   ExplodedPageState output;
    258   EXPECT_TRUE(DecodePageState(encoded, &output));
    259 
    260   ExpectEquality(input, output);
    261 }
    262 
    263 TEST_F(PageStateSerializationTest, BasicFrame) {
    264   ExplodedPageState input;
    265   PopulateFrameState(&input.top);
    266 
    267   std::string encoded;
    268   EXPECT_TRUE(EncodePageState(input, &encoded));
    269 
    270   ExplodedPageState output;
    271   EXPECT_TRUE(DecodePageState(encoded, &output));
    272 
    273   ExpectEquality(input, output);
    274 }
    275 
    276 TEST_F(PageStateSerializationTest, BasicFramePOST) {
    277   ExplodedPageState input;
    278   PopulateFrameState(&input.top);
    279   PopulateHttpBody(&input.top.http_body, &input.referenced_files);
    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, BasicFrameSet) {
    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     input.top.children.push_back(child_state);
    299   }
    300 
    301   std::string encoded;
    302   EXPECT_TRUE(EncodePageState(input, &encoded));
    303 
    304   ExplodedPageState output;
    305   EXPECT_TRUE(DecodePageState(encoded, &output));
    306 
    307   ExpectEquality(input, output);
    308 }
    309 
    310 TEST_F(PageStateSerializationTest, BasicFrameSetPOST) {
    311   ExplodedPageState input;
    312   PopulateFrameState(&input.top);
    313 
    314   // Add some child frames.
    315   for (int i = 0; i < 4; ++i) {
    316     ExplodedFrameState child_state;
    317     PopulateFrameState(&child_state);
    318 
    319     // Simulate a form POST on a subframe.
    320     if (i == 2)
    321       PopulateHttpBody(&child_state.http_body, &input.referenced_files);
    322 
    323     input.top.children.push_back(child_state);
    324   }
    325 
    326   std::string encoded;
    327   EncodePageState(input, &encoded);
    328 
    329   ExplodedPageState output;
    330   DecodePageState(encoded, &output);
    331 
    332   ExpectEquality(input, output);
    333 }
    334 
    335 TEST_F(PageStateSerializationTest, BadMessagesTest1) {
    336   Pickle p;
    337   // Version 14
    338   p.WriteInt(14);
    339   // Empty strings.
    340   for (int i = 0; i < 6; ++i)
    341     p.WriteInt(-1);
    342   // Bad real number.
    343   p.WriteInt(-1);
    344 
    345   std::string s(static_cast<const char*>(p.data()), p.size());
    346 
    347   ExplodedPageState output;
    348   EXPECT_FALSE(DecodePageState(s, &output));
    349 }
    350 
    351 TEST_F(PageStateSerializationTest, BadMessagesTest2) {
    352   double d = 0;
    353   Pickle p;
    354   // Version 14
    355   p.WriteInt(14);
    356   // Empty strings.
    357   for (int i = 0; i < 6; ++i)
    358     p.WriteInt(-1);
    359   // More misc fields.
    360   p.WriteData(reinterpret_cast<const char*>(&d), sizeof(d));
    361   p.WriteInt(1);
    362   p.WriteInt(1);
    363   p.WriteInt(0);
    364   p.WriteInt(0);
    365   p.WriteInt(-1);
    366   p.WriteInt(0);
    367   // WebForm
    368   p.WriteInt(1);
    369   p.WriteInt(WebKit::WebHTTPBody::Element::TypeData);
    370 
    371   std::string s(static_cast<const char*>(p.data()), p.size());
    372 
    373   ExplodedPageState output;
    374   EXPECT_FALSE(DecodePageState(s, &output));
    375 }
    376 
    377 TEST_F(PageStateSerializationTest, DumpExpectedPageStateForBackwardsCompat) {
    378   // Comment out this return statement to enable this code.  Use this code to
    379   // generate data, based on the current serialization format, for the
    380   // BackwardsCompat_vXX tests.
    381   return;
    382 
    383   ExplodedPageState state;
    384   PopulatePageStateForBackwardsCompatTest(&state);
    385 
    386   std::string encoded;
    387   EXPECT_TRUE(EncodePageState(state, &encoded));
    388 
    389   std::string base64;
    390   EXPECT_TRUE(base::Base64Encode(encoded, &base64));
    391 
    392   base::FilePath path;
    393   PathService::Get(base::DIR_TEMP, &path);
    394   path = path.AppendASCII("expected.dat");
    395 
    396   FILE* fp = file_util::OpenFile(path, "wb");
    397   ASSERT_TRUE(fp);
    398 
    399   const size_t kRowSize = 76;
    400   for (size_t offset = 0; offset < base64.size(); offset += kRowSize) {
    401     size_t length = std::min(base64.size() - offset, kRowSize);
    402     std::string segment(&base64[offset], length);
    403     segment.push_back('\n');
    404     fwrite(segment.data(), segment.size(), 1, fp);
    405   }
    406 
    407   fclose(fp);
    408 }
    409 
    410 #if !defined(OS_ANDROID)
    411 // TODO(darin): Re-enable for Android once this test accounts for systems with
    412 //              a device scale factor not equal to 2.
    413 TEST_F(PageStateSerializationTest, BackwardsCompat_v11) {
    414   TestBackwardsCompat(11);
    415 }
    416 #endif
    417 
    418 TEST_F(PageStateSerializationTest, BackwardsCompat_v12) {
    419   TestBackwardsCompat(12);
    420 }
    421 
    422 TEST_F(PageStateSerializationTest, BackwardsCompat_v13) {
    423   TestBackwardsCompat(13);
    424 }
    425 
    426 TEST_F(PageStateSerializationTest, BackwardsCompat_v14) {
    427   TestBackwardsCompat(14);
    428 }
    429 
    430 }  // namespace
    431 }  // namespace content
    432