1// Copyright 2014 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 "content/renderer/manifest/manifest_parser.h" 6 7#include "base/strings/string_util.h" 8#include "content/public/common/manifest.h" 9#include "testing/gtest/include/gtest/gtest.h" 10 11namespace content { 12 13class ManifestParserTest : public testing::Test { 14 protected: 15 ManifestParserTest() {} 16 virtual ~ManifestParserTest() {} 17 18 Manifest ParseManifest(const base::StringPiece& json, 19 const GURL& document_url = default_document_url, 20 const GURL& manifest_url = default_manifest_url) { 21 return ManifestParser::Parse(json, document_url, manifest_url); 22 } 23 24 static const GURL default_document_url; 25 static const GURL default_manifest_url; 26 27 private: 28 DISALLOW_COPY_AND_ASSIGN(ManifestParserTest); 29}; 30 31const GURL ManifestParserTest::default_document_url( 32 "http://foo.com/index.html"); 33const GURL ManifestParserTest::default_manifest_url( 34 "http://foo.com/manifest.json"); 35 36TEST_F(ManifestParserTest, EmptyStringNull) { 37 Manifest manifest = ParseManifest(""); 38 39 // A parsing error is equivalent to an empty manifest. 40 ASSERT_TRUE(manifest.IsEmpty()); 41 ASSERT_TRUE(manifest.name.is_null()); 42 ASSERT_TRUE(manifest.short_name.is_null()); 43 ASSERT_TRUE(manifest.start_url.is_empty()); 44 ASSERT_EQ(manifest.display, Manifest::DISPLAY_MODE_UNSPECIFIED); 45 ASSERT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault); 46} 47 48TEST_F(ManifestParserTest, ValidNoContentParses) { 49 Manifest manifest = ParseManifest("{}"); 50 51 // Check that all the fields are null in that case. 52 ASSERT_TRUE(manifest.IsEmpty()); 53 ASSERT_TRUE(manifest.name.is_null()); 54 ASSERT_TRUE(manifest.short_name.is_null()); 55 ASSERT_TRUE(manifest.start_url.is_empty()); 56 ASSERT_EQ(manifest.display, Manifest::DISPLAY_MODE_UNSPECIFIED); 57 ASSERT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault); 58} 59 60TEST_F(ManifestParserTest, NameParseRules) { 61 // Smoke test. 62 { 63 Manifest manifest = ParseManifest("{ \"name\": \"foo\" }"); 64 ASSERT_TRUE(EqualsASCII(manifest.name.string(), "foo")); 65 ASSERT_FALSE(manifest.IsEmpty()); 66 } 67 68 // Trim whitespaces. 69 { 70 Manifest manifest = ParseManifest("{ \"name\": \" foo \" }"); 71 ASSERT_TRUE(EqualsASCII(manifest.name.string(), "foo")); 72 } 73 74 // Don't parse if name isn't a string. 75 { 76 Manifest manifest = ParseManifest("{ \"name\": {} }"); 77 ASSERT_TRUE(manifest.name.is_null()); 78 } 79 80 // Don't parse if name isn't a string. 81 { 82 Manifest manifest = ParseManifest("{ \"name\": 42 }"); 83 ASSERT_TRUE(manifest.name.is_null()); 84 } 85} 86 87TEST_F(ManifestParserTest, ShortNameParseRules) { 88 // Smoke test. 89 { 90 Manifest manifest = ParseManifest("{ \"short_name\": \"foo\" }"); 91 ASSERT_TRUE(EqualsASCII(manifest.short_name.string(), "foo")); 92 ASSERT_FALSE(manifest.IsEmpty()); 93 } 94 95 // Trim whitespaces. 96 { 97 Manifest manifest = ParseManifest("{ \"short_name\": \" foo \" }"); 98 ASSERT_TRUE(EqualsASCII(manifest.short_name.string(), "foo")); 99 } 100 101 // Don't parse if name isn't a string. 102 { 103 Manifest manifest = ParseManifest("{ \"short_name\": {} }"); 104 ASSERT_TRUE(manifest.short_name.is_null()); 105 } 106 107 // Don't parse if name isn't a string. 108 { 109 Manifest manifest = ParseManifest("{ \"short_name\": 42 }"); 110 ASSERT_TRUE(manifest.short_name.is_null()); 111 } 112} 113 114TEST_F(ManifestParserTest, StartURLParseRules) { 115 // Smoke test. 116 { 117 Manifest manifest = ParseManifest("{ \"start_url\": \"land.html\" }"); 118 ASSERT_EQ(manifest.start_url.spec(), 119 default_document_url.Resolve("land.html").spec()); 120 ASSERT_FALSE(manifest.IsEmpty()); 121 } 122 123 // Whitespaces. 124 { 125 Manifest manifest = ParseManifest("{ \"start_url\": \" land.html \" }"); 126 ASSERT_EQ(manifest.start_url.spec(), 127 default_document_url.Resolve("land.html").spec()); 128 } 129 130 // Don't parse if property isn't a string. 131 { 132 Manifest manifest = ParseManifest("{ \"start_url\": {} }"); 133 ASSERT_TRUE(manifest.start_url.is_empty()); 134 } 135 136 // Don't parse if property isn't a string. 137 { 138 Manifest manifest = ParseManifest("{ \"start_url\": 42 }"); 139 ASSERT_TRUE(manifest.start_url.is_empty()); 140 } 141 142 // Absolute start_url, same origin with document. 143 { 144 Manifest manifest = 145 ParseManifest("{ \"start_url\": \"http://foo.com/land.html\" }", 146 GURL("http://foo.com/manifest.json"), 147 GURL("http://foo.com/index.html")); 148 ASSERT_EQ(manifest.start_url.spec(), "http://foo.com/land.html"); 149 } 150 151 // Absolute start_url, cross origin with document. 152 { 153 Manifest manifest = 154 ParseManifest("{ \"start_url\": \"http://bar.com/land.html\" }", 155 GURL("http://foo.com/manifest.json"), 156 GURL("http://foo.com/index.html")); 157 ASSERT_TRUE(manifest.start_url.is_empty()); 158 } 159 160 // Resolving has to happen based on the manifest_url. 161 { 162 Manifest manifest = 163 ParseManifest("{ \"start_url\": \"land.html\" }", 164 GURL("http://foo.com/landing/manifest.json"), 165 GURL("http://foo.com/index.html")); 166 ASSERT_EQ(manifest.start_url.spec(), "http://foo.com/landing/land.html"); 167 } 168} 169 170TEST_F(ManifestParserTest, DisplayParserRules) { 171 // Smoke test. 172 { 173 Manifest manifest = ParseManifest("{ \"display\": \"browser\" }"); 174 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_BROWSER); 175 EXPECT_FALSE(manifest.IsEmpty()); 176 } 177 178 // Trim whitespaces. 179 { 180 Manifest manifest = ParseManifest("{ \"display\": \" browser \" }"); 181 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_BROWSER); 182 } 183 184 // Don't parse if name isn't a string. 185 { 186 Manifest manifest = ParseManifest("{ \"display\": {} }"); 187 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_UNSPECIFIED); 188 } 189 190 // Don't parse if name isn't a string. 191 { 192 Manifest manifest = ParseManifest("{ \"display\": 42 }"); 193 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_UNSPECIFIED); 194 } 195 196 // Parse fails if string isn't known. 197 { 198 Manifest manifest = ParseManifest("{ \"display\": \"browser_something\" }"); 199 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_UNSPECIFIED); 200 } 201 202 // Accept 'fullscreen'. 203 { 204 Manifest manifest = ParseManifest("{ \"display\": \"fullscreen\" }"); 205 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_FULLSCREEN); 206 } 207 208 // Accept 'fullscreen'. 209 { 210 Manifest manifest = ParseManifest("{ \"display\": \"standalone\" }"); 211 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_STANDALONE); 212 } 213 214 // Accept 'minimal-ui'. 215 { 216 Manifest manifest = ParseManifest("{ \"display\": \"minimal-ui\" }"); 217 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_MINIMAL_UI); 218 } 219 220 // Accept 'browser'. 221 { 222 Manifest manifest = ParseManifest("{ \"display\": \"browser\" }"); 223 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_BROWSER); 224 } 225 226 // Case insensitive. 227 { 228 Manifest manifest = ParseManifest("{ \"display\": \"BROWSER\" }"); 229 EXPECT_EQ(manifest.display, Manifest::DISPLAY_MODE_BROWSER); 230 } 231} 232 233TEST_F(ManifestParserTest, OrientationParserRules) { 234 // Smoke test. 235 { 236 Manifest manifest = ParseManifest("{ \"orientation\": \"natural\" }"); 237 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockNatural); 238 EXPECT_FALSE(manifest.IsEmpty()); 239 } 240 241 // Trim whitespaces. 242 { 243 Manifest manifest = ParseManifest("{ \"orientation\": \"natural\" }"); 244 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockNatural); 245 } 246 247 // Don't parse if name isn't a string. 248 { 249 Manifest manifest = ParseManifest("{ \"orientation\": {} }"); 250 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault); 251 } 252 253 // Don't parse if name isn't a string. 254 { 255 Manifest manifest = ParseManifest("{ \"orientation\": 42 }"); 256 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault); 257 } 258 259 // Parse fails if string isn't known. 260 { 261 Manifest manifest = ParseManifest("{ \"orientation\": \"naturalish\" }"); 262 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockDefault); 263 } 264 265 // Accept 'any'. 266 { 267 Manifest manifest = ParseManifest("{ \"orientation\": \"any\" }"); 268 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockAny); 269 } 270 271 // Accept 'natural'. 272 { 273 Manifest manifest = ParseManifest("{ \"orientation\": \"natural\" }"); 274 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockNatural); 275 } 276 277 // Accept 'landscape'. 278 { 279 Manifest manifest = ParseManifest("{ \"orientation\": \"landscape\" }"); 280 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockLandscape); 281 } 282 283 // Accept 'landscape-primary'. 284 { 285 Manifest manifest = 286 ParseManifest("{ \"orientation\": \"landscape-primary\" }"); 287 EXPECT_EQ(manifest.orientation, 288 blink::WebScreenOrientationLockLandscapePrimary); 289 } 290 291 // Accept 'landscape-secondary'. 292 { 293 Manifest manifest = 294 ParseManifest("{ \"orientation\": \"landscape-secondary\" }"); 295 EXPECT_EQ(manifest.orientation, 296 blink::WebScreenOrientationLockLandscapeSecondary); 297 } 298 299 // Accept 'portrait'. 300 { 301 Manifest manifest = ParseManifest("{ \"orientation\": \"portrait\" }"); 302 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockPortrait); 303 } 304 305 // Accept 'portrait-primary'. 306 { 307 Manifest manifest = 308 ParseManifest("{ \"orientation\": \"portrait-primary\" }"); 309 EXPECT_EQ(manifest.orientation, 310 blink::WebScreenOrientationLockPortraitPrimary); 311 } 312 313 // Accept 'portrait-secondary'. 314 { 315 Manifest manifest = 316 ParseManifest("{ \"orientation\": \"portrait-secondary\" }"); 317 EXPECT_EQ(manifest.orientation, 318 blink::WebScreenOrientationLockPortraitSecondary); 319 } 320 321 // Case insensitive. 322 { 323 Manifest manifest = ParseManifest("{ \"orientation\": \"LANDSCAPE\" }"); 324 EXPECT_EQ(manifest.orientation, blink::WebScreenOrientationLockLandscape); 325 } 326} 327 328TEST_F(ManifestParserTest, IconsParseRules) { 329 // Smoke test: if no icon, empty list. 330 { 331 Manifest manifest = ParseManifest("{ \"icons\": [] }"); 332 EXPECT_EQ(manifest.icons.size(), 0u); 333 EXPECT_TRUE(manifest.IsEmpty()); 334 } 335 336 // Smoke test: if empty icon, empty list. 337 { 338 Manifest manifest = ParseManifest("{ \"icons\": [ {} ] }"); 339 EXPECT_EQ(manifest.icons.size(), 0u); 340 EXPECT_TRUE(manifest.IsEmpty()); 341 } 342 343 // Smoke test: icon with invalid src, empty list. 344 { 345 Manifest manifest = ParseManifest("{ \"icons\": [ { \"icons\": [] } ] }"); 346 EXPECT_EQ(manifest.icons.size(), 0u); 347 EXPECT_TRUE(manifest.IsEmpty()); 348 } 349 350 // Smoke test: if icon with empty src, it will be present in the list. 351 { 352 Manifest manifest = ParseManifest("{ \"icons\": [ { \"src\": \"\" } ] }"); 353 EXPECT_EQ(manifest.icons.size(), 1u); 354 EXPECT_EQ(manifest.icons[0].src.spec(), "http://foo.com/index.html"); 355 EXPECT_FALSE(manifest.IsEmpty()); 356 } 357 358 // Smoke test: if one icons with valid src, it will be present in the list. 359 { 360 Manifest manifest = 361 ParseManifest("{ \"icons\": [{ \"src\": \"foo.jpg\" }] }"); 362 EXPECT_EQ(manifest.icons.size(), 1u); 363 EXPECT_EQ(manifest.icons[0].src.spec(), "http://foo.com/foo.jpg"); 364 EXPECT_FALSE(manifest.IsEmpty()); 365 } 366} 367 368TEST_F(ManifestParserTest, IconSrcParseRules) { 369 // Smoke test. 370 { 371 Manifest manifest = 372 ParseManifest("{ \"icons\": [ {\"src\": \"foo.png\" } ] }"); 373 EXPECT_EQ(manifest.icons[0].src.spec(), 374 default_document_url.Resolve("foo.png").spec()); 375 } 376 377 // Whitespaces. 378 { 379 Manifest manifest = 380 ParseManifest("{ \"icons\": [ {\"src\": \" foo.png \" } ] }"); 381 EXPECT_EQ(manifest.icons[0].src.spec(), 382 default_document_url.Resolve("foo.png").spec()); 383 } 384 385 // Don't parse if property isn't a string. 386 { 387 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": {} } ] }"); 388 EXPECT_TRUE(manifest.icons.empty()); 389 } 390 391 // Don't parse if property isn't a string. 392 { 393 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": 42 } ] }"); 394 EXPECT_TRUE(manifest.icons.empty()); 395 } 396 397 // Resolving has to happen based on the document_url. 398 { 399 Manifest manifest = 400 ParseManifest("{ \"icons\": [ {\"src\": \"icons/foo.png\" } ] }", 401 GURL("http://foo.com/landing/index.html")); 402 EXPECT_EQ(manifest.icons[0].src.spec(), 403 "http://foo.com/landing/icons/foo.png"); 404 } 405} 406 407TEST_F(ManifestParserTest, IconTypeParseRules) { 408 // Smoke test. 409 { 410 Manifest manifest = 411 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"type\": \"foo\" } ] }"); 412 EXPECT_TRUE(EqualsASCII(manifest.icons[0].type.string(), "foo")); 413 } 414 415 // Trim whitespaces. 416 { 417 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 418 " \"type\": \" foo \" } ] }"); 419 EXPECT_TRUE(EqualsASCII(manifest.icons[0].type.string(), "foo")); 420 } 421 422 // Don't parse if property isn't a string. 423 { 424 Manifest manifest = 425 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"type\": {} } ] }"); 426 EXPECT_TRUE(manifest.icons[0].type.is_null()); 427 } 428 429 // Don't parse if property isn't a string. 430 { 431 Manifest manifest = 432 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"type\": 42 } ] }"); 433 EXPECT_TRUE(manifest.icons[0].type.is_null()); 434 } 435} 436 437TEST_F(ManifestParserTest, IconDensityParseRules) { 438 // Smoke test. 439 { 440 Manifest manifest = 441 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": 42 } ] }"); 442 EXPECT_EQ(manifest.icons[0].density, 42); 443 } 444 445 // Decimal value. 446 { 447 Manifest manifest = 448 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": 2.5 } ] }"); 449 EXPECT_EQ(manifest.icons[0].density, 2.5); 450 } 451 452 // Parse fail if it isn't a float. 453 { 454 Manifest manifest = 455 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": {} } ] }"); 456 EXPECT_EQ(manifest.icons[0].density, Manifest::Icon::kDefaultDensity); 457 } 458 459 // Parse fail if it isn't a float. 460 { 461 Manifest manifest = 462 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\":\"2\" } ] }"); 463 EXPECT_EQ(manifest.icons[0].density, Manifest::Icon::kDefaultDensity); 464 } 465 466 // Edge case: 1.0. 467 { 468 Manifest manifest = 469 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": 1.00 } ] }"); 470 EXPECT_EQ(manifest.icons[0].density, 1); 471 } 472 473 // Edge case: values between 0.0 and 1.0 are allowed. 474 { 475 Manifest manifest = 476 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": 0.42 } ] }"); 477 EXPECT_EQ(manifest.icons[0].density, 0.42); 478 } 479 480 // 0 is an invalid value. 481 { 482 Manifest manifest = 483 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": 0.0 } ] }"); 484 EXPECT_EQ(manifest.icons[0].density, Manifest::Icon::kDefaultDensity); 485 } 486 487 // Negative values are invalid. 488 { 489 Manifest manifest = 490 ParseManifest("{ \"icons\": [ {\"src\": \"\", \"density\": -2.5 } ] }"); 491 EXPECT_EQ(manifest.icons[0].density, Manifest::Icon::kDefaultDensity); 492 } 493} 494 495TEST_F(ManifestParserTest, IconSizesParseRules) { 496 // Smoke test. 497 { 498 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 499 "\"sizes\": \"42x42\" } ] }"); 500 EXPECT_EQ(manifest.icons[0].sizes.size(), 1u); 501 } 502 503 // Trim whitespaces. 504 { 505 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 506 "\"sizes\": \" 42x42 \" } ] }"); 507 EXPECT_EQ(manifest.icons[0].sizes.size(), 1u); 508 } 509 510 // Don't parse if name isn't a string. 511 { 512 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 513 "\"sizes\": {} } ] }"); 514 EXPECT_EQ(manifest.icons[0].sizes.size(), 0u); 515 } 516 517 // Don't parse if name isn't a string. 518 { 519 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 520 "\"sizes\": 42 } ] }"); 521 EXPECT_EQ(manifest.icons[0].sizes.size(), 0u); 522 } 523 524 // Smoke test: value correctly parsed. 525 { 526 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 527 "\"sizes\": \"42x42 48x48\" } ] }"); 528 EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42)); 529 EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(48, 48)); 530 } 531 532 // <WIDTH>'x'<HEIGHT> and <WIDTH>'X'<HEIGHT> are equivalent. 533 { 534 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 535 "\"sizes\": \"42X42 48X48\" } ] }"); 536 EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42)); 537 EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(48, 48)); 538 } 539 540 // Twice the same value is parsed twice. 541 { 542 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 543 "\"sizes\": \"42X42 42x42\" } ] }"); 544 EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42)); 545 EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(42, 42)); 546 } 547 548 // Width or height can't start with 0. 549 { 550 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 551 "\"sizes\": \"004X007 042x00\" } ] }"); 552 EXPECT_EQ(manifest.icons[0].sizes.size(), 0u); 553 } 554 555 // Width and height MUST contain digits. 556 { 557 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 558 "\"sizes\": \"e4X1.0 55ax1e10\" } ] }"); 559 EXPECT_EQ(manifest.icons[0].sizes.size(), 0u); 560 } 561 562 // 'any' is correctly parsed and transformed to gfx::Size(0,0). 563 { 564 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 565 "\"sizes\": \"any AnY ANY aNy\" } ] }"); 566 gfx::Size any = gfx::Size(0, 0); 567 EXPECT_EQ(manifest.icons[0].sizes.size(), 4u); 568 EXPECT_EQ(manifest.icons[0].sizes[0], any); 569 EXPECT_EQ(manifest.icons[0].sizes[1], any); 570 EXPECT_EQ(manifest.icons[0].sizes[2], any); 571 EXPECT_EQ(manifest.icons[0].sizes[3], any); 572 } 573 574 // Some invalid width/height combinations. 575 { 576 Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\"," 577 "\"sizes\": \"x 40xx 1x2x3 x42 42xx42\" } ] }"); 578 gfx::Size any = gfx::Size(0, 0); 579 EXPECT_EQ(manifest.icons[0].sizes.size(), 0u); 580 } 581} 582 583} // namespace content 584