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 "extensions/common/file_util.h" 6 7 #include "base/basictypes.h" 8 #include "base/file_util.h" 9 #include "base/files/scoped_temp_dir.h" 10 #include "base/json/json_string_value_serializer.h" 11 #include "base/path_service.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/common/chrome_paths.h" 15 #include "extensions/common/constants.h" 16 #include "extensions/common/extension.h" 17 #include "extensions/common/manifest.h" 18 #include "extensions/common/manifest_constants.h" 19 #include "grit/extensions_strings.h" 20 #include "testing/gmock/include/gmock/gmock.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 #include "ui/base/l10n/l10n_util.h" 23 #include "url/gurl.h" 24 25 namespace extensions { 26 27 namespace { 28 29 scoped_refptr<Extension> LoadExtensionManifest( 30 base::DictionaryValue* manifest, 31 const base::FilePath& manifest_dir, 32 Manifest::Location location, 33 int extra_flags, 34 std::string* error) { 35 scoped_refptr<Extension> extension = 36 Extension::Create(manifest_dir, location, *manifest, extra_flags, error); 37 return extension; 38 } 39 40 scoped_refptr<Extension> LoadExtensionManifest( 41 const std::string& manifest_value, 42 const base::FilePath& manifest_dir, 43 Manifest::Location location, 44 int extra_flags, 45 std::string* error) { 46 JSONStringValueSerializer serializer(manifest_value); 47 scoped_ptr<base::Value> result(serializer.Deserialize(NULL, error)); 48 if (!result.get()) 49 return NULL; 50 CHECK_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); 51 return LoadExtensionManifest( 52 static_cast<base::DictionaryValue*>(result.get()), 53 manifest_dir, 54 location, 55 extra_flags, 56 error); 57 } 58 59 } // namespace 60 61 typedef testing::Test FileUtilTest; 62 63 TEST_F(FileUtilTest, InstallUninstallGarbageCollect) { 64 base::ScopedTempDir temp; 65 ASSERT_TRUE(temp.CreateUniqueTempDir()); 66 67 // Create a source extension. 68 std::string extension_id("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 69 std::string version("1.0"); 70 base::FilePath src = temp.path().AppendASCII(extension_id); 71 ASSERT_TRUE(base::CreateDirectory(src)); 72 73 // Create a extensions tree. 74 base::FilePath all_extensions = temp.path().AppendASCII("extensions"); 75 ASSERT_TRUE(base::CreateDirectory(all_extensions)); 76 77 // Install in empty directory. Should create parent directories as needed. 78 base::FilePath version_1 = 79 file_util::InstallExtension(src, extension_id, version, all_extensions); 80 ASSERT_EQ( 81 version_1.value(), 82 all_extensions.AppendASCII(extension_id).AppendASCII("1.0_0").value()); 83 ASSERT_TRUE(base::DirectoryExists(version_1)); 84 85 // Should have moved the source. 86 ASSERT_FALSE(base::DirectoryExists(src)); 87 88 // Install again. Should create a new one with different name. 89 ASSERT_TRUE(base::CreateDirectory(src)); 90 base::FilePath version_2 = 91 file_util::InstallExtension(src, extension_id, version, all_extensions); 92 ASSERT_EQ( 93 version_2.value(), 94 all_extensions.AppendASCII(extension_id).AppendASCII("1.0_1").value()); 95 ASSERT_TRUE(base::DirectoryExists(version_2)); 96 97 // Should have moved the source. 98 ASSERT_FALSE(base::DirectoryExists(src)); 99 100 // Install yet again. Should create a new one with a different name. 101 ASSERT_TRUE(base::CreateDirectory(src)); 102 base::FilePath version_3 = 103 file_util::InstallExtension(src, extension_id, version, all_extensions); 104 ASSERT_EQ( 105 version_3.value(), 106 all_extensions.AppendASCII(extension_id).AppendASCII("1.0_2").value()); 107 ASSERT_TRUE(base::DirectoryExists(version_3)); 108 109 // Uninstall. Should remove entire extension subtree. 110 file_util::UninstallExtension(all_extensions, extension_id); 111 ASSERT_FALSE(base::DirectoryExists(version_1.DirName())); 112 ASSERT_FALSE(base::DirectoryExists(version_2.DirName())); 113 ASSERT_FALSE(base::DirectoryExists(version_3.DirName())); 114 ASSERT_TRUE(base::DirectoryExists(all_extensions)); 115 } 116 117 TEST_F(FileUtilTest, LoadExtensionWithValidLocales) { 118 base::FilePath install_dir; 119 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 120 install_dir = install_dir.AppendASCII("extensions") 121 .AppendASCII("good") 122 .AppendASCII("Extensions") 123 .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") 124 .AppendASCII("1.0.0.0"); 125 126 std::string error; 127 scoped_refptr<Extension> extension(file_util::LoadExtension( 128 install_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 129 ASSERT_TRUE(extension.get() != NULL); 130 EXPECT_EQ("The first extension that I made.", extension->description()); 131 } 132 133 TEST_F(FileUtilTest, LoadExtensionWithoutLocalesFolder) { 134 base::FilePath install_dir; 135 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 136 install_dir = install_dir.AppendASCII("extensions") 137 .AppendASCII("good") 138 .AppendASCII("Extensions") 139 .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa") 140 .AppendASCII("1.0"); 141 142 std::string error; 143 scoped_refptr<Extension> extension(file_util::LoadExtension( 144 install_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 145 ASSERT_FALSE(extension.get() == NULL); 146 EXPECT_TRUE(error.empty()); 147 } 148 149 TEST_F(FileUtilTest, CheckIllegalFilenamesNoUnderscores) { 150 base::ScopedTempDir temp; 151 ASSERT_TRUE(temp.CreateUniqueTempDir()); 152 153 base::FilePath src_path = temp.path().AppendASCII("some_dir"); 154 ASSERT_TRUE(base::CreateDirectory(src_path)); 155 156 std::string data = "{ \"name\": { \"message\": \"foobar\" } }"; 157 ASSERT_TRUE(base::WriteFile( 158 src_path.AppendASCII("some_file.txt"), data.c_str(), data.length())); 159 std::string error; 160 EXPECT_TRUE(file_util::CheckForIllegalFilenames(temp.path(), &error)); 161 } 162 163 TEST_F(FileUtilTest, CheckIllegalFilenamesOnlyReserved) { 164 base::ScopedTempDir temp; 165 ASSERT_TRUE(temp.CreateUniqueTempDir()); 166 167 const base::FilePath::CharType* folders[] = { 168 extensions::kLocaleFolder, extensions::kPlatformSpecificFolder}; 169 170 for (size_t i = 0; i < arraysize(folders); i++) { 171 base::FilePath src_path = temp.path().Append(folders[i]); 172 ASSERT_TRUE(base::CreateDirectory(src_path)); 173 } 174 175 std::string error; 176 EXPECT_TRUE(file_util::CheckForIllegalFilenames(temp.path(), &error)); 177 } 178 179 TEST_F(FileUtilTest, CheckIllegalFilenamesReservedAndIllegal) { 180 base::ScopedTempDir temp; 181 ASSERT_TRUE(temp.CreateUniqueTempDir()); 182 183 base::FilePath src_path = temp.path().Append(extensions::kLocaleFolder); 184 ASSERT_TRUE(base::CreateDirectory(src_path)); 185 186 src_path = temp.path().AppendASCII("_some_dir"); 187 ASSERT_TRUE(base::CreateDirectory(src_path)); 188 189 std::string error; 190 EXPECT_FALSE(file_util::CheckForIllegalFilenames(temp.path(), &error)); 191 } 192 193 TEST_F(FileUtilTest, LoadExtensionGivesHelpfullErrorOnMissingManifest) { 194 base::FilePath install_dir; 195 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 196 install_dir = install_dir.AppendASCII("extensions") 197 .AppendASCII("bad") 198 .AppendASCII("Extensions") 199 .AppendASCII("dddddddddddddddddddddddddddddddd") 200 .AppendASCII("1.0"); 201 202 std::string error; 203 scoped_refptr<Extension> extension(file_util::LoadExtension( 204 install_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 205 ASSERT_TRUE(extension.get() == NULL); 206 ASSERT_FALSE(error.empty()); 207 ASSERT_STREQ("Manifest file is missing or unreadable.", error.c_str()); 208 } 209 210 TEST_F(FileUtilTest, LoadExtensionGivesHelpfullErrorOnBadManifest) { 211 base::FilePath install_dir; 212 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 213 install_dir = install_dir.AppendASCII("extensions") 214 .AppendASCII("bad") 215 .AppendASCII("Extensions") 216 .AppendASCII("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") 217 .AppendASCII("1.0"); 218 219 std::string error; 220 scoped_refptr<Extension> extension(file_util::LoadExtension( 221 install_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 222 ASSERT_TRUE(extension.get() == NULL); 223 ASSERT_FALSE(error.empty()); 224 ASSERT_STREQ( 225 "Manifest is not valid JSON. " 226 "Line: 2, column: 16, Syntax error.", 227 error.c_str()); 228 } 229 230 TEST_F(FileUtilTest, FailLoadingNonUTF8Scripts) { 231 base::FilePath install_dir; 232 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 233 install_dir = install_dir.AppendASCII("extensions") 234 .AppendASCII("bad") 235 .AppendASCII("bad_encoding"); 236 237 std::string error; 238 scoped_refptr<Extension> extension(file_util::LoadExtension( 239 install_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 240 ASSERT_TRUE(extension.get() == NULL); 241 ASSERT_STREQ( 242 "Could not load file 'bad_encoding.js' for content script. " 243 "It isn't UTF-8 encoded.", 244 error.c_str()); 245 } 246 247 TEST_F(FileUtilTest, ValidateThemeUTF8) { 248 base::ScopedTempDir temp; 249 ASSERT_TRUE(temp.CreateUniqueTempDir()); 250 251 // "aeo" with accents. Use http://0xcc.net/jsescape/ to decode them. 252 std::string non_ascii_file = "\xC3\xA0\xC3\xA8\xC3\xB2.png"; 253 base::FilePath non_ascii_path = 254 temp.path().Append(base::FilePath::FromUTF8Unsafe(non_ascii_file)); 255 base::WriteFile(non_ascii_path, "", 0); 256 257 std::string kManifest = base::StringPrintf( 258 "{ \"name\": \"Test\", \"version\": \"1.0\", " 259 " \"theme\": { \"images\": { \"theme_frame\": \"%s\" } }" 260 "}", 261 non_ascii_file.c_str()); 262 std::string error; 263 scoped_refptr<Extension> extension = LoadExtensionManifest( 264 kManifest, temp.path(), Manifest::UNPACKED, 0, &error); 265 ASSERT_TRUE(extension.get()) << error; 266 267 std::vector<extensions::InstallWarning> warnings; 268 EXPECT_TRUE(file_util::ValidateExtension(extension.get(), &error, &warnings)) 269 << error; 270 EXPECT_EQ(0U, warnings.size()); 271 } 272 273 TEST_F(FileUtilTest, BackgroundScriptsMustExist) { 274 base::ScopedTempDir temp; 275 ASSERT_TRUE(temp.CreateUniqueTempDir()); 276 277 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); 278 value->SetString("name", "test"); 279 value->SetString("version", "1"); 280 value->SetInteger("manifest_version", 1); 281 282 base::ListValue* scripts = new base::ListValue(); 283 scripts->Append(new base::StringValue("foo.js")); 284 value->Set("background.scripts", scripts); 285 286 std::string error; 287 std::vector<extensions::InstallWarning> warnings; 288 scoped_refptr<Extension> extension = LoadExtensionManifest( 289 value.get(), temp.path(), Manifest::UNPACKED, 0, &error); 290 ASSERT_TRUE(extension.get()) << error; 291 292 EXPECT_FALSE( 293 file_util::ValidateExtension(extension.get(), &error, &warnings)); 294 EXPECT_EQ( 295 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED, 296 base::ASCIIToUTF16("foo.js")), 297 error); 298 EXPECT_EQ(0U, warnings.size()); 299 300 scripts->Clear(); 301 scripts->Append(new base::StringValue("http://google.com/foo.js")); 302 303 extension = LoadExtensionManifest( 304 value.get(), temp.path(), Manifest::UNPACKED, 0, &error); 305 ASSERT_TRUE(extension.get()) << error; 306 307 warnings.clear(); 308 EXPECT_FALSE( 309 file_util::ValidateExtension(extension.get(), &error, &warnings)); 310 EXPECT_EQ( 311 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED, 312 base::ASCIIToUTF16("http://google.com/foo.js")), 313 error); 314 EXPECT_EQ(0U, warnings.size()); 315 } 316 317 // Private key, generated by Chrome specifically for this test, and 318 // never used elsewhere. 319 const char private_key[] = 320 "-----BEGIN PRIVATE KEY-----\n" 321 "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKt02SR0FYaYy6fpW\n" 322 "MAA+kU1BgK3d+OmmWfdr+JATIjhRkyeSF4lTd/71JQsyKqPzYkQPi3EeROWM+goTv\n" 323 "EhJqq07q63BolpsFmlV+S4ny+sBA2B4aWwRYXlBWikdrQSA0mJMzvEHc6nKzBgXik\n" 324 "QSVbyyBNAsxlDB9WaCxRVOpK3AgMBAAECgYBGvSPlrVtAOAQ2V8j9FqorKZA8SLPX\n" 325 "IeJC/yzU3RB2nPMjI17aMOvrUHxJUhzMeh4jwabVvSzzDtKFozPGupW3xaI8sQdi2\n" 326 "WWMTQIk/Q9HHDWoQ9qA6SwX2qWCc5SyjCKqVp78ye+000kqTJYjBsDgXeAlzKcx2B\n" 327 "4GAAeWonDdkQJBANNb8wrqNWFn7DqyQTfELzcRTRnqQ/r1pdeJo6obzbnwGnlqe3t\n" 328 "KhLjtJNIGrQg5iC0OVLWFuvPJs0t3z62A1ckCQQDPq2JZuwTwu5Pl4DJ0r9O1FdqN\n" 329 "JgqPZyMptokCDQ3khLLGakIu+TqB9YtrzI69rJMSG2Egb+6McaDX+dh3XmR/AkB9t\n" 330 "xJf6qDnmA2td/tMtTc0NOk8Qdg/fD8xbZ/YfYMnVoYYs9pQoilBaWRePDRNURMLYZ\n" 331 "vHAI0Llmw7tj7jv17pAkEAz44uXRpjRKtllUIvi5pUENAHwDz+HvdpGH68jpU3hmb\n" 332 "uOwrmnQYxaMReFV68Z2w9DcLZn07f7/R9Wn72z89CxwJAFsDoNaDes4h48bX7plct\n" 333 "s9ACjmTwcCigZjN2K7AGv7ntCLF3DnV5dK0dTHNaAdD3SbY3jl29Rk2CwiURSX6Ee\n" 334 "g==\n" 335 "-----END PRIVATE KEY-----\n"; 336 337 TEST_F(FileUtilTest, FindPrivateKeyFiles) { 338 base::ScopedTempDir temp; 339 ASSERT_TRUE(temp.CreateUniqueTempDir()); 340 341 base::FilePath src_path = temp.path().AppendASCII("some_dir"); 342 ASSERT_TRUE(base::CreateDirectory(src_path)); 343 344 ASSERT_TRUE(base::WriteFile( 345 src_path.AppendASCII("a_key.pem"), private_key, arraysize(private_key))); 346 ASSERT_TRUE(base::WriteFile(src_path.AppendASCII("second_key.pem"), 347 private_key, 348 arraysize(private_key))); 349 // Shouldn't find a key with a different extension. 350 ASSERT_TRUE(base::WriteFile(src_path.AppendASCII("key.diff_ext"), 351 private_key, 352 arraysize(private_key))); 353 // Shouldn't find a key that isn't parsable. 354 ASSERT_TRUE(base::WriteFile(src_path.AppendASCII("unparsable_key.pem"), 355 private_key, 356 arraysize(private_key) - 30)); 357 std::vector<base::FilePath> private_keys = 358 file_util::FindPrivateKeyFiles(temp.path()); 359 EXPECT_EQ(2U, private_keys.size()); 360 EXPECT_THAT(private_keys, 361 testing::Contains(src_path.AppendASCII("a_key.pem"))); 362 EXPECT_THAT(private_keys, 363 testing::Contains(src_path.AppendASCII("second_key.pem"))); 364 } 365 366 TEST_F(FileUtilTest, WarnOnPrivateKey) { 367 base::ScopedTempDir temp; 368 ASSERT_TRUE(temp.CreateUniqueTempDir()); 369 370 base::FilePath ext_path = temp.path().AppendASCII("ext_root"); 371 ASSERT_TRUE(base::CreateDirectory(ext_path)); 372 373 const char manifest[] = 374 "{\n" 375 " \"name\": \"Test Extension\",\n" 376 " \"version\": \"1.0\",\n" 377 " \"manifest_version\": 2,\n" 378 " \"description\": \"The first extension that I made.\"\n" 379 "}\n"; 380 ASSERT_TRUE(base::WriteFile( 381 ext_path.AppendASCII("manifest.json"), manifest, strlen(manifest))); 382 ASSERT_TRUE(base::WriteFile( 383 ext_path.AppendASCII("a_key.pem"), private_key, strlen(private_key))); 384 385 std::string error; 386 scoped_refptr<Extension> extension( 387 file_util::LoadExtension(ext_path, 388 "the_id", 389 Manifest::EXTERNAL_PREF, 390 Extension::NO_FLAGS, 391 &error)); 392 ASSERT_TRUE(extension.get()) << error; 393 ASSERT_EQ(1u, extension->install_warnings().size()); 394 EXPECT_THAT(extension->install_warnings(), 395 testing::ElementsAre(testing::Field( 396 &extensions::InstallWarning::message, 397 testing::ContainsRegex( 398 "extension includes the key file.*ext_root.a_key.pem")))); 399 400 // Turn the warning into an error with ERROR_ON_PRIVATE_KEY. 401 extension = file_util::LoadExtension(ext_path, 402 "the_id", 403 Manifest::EXTERNAL_PREF, 404 Extension::ERROR_ON_PRIVATE_KEY, 405 &error); 406 EXPECT_FALSE(extension.get()); 407 EXPECT_THAT(error, 408 testing::ContainsRegex( 409 "extension includes the key file.*ext_root.a_key.pem")); 410 } 411 412 TEST_F(FileUtilTest, CheckZeroLengthImageFile) { 413 base::FilePath install_dir; 414 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &install_dir)); 415 416 // Try to install an extension with a zero-length icon file. 417 base::FilePath ext_dir = install_dir.AppendASCII("extensions") 418 .AppendASCII("bad") 419 .AppendASCII("Extensions") 420 .AppendASCII("ffffffffffffffffffffffffffffffff"); 421 422 std::string error; 423 scoped_refptr<Extension> extension(file_util::LoadExtension( 424 ext_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 425 EXPECT_TRUE(extension.get() == NULL); 426 EXPECT_STREQ("Could not load extension icon 'icon.png'.", error.c_str()); 427 428 // Try to install an extension with a zero-length browser action icon file. 429 ext_dir = install_dir.AppendASCII("extensions") 430 .AppendASCII("bad") 431 .AppendASCII("Extensions") 432 .AppendASCII("gggggggggggggggggggggggggggggggg"); 433 434 scoped_refptr<Extension> extension2(file_util::LoadExtension( 435 ext_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 436 EXPECT_TRUE(extension2.get() == NULL); 437 EXPECT_STREQ("Could not load icon 'icon.png' for browser action.", 438 error.c_str()); 439 440 // Try to install an extension with a zero-length page action icon file. 441 ext_dir = install_dir.AppendASCII("extensions") 442 .AppendASCII("bad") 443 .AppendASCII("Extensions") 444 .AppendASCII("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"); 445 446 scoped_refptr<Extension> extension3(file_util::LoadExtension( 447 ext_dir, Manifest::UNPACKED, Extension::NO_FLAGS, &error)); 448 EXPECT_TRUE(extension3.get() == NULL); 449 EXPECT_STREQ("Could not load icon 'icon.png' for page action.", 450 error.c_str()); 451 } 452 453 TEST_F(FileUtilTest, ExtensionURLToRelativeFilePath) { 454 #define URL_PREFIX "chrome-extension://extension-id/" 455 struct TestCase { 456 const char* url; 457 const char* expected_relative_path; 458 } test_cases[] = { 459 { URL_PREFIX "simple.html", 460 "simple.html" }, 461 { URL_PREFIX "directory/to/file.html", 462 "directory/to/file.html" }, 463 { URL_PREFIX "escape%20spaces.html", 464 "escape spaces.html" }, 465 { URL_PREFIX "%C3%9Cber.html", 466 "\xC3\x9C" "ber.html" }, 467 #if defined(OS_WIN) 468 { URL_PREFIX "C%3A/simple.html", 469 "" }, 470 #endif 471 { URL_PREFIX "////simple.html", 472 "simple.html" }, 473 { URL_PREFIX "/simple.html", 474 "simple.html" }, 475 { URL_PREFIX "\\simple.html", 476 "simple.html" }, 477 { URL_PREFIX "\\\\foo\\simple.html", 478 "foo/simple.html" }, 479 }; 480 #undef URL_PREFIX 481 482 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 483 GURL url(test_cases[i].url); 484 base::FilePath expected_path = 485 base::FilePath::FromUTF8Unsafe(test_cases[i].expected_relative_path); 486 base::FilePath actual_path = 487 extensions::file_util::ExtensionURLToRelativeFilePath(url); 488 EXPECT_FALSE(actual_path.IsAbsolute()) << 489 " For the path " << actual_path.value(); 490 EXPECT_EQ(expected_path.value(), actual_path.value()) << 491 " For the path " << url; 492 } 493 } 494 495 TEST_F(FileUtilTest, ExtensionResourceURLToFilePath) { 496 // Setup filesystem for testing. 497 base::FilePath root_path; 498 ASSERT_TRUE(base::CreateNewTempDirectory( 499 base::FilePath::StringType(), &root_path)); 500 root_path = base::MakeAbsoluteFilePath(root_path); 501 ASSERT_FALSE(root_path.empty()); 502 503 base::FilePath api_path = root_path.Append(FILE_PATH_LITERAL("apiname")); 504 ASSERT_TRUE(base::CreateDirectory(api_path)); 505 506 const char data[] = "Test Data"; 507 base::FilePath resource_path = api_path.Append(FILE_PATH_LITERAL("test.js")); 508 ASSERT_TRUE(base::WriteFile(resource_path, data, sizeof(data))); 509 resource_path = api_path.Append(FILE_PATH_LITERAL("escape spaces.js")); 510 ASSERT_TRUE(base::WriteFile(resource_path, data, sizeof(data))); 511 512 #ifdef FILE_PATH_USES_WIN_SEPARATORS 513 #define SEP "\\" 514 #else 515 #define SEP "/" 516 #endif 517 #define URL_PREFIX "chrome-extension-resource://" 518 struct TestCase { 519 const char* url; 520 const base::FilePath::CharType* expected_path; 521 } test_cases[] = { 522 { URL_PREFIX "apiname/test.js", 523 FILE_PATH_LITERAL("test.js") }, 524 { URL_PREFIX "/apiname/test.js", 525 FILE_PATH_LITERAL("test.js") }, 526 // Test % escape 527 { URL_PREFIX "apiname/%74%65st.js", 528 FILE_PATH_LITERAL("test.js") }, 529 { URL_PREFIX "apiname/escape%20spaces.js", 530 FILE_PATH_LITERAL("escape spaces.js") }, 531 // Test file does not exist. 532 { URL_PREFIX "apiname/directory/to/file.js", 533 NULL }, 534 // Test apiname/../../test.js 535 { URL_PREFIX "apiname/../../test.js", 536 FILE_PATH_LITERAL("test.js") }, 537 { URL_PREFIX "apiname/..%2F../test.js", 538 NULL }, 539 { URL_PREFIX "apiname/f/../../../test.js", 540 FILE_PATH_LITERAL("test.js") }, 541 { URL_PREFIX "apiname/f%2F..%2F..%2F../test.js", 542 NULL }, 543 }; 544 #undef SEP 545 #undef URL_PREFIX 546 547 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 548 GURL url(test_cases[i].url); 549 base::FilePath expected_path; 550 if (test_cases[i].expected_path) 551 expected_path = root_path.Append(FILE_PATH_LITERAL("apiname")).Append( 552 test_cases[i].expected_path); 553 base::FilePath actual_path = 554 extensions::file_util::ExtensionResourceURLToFilePath(url, root_path); 555 EXPECT_EQ(expected_path.value(), actual_path.value()) << 556 " For the path " << url; 557 } 558 // Remove temp files. 559 ASSERT_TRUE(base::DeleteFile(root_path, true)); 560 } 561 562 } // namespace extensions 563