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.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 
     58 template <>
     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);
     65 }
     66 
     67 template <>
     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.state_object, b.state_object);
     74   ExpectEquality(a.document_state, b.document_state);
     75   EXPECT_EQ(a.scroll_offset, b.scroll_offset);
     76   EXPECT_EQ(a.item_sequence_number, b.item_sequence_number);
     77   EXPECT_EQ(a.document_sequence_number, b.document_sequence_number);
     78   EXPECT_EQ(a.target_frame_id, b.target_frame_id);
     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 
     84 void 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 
     91 class 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->original_url_string = frame_state->url_string;
     97     frame_state->referrer = NS16("https://www.google.com/search?q=dev.chromium.org");
     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->scroll_offset = gfx::Point(0, 100);
    105     frame_state->item_sequence_number = 1;
    106     frame_state->document_sequence_number = 2;
    107     frame_state->target_frame_id = 3;
    108     frame_state->page_scale_factor = 2.0;
    109   }
    110 
    111   void PopulateHttpBody(ExplodedHttpBody* http_body,
    112                         std::vector<base::NullableString16>* referenced_files) {
    113     http_body->is_null = false;
    114     http_body->identifier = 12345;
    115     http_body->contains_passwords = false;
    116     http_body->http_content_type = NS16("text/foo");
    117 
    118     ExplodedHttpBodyElement e1;
    119     e1.type = blink::WebHTTPBody::Element::TypeData;
    120     e1.data = "foo";
    121     http_body->elements.push_back(e1);
    122 
    123     ExplodedHttpBodyElement e2;
    124     e2.type = blink::WebHTTPBody::Element::TypeFile;
    125     e2.file_path = NS16("file.txt");
    126     e2.file_start = 100;
    127     e2.file_length = 1024;
    128     e2.file_modification_time = 9999.0;
    129     http_body->elements.push_back(e2);
    130 
    131     referenced_files->push_back(e2.file_path);
    132   }
    133 
    134   void PopulateFrameStateForBackwardsCompatTest(
    135       ExplodedFrameState* frame_state,
    136       bool is_child) {
    137     frame_state->url_string = NS16("http://chromium.org/");
    138     frame_state->original_url_string = frame_state->url_string;
    139     frame_state->referrer = NS16("http://google.com/");
    140     if (!is_child)
    141       frame_state->target = NS16("target");
    142     frame_state->scroll_offset = gfx::Point(42, -42);
    143     frame_state->item_sequence_number = 123;
    144     frame_state->document_sequence_number = 456;
    145     frame_state->target_frame_id = 0;
    146     frame_state->page_scale_factor = 2.0f;
    147 
    148     frame_state->document_state.push_back(
    149         NS16("\n\r?% WebKit serialized form state version 8 \n\r=&"));
    150     frame_state->document_state.push_back(NS16("form key"));
    151     frame_state->document_state.push_back(NS16("1"));
    152     frame_state->document_state.push_back(NS16("foo"));
    153     frame_state->document_state.push_back(NS16("file"));
    154     frame_state->document_state.push_back(NS16("2"));
    155     frame_state->document_state.push_back(NS16("file.txt"));
    156     frame_state->document_state.push_back(NS16("displayName"));
    157 
    158     if (!is_child) {
    159       frame_state->http_body.http_content_type = NS16("foo/bar");
    160       frame_state->http_body.identifier = 789;
    161       frame_state->http_body.is_null = false;
    162 
    163       ExplodedHttpBodyElement e1;
    164       e1.type = blink::WebHTTPBody::Element::TypeData;
    165       e1.data = "first data block";
    166       frame_state->http_body.elements.push_back(e1);
    167 
    168       ExplodedHttpBodyElement e2;
    169       e2.type = blink::WebHTTPBody::Element::TypeFile;
    170       e2.file_path = NS16("file.txt");
    171       frame_state->http_body.elements.push_back(e2);
    172 
    173       ExplodedHttpBodyElement e3;
    174       e3.type = blink::WebHTTPBody::Element::TypeData;
    175       e3.data = "data the second";
    176       frame_state->http_body.elements.push_back(e3);
    177 
    178       ExplodedFrameState child_state;
    179       PopulateFrameStateForBackwardsCompatTest(&child_state, true);
    180       frame_state->children.push_back(child_state);
    181     }
    182   }
    183 
    184   void PopulatePageStateForBackwardsCompatTest(ExplodedPageState* page_state) {
    185     page_state->referenced_files.push_back(NS16("file.txt"));
    186     PopulateFrameStateForBackwardsCompatTest(&page_state->top, false);
    187   }
    188 
    189   void TestBackwardsCompat(int version) {
    190     const char* suffix = "";
    191 
    192 #if defined(OS_ANDROID)
    193     // Unfortunately, the format of version 11 is different on Android, so we
    194     // need to use a special reference file.
    195     if (version == 11)
    196       suffix = "_android";
    197 #endif
    198 
    199     base::FilePath path;
    200     PathService::Get(content::DIR_TEST_DATA, &path);
    201     path = path.AppendASCII("page_state").AppendASCII(
    202         base::StringPrintf("serialized_v%d%s.dat", version, suffix));
    203 
    204     std::string file_contents;
    205     if (!base::ReadFileToString(path, &file_contents)) {
    206       ADD_FAILURE() << "File not found: " << path.value();
    207       return;
    208     }
    209 
    210     std::string trimmed_contents;
    211     EXPECT_TRUE(base::RemoveChars(file_contents, "\r\n", &trimmed_contents));
    212 
    213     std::string encoded;
    214     EXPECT_TRUE(base::Base64Decode(trimmed_contents, &encoded));
    215 
    216     ExplodedPageState output;
    217 #if defined(OS_ANDROID)
    218     // Because version 11 of the file format unfortunately bakes in the device
    219     // scale factor on Android, perform this test by assuming a preset device
    220     // scale factor, ignoring the device scale factor of the current device.
    221     const float kPresetDeviceScaleFactor = 2.0f;
    222     EXPECT_TRUE(DecodePageStateWithDeviceScaleFactorForTesting(
    223         encoded,
    224         kPresetDeviceScaleFactor,
    225         &output));
    226 #else
    227     EXPECT_TRUE(DecodePageState(encoded, &output));
    228 #endif
    229 
    230     ExplodedPageState expected;
    231     PopulatePageStateForBackwardsCompatTest(&expected);
    232 
    233     ExpectEquality(expected, output);
    234   }
    235 };
    236 
    237 TEST_F(PageStateSerializationTest, BasicEmpty) {
    238   ExplodedPageState input;
    239 
    240   std::string encoded;
    241   EXPECT_TRUE(EncodePageState(input, &encoded));
    242 
    243   ExplodedPageState output;
    244   EXPECT_TRUE(DecodePageState(encoded, &output));
    245 
    246   ExpectEquality(input, output);
    247 }
    248 
    249 TEST_F(PageStateSerializationTest, BasicFrame) {
    250   ExplodedPageState input;
    251   PopulateFrameState(&input.top);
    252 
    253   std::string encoded;
    254   EXPECT_TRUE(EncodePageState(input, &encoded));
    255 
    256   ExplodedPageState output;
    257   EXPECT_TRUE(DecodePageState(encoded, &output));
    258 
    259   ExpectEquality(input, output);
    260 }
    261 
    262 TEST_F(PageStateSerializationTest, BasicFramePOST) {
    263   ExplodedPageState input;
    264   PopulateFrameState(&input.top);
    265   PopulateHttpBody(&input.top.http_body, &input.referenced_files);
    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, BasicFrameSet) {
    277   ExplodedPageState input;
    278   PopulateFrameState(&input.top);
    279 
    280   // Add some child frames.
    281   for (int i = 0; i < 4; ++i) {
    282     ExplodedFrameState child_state;
    283     PopulateFrameState(&child_state);
    284     input.top.children.push_back(child_state);
    285   }
    286 
    287   std::string encoded;
    288   EXPECT_TRUE(EncodePageState(input, &encoded));
    289 
    290   ExplodedPageState output;
    291   EXPECT_TRUE(DecodePageState(encoded, &output));
    292 
    293   ExpectEquality(input, output);
    294 }
    295 
    296 TEST_F(PageStateSerializationTest, BasicFrameSetPOST) {
    297   ExplodedPageState input;
    298   PopulateFrameState(&input.top);
    299 
    300   // Add some child frames.
    301   for (int i = 0; i < 4; ++i) {
    302     ExplodedFrameState child_state;
    303     PopulateFrameState(&child_state);
    304 
    305     // Simulate a form POST on a subframe.
    306     if (i == 2)
    307       PopulateHttpBody(&child_state.http_body, &input.referenced_files);
    308 
    309     input.top.children.push_back(child_state);
    310   }
    311 
    312   std::string encoded;
    313   EncodePageState(input, &encoded);
    314 
    315   ExplodedPageState output;
    316   DecodePageState(encoded, &output);
    317 
    318   ExpectEquality(input, output);
    319 }
    320 
    321 TEST_F(PageStateSerializationTest, BadMessagesTest1) {
    322   Pickle p;
    323   // Version 14
    324   p.WriteInt(14);
    325   // Empty strings.
    326   for (int i = 0; i < 6; ++i)
    327     p.WriteInt(-1);
    328   // Bad real number.
    329   p.WriteInt(-1);
    330 
    331   std::string s(static_cast<const char*>(p.data()), p.size());
    332 
    333   ExplodedPageState output;
    334   EXPECT_FALSE(DecodePageState(s, &output));
    335 }
    336 
    337 TEST_F(PageStateSerializationTest, BadMessagesTest2) {
    338   double d = 0;
    339   Pickle p;
    340   // Version 14
    341   p.WriteInt(14);
    342   // Empty strings.
    343   for (int i = 0; i < 6; ++i)
    344     p.WriteInt(-1);
    345   // More misc fields.
    346   p.WriteData(reinterpret_cast<const char*>(&d), sizeof(d));
    347   p.WriteInt(1);
    348   p.WriteInt(1);
    349   p.WriteInt(0);
    350   p.WriteInt(0);
    351   p.WriteInt(-1);
    352   p.WriteInt(0);
    353   // WebForm
    354   p.WriteInt(1);
    355   p.WriteInt(blink::WebHTTPBody::Element::TypeData);
    356 
    357   std::string s(static_cast<const char*>(p.data()), p.size());
    358 
    359   ExplodedPageState output;
    360   EXPECT_FALSE(DecodePageState(s, &output));
    361 }
    362 
    363 TEST_F(PageStateSerializationTest, DumpExpectedPageStateForBackwardsCompat) {
    364   // Comment out this return statement to enable this code.  Use this code to
    365   // generate data, based on the current serialization format, for the
    366   // BackwardsCompat_vXX tests.
    367   return;
    368 
    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 }
    395 
    396 #if !defined(OS_ANDROID)
    397 // TODO(darin): Re-enable for Android once this test accounts for systems with
    398 //              a device scale factor not equal to 2.
    399 TEST_F(PageStateSerializationTest, BackwardsCompat_v11) {
    400   TestBackwardsCompat(11);
    401 }
    402 #endif
    403 
    404 TEST_F(PageStateSerializationTest, BackwardsCompat_v12) {
    405   TestBackwardsCompat(12);
    406 }
    407 
    408 TEST_F(PageStateSerializationTest, BackwardsCompat_v13) {
    409   TestBackwardsCompat(13);
    410 }
    411 
    412 TEST_F(PageStateSerializationTest, BackwardsCompat_v14) {
    413   TestBackwardsCompat(14);
    414 }
    415 
    416 TEST_F(PageStateSerializationTest, BackwardsCompat_v15) {
    417   TestBackwardsCompat(15);
    418 }
    419 
    420 TEST_F(PageStateSerializationTest, BackwardsCompat_v16) {
    421   TestBackwardsCompat(16);
    422 }
    423 
    424 }  // namespace
    425 }  // namespace content
    426