1 // Copyright (c) 2011 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 <atlbase.h> 6 #include <atlcom.h> 7 8 #include "base/file_util.h" 9 #include "base/path_service.h" 10 #include "base/win/scoped_comptr.h" 11 #include "chrome_frame/test/chrome_frame_test_utils.h" 12 #include "chrome_frame/test/urlmon_moniker_tests.h" 13 #include "chrome_frame/urlmon_bind_status_callback.h" 14 15 using chrome_frame_test::ScopedVirtualizeHklmAndHkcu; 16 using testing::Return; 17 using testing::Eq; 18 19 class MonikerPatchTest : public testing::Test { 20 protected: 21 MonikerPatchTest() { 22 } 23 24 virtual void SetUp() { 25 DeleteAllSingletons(); 26 PathService::Get(base::DIR_SOURCE_ROOT, &test_file_path_); 27 test_file_path_ = test_file_path_.Append(FILE_PATH_LITERAL("chrome_frame")) 28 .Append(FILE_PATH_LITERAL("test")) 29 .Append(FILE_PATH_LITERAL("data")); 30 } 31 32 bool ReadFileAsString(const wchar_t* file_name, std::string* file_contents) { 33 EXPECT_TRUE(file_name); 34 base::FilePath file_path = test_file_path_.Append(file_name); 35 return base::ReadFileToString(file_path, file_contents); 36 } 37 38 static bool StringToStream(const std::string& data, IStream** ret) { 39 EXPECT_TRUE(!data.empty()); 40 41 base::win::ScopedComPtr<IStream> stream; 42 HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream.Receive()); 43 EXPECT_HRESULT_SUCCEEDED(hr); 44 if (FAILED(hr)) { 45 return false; 46 } 47 48 DWORD written = 0; 49 hr = stream->Write(data.c_str(), data.size(), &written); 50 EXPECT_HRESULT_SUCCEEDED(hr); 51 EXPECT_EQ(data.size(), written); 52 53 bool result = false; 54 if (SUCCEEDED(hr)) { 55 RewindStream(stream); 56 *ret = stream.Detach(); 57 result = true; 58 } 59 60 return result; 61 } 62 63 base::FilePath test_file_path_; 64 ScopedVirtualizeHklmAndHkcu virtualized_registry_; 65 }; 66 67 // Tests the CacheStream class by writing content into a stream object 68 // and verify that reading that stream back 69 TEST_F(MonikerPatchTest, CacheStream) { 70 const char data[] = "ReadStreamCacheTest"; 71 char ret[2 * sizeof(data)] = {0}; 72 DWORD read = 0; 73 74 // Test 1: empty stream reads nothing 75 CComObjectStackEx<CacheStream> cache_stream1; 76 EXPECT_EQ(S_FALSE, cache_stream1.Read(ret, sizeof(ret), &read)); 77 EXPECT_EQ(0, read); 78 79 // Test 2: Read from initialized cache 80 CComObjectStackEx<CacheStream> cache_stream2; 81 cache_stream2.Initialize(data, sizeof(data), false); 82 EXPECT_HRESULT_SUCCEEDED(cache_stream2.Read(ret, sizeof(ret), &read)); 83 EXPECT_EQ(sizeof(data), read); 84 EXPECT_EQ(std::string(data), std::string(ret)); 85 86 read = 0; 87 EXPECT_EQ(E_PENDING, cache_stream2.Read(ret, sizeof(ret), &read)); 88 EXPECT_EQ(0, read); 89 } 90 91 ACTION_P3(ReadStream, buffer, size, ret) { 92 EXPECT_EQ(TYMED_ISTREAM, arg3->tymed); 93 *ret = arg3->pstm->Read(buffer, *size, size); 94 } 95 96 // Tests the implementation of BSCBFeedData to feed data to the 97 // specified IBindStatusCallback 98 TEST_F(MonikerPatchTest, BSCBFeedData) { 99 CComObjectStackEx<MockBindStatusCallbackImpl> mock; 100 const char data[] = "ReadStreamCacheTest"; 101 const DWORD size = sizeof(data); 102 const CLIPFORMAT cf = 0xd0d0; 103 const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; 104 const DWORD kArbitraryreadSize = 0xdeadbabe; 105 106 char read_buffer1[size] = {0}, read_buffer2[size] = {0}; 107 DWORD read_size1 = size, read_size2 = kArbitraryreadSize; 108 HRESULT ret1 = E_FAIL, ret2 = E_FAIL; 109 110 EXPECT_CALL(mock, OnDataAvailable(flags, size, 111 testing::Field(&FORMATETC::cfFormat, cf), 112 testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) 113 .WillOnce(testing::DoAll( 114 ReadStream(read_buffer1, &read_size1, &ret1), 115 ReadStream(read_buffer2, &read_size2, &ret2), 116 Return(S_OK))); 117 118 EXPECT_HRESULT_SUCCEEDED(CacheStream::BSCBFeedData(&mock, data, size, cf, 119 flags, false)); 120 121 EXPECT_HRESULT_SUCCEEDED(ret1); 122 EXPECT_STREQ(data, read_buffer1); 123 EXPECT_EQ(size, read_size1); 124 125 EXPECT_EQ(E_PENDING, ret2); 126 EXPECT_STREQ("", read_buffer2); 127 EXPECT_EQ(kArbitraryreadSize, read_size2); 128 } 129 130 const wchar_t kSmallHtmlMetaTag[] = L"sub_frame1.html"; 131 const wchar_t kSmallHtmlNoMetaTag[] = L"host_browser.html"; 132 133 // Test various aspects of the SniffData class 134 TEST_F(MonikerPatchTest, SniffDataMetaTag) { 135 std::string small_html_meta_tag, small_html_no_meta_tag; 136 ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); 137 ASSERT_TRUE(ReadFileAsString(kSmallHtmlNoMetaTag, &small_html_no_meta_tag)); 138 139 base::win::ScopedComPtr<IStream> stream_with_meta, stream_no_meta; 140 ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); 141 ASSERT_TRUE(StringToStream(small_html_no_meta_tag, 142 stream_no_meta.Receive())); 143 144 // Initialize 2 sniffers 1 with meta tag and 1 without. 145 SniffData sniffer1, sniffer2; 146 EXPECT_HRESULT_SUCCEEDED(sniffer1.InitializeCache(std::wstring())); 147 EXPECT_HRESULT_SUCCEEDED(sniffer2.InitializeCache(std::wstring())); 148 EXPECT_HRESULT_SUCCEEDED(sniffer1.ReadIntoCache(stream_with_meta, true)); 149 EXPECT_HRESULT_SUCCEEDED(sniffer2.ReadIntoCache(stream_no_meta, true)); 150 151 // Verify renderer type and size read. 152 EXPECT_TRUE(sniffer1.is_chrome()); 153 EXPECT_EQ(SniffData::OTHER, sniffer2.renderer_type()); 154 EXPECT_EQ(small_html_meta_tag.size(), sniffer1.size()); 155 EXPECT_EQ(small_html_no_meta_tag.size(), sniffer2.size()); 156 } 157 158 // Now test how the data is fed back the the bind status callback. 159 // case 1: callback reads data in 1 read 160 TEST_F(MonikerPatchTest, SniffDataPlayback1) { 161 std::string small_html_meta_tag; 162 base::win::ScopedComPtr<IStream> stream_with_meta; 163 SniffData sniffer; 164 165 EXPECT_HRESULT_SUCCEEDED(sniffer.InitializeCache(std::wstring())); 166 ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); 167 ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); 168 EXPECT_HRESULT_SUCCEEDED(sniffer.ReadIntoCache(stream_with_meta, true)); 169 170 CComObjectStackEx<MockBindStatusCallbackImpl> mock; 171 const CLIPFORMAT cf = 0xd0d0; 172 const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; 173 const DWORD data_size = small_html_meta_tag.size(); 174 175 DWORD read_size1 = data_size * 2; 176 scoped_ptr<char[]> read_buffer1(new char[read_size1]); 177 ZeroMemory(read_buffer1.get(), read_size1); 178 179 char read_buffer2[10] = {0}; 180 DWORD read_size2 = sizeof(read_buffer2); 181 HRESULT ret1 = E_FAIL, ret2 = E_FAIL; 182 183 EXPECT_CALL(mock, OnDataAvailable(flags, data_size, 184 testing::Field(&FORMATETC::cfFormat, cf), 185 testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) 186 .WillOnce(testing::DoAll( 187 ReadStream(read_buffer1.get(), &read_size1, &ret1), 188 ReadStream(read_buffer2, &read_size2, &ret2), 189 Return(S_OK))); 190 191 EXPECT_HRESULT_SUCCEEDED(sniffer.DrainCache(&mock, flags, cf)); 192 193 EXPECT_HRESULT_SUCCEEDED(ret1); 194 EXPECT_EQ(small_html_meta_tag, read_buffer1.get()); 195 EXPECT_EQ(data_size, read_size1); 196 197 EXPECT_EQ(S_FALSE, ret2); 198 EXPECT_STREQ("", read_buffer2); 199 EXPECT_EQ(sizeof(read_buffer2), read_size2); 200 } 201 202 // case 2: callback reads data in 2 reads. 203 TEST_F(MonikerPatchTest, SniffDataPlayback2) { 204 std::string small_html_meta_tag; 205 base::win::ScopedComPtr<IStream> stream_with_meta; 206 SniffData sniffer; 207 208 EXPECT_HRESULT_SUCCEEDED(sniffer.InitializeCache(std::wstring())); 209 ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); 210 ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); 211 EXPECT_HRESULT_SUCCEEDED(sniffer.ReadIntoCache(stream_with_meta, true)); 212 213 CComObjectStackEx<MockBindStatusCallbackImpl> mock; 214 const CLIPFORMAT cf = 0xd0d0; 215 const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; 216 const DWORD data_size = small_html_meta_tag.size(); 217 218 DWORD read_size1 = data_size / 2; // First read is half the data. 219 DWORD read_size2 = data_size; // Second read, try to read past data. 220 scoped_ptr<char[]> read_buffer1(new char[read_size1]); 221 scoped_ptr<char[]> read_buffer2(new char[read_size2]); 222 ZeroMemory(read_buffer1.get(), read_size1); 223 ZeroMemory(read_buffer2.get(), read_size2); 224 225 char read_buffer3[10] = {0}; 226 DWORD read_size3 = sizeof(read_buffer3); 227 HRESULT ret1 = E_FAIL, ret2 = E_FAIL, ret3 = E_FAIL; 228 229 EXPECT_CALL(mock, OnDataAvailable(flags, data_size, 230 testing::Field(&FORMATETC::cfFormat, cf), 231 testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) 232 .WillOnce(testing::DoAll( 233 ReadStream(read_buffer1.get(), &read_size1, &ret1), 234 ReadStream(read_buffer2.get(), &read_size2, &ret2), 235 ReadStream(read_buffer3, &read_size3, &ret3), 236 Return(S_OK))); 237 238 EXPECT_HRESULT_SUCCEEDED(sniffer.DrainCache(&mock, flags, cf)); 239 240 EXPECT_HRESULT_SUCCEEDED(ret1); 241 EXPECT_HRESULT_SUCCEEDED(ret2); 242 EXPECT_EQ(data_size/2, read_size1); 243 EXPECT_EQ(data_size - read_size1, read_size2); 244 245 std::string data_read; 246 data_read.append(read_buffer1.get(), read_size1); 247 data_read.append(read_buffer2.get(), read_size2); 248 EXPECT_EQ(small_html_meta_tag, data_read); 249 250 EXPECT_EQ(S_FALSE, ret3); 251 EXPECT_STREQ("", read_buffer3); 252 EXPECT_EQ(sizeof(read_buffer3), read_size3); 253 } 254