1// Copyright (c) 2010 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 <string>
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "url/gurl.h"
9#include "webkit/browser/appcache/manifest_parser.h"
10
11namespace appcache {
12
13class AppCacheManifestParserTest : public testing::Test {
14};
15
16TEST(AppCacheManifestParserTest, NoData) {
17  GURL url;
18  Manifest manifest;
19  EXPECT_FALSE(ParseManifest(url, "", 0, manifest));
20  EXPECT_FALSE(ParseManifest(url, "CACHE MANIFEST\r", 0, manifest));  // 0 len
21}
22
23TEST(AppCacheManifestParserTest, CheckSignature) {
24  GURL url;
25  Manifest manifest;
26
27  const std::string kBadSignatures[] = {
28    "foo",
29    "CACHE MANIFEST;V2\r",          // not followed by whitespace
30    "CACHE MANIFEST#bad\r",         // no whitespace before comment
31    "cache manifest ",              // wrong case
32    "#CACHE MANIFEST\r",            // comment
33    "xCACHE MANIFEST\n",            // bad first char
34    " CACHE MANIFEST\r",            // begins with whitespace
35    "\xEF\xBE\xBF" "CACHE MANIFEST\r",  // bad UTF-8 BOM value
36  };
37
38  for (size_t i = 0; i < arraysize(kBadSignatures); ++i) {
39    const std::string bad = kBadSignatures[i];
40    EXPECT_FALSE(ParseManifest(url, bad.c_str(), bad.length(), manifest));
41  }
42
43  const std::string kGoodSignatures[] = {
44    "CACHE MANIFEST",
45    "CACHE MANIFEST ",
46    "CACHE MANIFEST\r",
47    "CACHE MANIFEST\n",
48    "CACHE MANIFEST\r\n",
49    "CACHE MANIFEST\t# ignore me\r",
50    "CACHE MANIFEST ignore\r\n",
51    "CHROMIUM CACHE MANIFEST\r\n",
52    "\xEF\xBB\xBF" "CACHE MANIFEST \r\n",   // BOM present
53  };
54
55  for (size_t i = 0; i < arraysize(kGoodSignatures); ++i) {
56    const std::string good = kGoodSignatures[i];
57    EXPECT_TRUE(ParseManifest(url, good.c_str(), good.length(), manifest));
58  }
59}
60
61TEST(AppCacheManifestParserTest, NoManifestUrl) {
62  Manifest manifest;
63  const std::string kData("CACHE MANIFEST\r"
64    "relative/tobase.com\r"
65    "http://absolute.com/addme.com");
66  const GURL kUrl;
67  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
68  EXPECT_TRUE(manifest.explicit_urls.empty());
69  EXPECT_TRUE(manifest.fallback_namespaces.empty());
70  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
71  EXPECT_FALSE(manifest.online_whitelist_all);
72}
73
74TEST(AppCacheManifestParserTest, ExplicitUrls) {
75  Manifest manifest;
76  const GURL kUrl("http://www.foo.com");
77  const std::string kData("CACHE MANIFEST\r"
78    "relative/one\r"
79    "# some comment\r"
80    "http://www.foo.com/two#strip\r\n"
81    "NETWORK:\r"
82    "  \t CACHE:\r"
83    "HTTP://www.diff.com/three\r"
84    "FALLBACK:\r"
85    " \t # another comment with leading whitespace\n"
86    "IGNORE:\r"
87    "http://www.foo.com/ignore\r"
88    "CACHE: \r"
89    "garbage:#!@\r"
90    "https://www.foo.com/diffscheme \t \r"
91    "  \t relative/four#stripme\n\r"
92    "*\r");
93
94  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
95  EXPECT_TRUE(manifest.fallback_namespaces.empty());
96  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
97  EXPECT_FALSE(manifest.online_whitelist_all);
98
99  base::hash_set<std::string> urls = manifest.explicit_urls;
100  const size_t kExpected = 5;
101  ASSERT_EQ(kExpected, urls.size());
102  EXPECT_TRUE(urls.find("http://www.foo.com/relative/one") != urls.end());
103  EXPECT_TRUE(urls.find("http://www.foo.com/two") != urls.end());
104  EXPECT_TRUE(urls.find("http://www.diff.com/three") != urls.end());
105  EXPECT_TRUE(urls.find("http://www.foo.com/relative/four") != urls.end());
106
107  // Wildcard is treated as a relative URL in explicit section.
108  EXPECT_TRUE(urls.find("http://www.foo.com/*") != urls.end());
109}
110
111TEST(AppCacheManifestParserTest, WhitelistUrls) {
112  Manifest manifest;
113  const GURL kUrl("http://www.bar.com");
114  const std::string kData("CACHE MANIFEST\r"
115    "NETWORK:\r"
116    "relative/one\r"
117    "# a comment\r"
118    "http://www.bar.com/two\r"
119    "HTTP://www.diff.com/three#strip\n\r"
120    "FALLBACK:\r"
121    "garbage\r"
122    "UNKNOWN:\r"
123    "http://www.bar.com/ignore\r"
124    "CACHE:\r"
125    "NETWORK:\r"
126    "https://www.wrongscheme.com\n"
127    "relative/four#stripref \t \r"
128    "http://www.five.com\r\n"
129    "*foo\r");
130
131  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
132  EXPECT_TRUE(manifest.explicit_urls.empty());
133  EXPECT_TRUE(manifest.fallback_namespaces.empty());
134  EXPECT_TRUE(manifest.intercept_namespaces.empty());
135  EXPECT_FALSE(manifest.online_whitelist_all);
136
137  const NamespaceVector& online = manifest.online_whitelist_namespaces;
138  const size_t kExpected = 6;
139  ASSERT_EQ(kExpected, online.size());
140  EXPECT_EQ(NETWORK_NAMESPACE, online[0].type);
141  EXPECT_FALSE(online[0].is_pattern);
142  EXPECT_TRUE(online[0].target_url.is_empty());
143  EXPECT_EQ(GURL("http://www.bar.com/relative/one"), online[0].namespace_url);
144  EXPECT_EQ(GURL("http://www.bar.com/two"), online[1].namespace_url);
145  EXPECT_EQ(GURL("http://www.diff.com/three"), online[2].namespace_url);
146  EXPECT_EQ(GURL("http://www.bar.com/relative/four"), online[3].namespace_url);
147  EXPECT_EQ(GURL("http://www.five.com"), online[4].namespace_url);
148  EXPECT_EQ(GURL("http://www.bar.com/*foo"), online[5].namespace_url);
149}
150
151TEST(AppCacheManifestParserTest, FallbackUrls) {
152  Manifest manifest;
153  const GURL kUrl("http://glorp.com");
154  const std::string kData("CACHE MANIFEST\r"
155    "# a comment\r"
156    "CACHE:\r"
157    "NETWORK:\r"
158    "UNKNOWN:\r"
159    "FALLBACK:\r"
160    "relative/one \t \t http://glorp.com/onefb  \t \r"
161    "*\r"
162    "https://glorp.com/wrong http://glorp.com/wrongfb\r"
163    "http://glorp.com/two#strip relative/twofb\r"
164    "HTTP://glorp.com/three relative/threefb#strip\n"
165    "http://glorp.com/three http://glorp.com/three-dup\r"
166    "http://glorp.com/solo \t \r\n"
167    "http://diff.com/ignore http://glorp.com/wronghost\r"
168    "http://glorp.com/wronghost http://diff.com/ohwell\r"
169    "relative/badscheme ftp://glorp.com/ignored\r"
170    "garbage\r\n"
171    "CACHE:\r"
172    "# only fallback urls in this test\r"
173    "FALLBACK:\n"
174    "relative/four#strip relative/fourfb#strip\r"
175    "http://www.glorp.com/notsame relative/skipped\r");
176
177  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
178  EXPECT_TRUE(manifest.explicit_urls.empty());
179  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
180  EXPECT_FALSE(manifest.online_whitelist_all);
181
182  const NamespaceVector& fallbacks = manifest.fallback_namespaces;
183  const size_t kExpected = 5;
184  ASSERT_EQ(kExpected, fallbacks.size());
185  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[0].type);
186  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[1].type);
187  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[2].type);
188  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[3].type);
189  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[4].type);
190  EXPECT_EQ(GURL("http://glorp.com/relative/one"),
191            fallbacks[0].namespace_url);
192  EXPECT_EQ(GURL("http://glorp.com/onefb"),
193            fallbacks[0].target_url);
194  EXPECT_EQ(GURL("http://glorp.com/two"),
195            fallbacks[1].namespace_url);
196  EXPECT_EQ(GURL("http://glorp.com/relative/twofb"),
197            fallbacks[1].target_url);
198  EXPECT_EQ(GURL("http://glorp.com/three"),
199            fallbacks[2].namespace_url);
200  EXPECT_EQ(GURL("http://glorp.com/relative/threefb"),
201            fallbacks[2].target_url);
202  EXPECT_EQ(GURL("http://glorp.com/three"),       // duplicates are stored
203            fallbacks[3].namespace_url);
204  EXPECT_EQ(GURL("http://glorp.com/three-dup"),
205            fallbacks[3].target_url);
206  EXPECT_EQ(GURL("http://glorp.com/relative/four"),
207            fallbacks[4].namespace_url);
208  EXPECT_EQ(GURL("http://glorp.com/relative/fourfb"),
209            fallbacks[4].target_url);
210
211  EXPECT_TRUE(manifest.intercept_namespaces.empty());
212}
213
214TEST(AppCacheManifestParserTest, FallbackUrlsWithPort) {
215  Manifest manifest;
216  const GURL kUrl("http://www.portme.com:1234");
217  const std::string kData("CACHE MANIFEST\r"
218    "FALLBACK:\r"
219    "http://www.portme.com:1234/one relative/onefb\r"
220    "HTTP://www.portme.com:9876/wrong http://www.portme.com:1234/ignore\r"
221    "http://www.portme.com:1234/stillwrong http://www.portme.com:42/boo\r"
222    "relative/two relative/twofb\r"
223    "http://www.portme.com:1234/three HTTP://www.portme.com:1234/threefb\r"
224    "http://www.portme.com/noport http://www.portme.com:1234/skipped\r"
225    "http://www.portme.com:1234/skipme http://www.portme.com/noport\r");
226
227  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
228  EXPECT_TRUE(manifest.explicit_urls.empty());
229  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
230  EXPECT_FALSE(manifest.online_whitelist_all);
231
232  const NamespaceVector& fallbacks = manifest.fallback_namespaces;
233  const size_t kExpected = 3;
234  ASSERT_EQ(kExpected, fallbacks.size());
235  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[0].type);
236  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[1].type);
237  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[2].type);
238  EXPECT_EQ(GURL("http://www.portme.com:1234/one"),
239            fallbacks[0].namespace_url);
240  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/onefb"),
241            fallbacks[0].target_url);
242  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/two"),
243            fallbacks[1].namespace_url);
244  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/twofb"),
245            fallbacks[1].target_url);
246  EXPECT_EQ(GURL("http://www.portme.com:1234/three"),
247            fallbacks[2].namespace_url);
248  EXPECT_EQ(GURL("http://www.portme.com:1234/threefb"),
249            fallbacks[2].target_url);
250
251  EXPECT_TRUE(manifest.intercept_namespaces.empty());
252}
253
254TEST(AppCacheManifestParserTest, InterceptUrls) {
255  Manifest manifest;
256  const GURL kUrl("http://www.portme.com:1234");
257  const std::string kData("CHROMIUM CACHE MANIFEST\r"
258    "CHROMIUM-INTERCEPT:\r"
259    "http://www.portme.com:1234/one return relative/int1\r"
260    "HTTP://www.portme.com:9/wrong return http://www.portme.com:1234/ignore\r"
261    "http://www.portme.com:1234/wrong return http://www.portme.com:9/boo\r"
262    "relative/two return relative/int2\r"
263    "relative/three wrong relative/threefb\r"
264    "http://www.portme.com:1234/three return HTTP://www.portme.com:1234/int3\r"
265    "http://www.portme.com/noport return http://www.portme.com:1234/skipped\r"
266    "http://www.portme.com:1234/skipme return http://www.portme.com/noport\r"
267    "relative/wrong/again missing/intercept_type\r");
268
269  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
270  EXPECT_TRUE(manifest.fallback_namespaces.empty());
271  EXPECT_TRUE(manifest.explicit_urls.empty());
272  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
273  EXPECT_FALSE(manifest.online_whitelist_all);
274
275  const NamespaceVector& intercepts = manifest.intercept_namespaces;
276  const size_t kExpected = 3;
277  ASSERT_EQ(kExpected, intercepts.size());
278  EXPECT_EQ(INTERCEPT_NAMESPACE, intercepts[0].type);
279  EXPECT_EQ(INTERCEPT_NAMESPACE, intercepts[1].type);
280  EXPECT_EQ(INTERCEPT_NAMESPACE, intercepts[2].type);
281  EXPECT_EQ(GURL("http://www.portme.com:1234/one"),
282            intercepts[0].namespace_url);
283  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/int1"),
284            intercepts[0].target_url);
285  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/two"),
286            intercepts[1].namespace_url);
287  EXPECT_EQ(GURL("http://www.portme.com:1234/relative/int2"),
288            intercepts[1].target_url);
289  EXPECT_EQ(GURL("http://www.portme.com:1234/three"),
290            intercepts[2].namespace_url);
291  EXPECT_EQ(GURL("http://www.portme.com:1234/int3"),
292            intercepts[2].target_url);
293}
294
295TEST(AppCacheManifestParserTest, ComboUrls) {
296  Manifest manifest;
297  const GURL kUrl("http://combo.com:42");
298  const std::string kData("CACHE MANIFEST\r"
299    "relative/explicit-1\r"
300    "# some comment\r"
301    "http://combo.com:99/explicit-2#strip\r"
302    "NETWORK:\r"
303    "http://combo.com/whitelist-1\r"
304    "HTTP://www.diff.com/whitelist-2#strip\r"
305    "*\r"
306    "CACHE:\n\r"
307    "http://www.diff.com/explicit-3\r"
308    "FALLBACK:\r"
309    "http://combo.com:42/fallback-1 http://combo.com:42/fallback-1b\r"
310    "relative/fallback-2 relative/fallback-2b\r"
311    "UNKNOWN:\r\n"
312    "http://combo.com/ignoreme\r"
313    "relative/still-ignored\r"
314    "NETWORK:\r\n"
315    "relative/whitelist-3#strip\r"
316    "http://combo.com:99/whitelist-4\r");
317  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
318  EXPECT_TRUE(manifest.online_whitelist_all);
319
320  base::hash_set<std::string> urls = manifest.explicit_urls;
321  size_t expected = 3;
322  ASSERT_EQ(expected, urls.size());
323  EXPECT_TRUE(urls.find("http://combo.com:42/relative/explicit-1") !=
324              urls.end());
325  EXPECT_TRUE(urls.find("http://combo.com:99/explicit-2") != urls.end());
326  EXPECT_TRUE(urls.find("http://www.diff.com/explicit-3") != urls.end());
327
328  const NamespaceVector& online = manifest.online_whitelist_namespaces;
329  expected = 4;
330  ASSERT_EQ(expected, online.size());
331  EXPECT_EQ(GURL("http://combo.com/whitelist-1"),
332                 online[0].namespace_url);
333  EXPECT_EQ(GURL("http://www.diff.com/whitelist-2"),
334                 online[1].namespace_url);
335  EXPECT_EQ(GURL("http://combo.com:42/relative/whitelist-3"),
336                 online[2].namespace_url);
337  EXPECT_EQ(GURL("http://combo.com:99/whitelist-4"),
338                 online[3].namespace_url);
339
340  const NamespaceVector& fallbacks = manifest.fallback_namespaces;
341  expected = 2;
342  ASSERT_EQ(expected, fallbacks.size());
343  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[0].type);
344  EXPECT_EQ(FALLBACK_NAMESPACE, fallbacks[1].type);
345  EXPECT_EQ(GURL("http://combo.com:42/fallback-1"),
346            fallbacks[0].namespace_url);
347  EXPECT_EQ(GURL("http://combo.com:42/fallback-1b"),
348            fallbacks[0].target_url);
349  EXPECT_EQ(GURL("http://combo.com:42/relative/fallback-2"),
350            fallbacks[1].namespace_url);
351  EXPECT_EQ(GURL("http://combo.com:42/relative/fallback-2b"),
352            fallbacks[1].target_url);
353
354  EXPECT_TRUE(manifest.intercept_namespaces.empty());
355}
356
357TEST(AppCacheManifestParserTest, UnusualUtf8) {
358  Manifest manifest;
359  const GURL kUrl("http://bad.com");
360  const std::string kData("CACHE MANIFEST\r"
361    "\xC0" "invalidutf8\r"
362    "nonbmp" "\xF1\x84\xAB\xBC\r");
363  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
364
365  base::hash_set<std::string> urls = manifest.explicit_urls;
366  EXPECT_TRUE(urls.find("http://bad.com/%EF%BF%BDinvalidutf8") != urls.end());
367  EXPECT_TRUE(urls.find("http://bad.com/nonbmp%F1%84%AB%BC") != urls.end());
368}
369
370TEST(AppCacheManifestParserTest, IgnoreAfterSpace) {
371  Manifest manifest;
372  const GURL kUrl("http://smorg.borg");
373  const std::string kData(
374    "CACHE MANIFEST\r"
375    "resource.txt this stuff after the white space should be ignored\r");
376  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
377
378  base::hash_set<std::string> urls = manifest.explicit_urls;
379  EXPECT_TRUE(urls.find("http://smorg.borg/resource.txt") != urls.end());
380}
381
382TEST(AppCacheManifestParserTest, DifferentOriginUrlWithSecureScheme) {
383  Manifest manifest;
384  const GURL kUrl("https://www.foo.com");
385  const std::string kData("CACHE MANIFEST\r"
386    "CACHE: \r"
387    "relative/secureschemesameorigin\r"
388    "https://www.foo.com/secureschemesameorigin\r"
389    "http://www.xyz.com/secureschemedifforigin\r"
390    "https://www.xyz.com/secureschemedifforigin\r");
391
392  EXPECT_TRUE(ParseManifest(kUrl, kData.c_str(), kData.length(), manifest));
393  EXPECT_TRUE(manifest.fallback_namespaces.empty());
394  EXPECT_TRUE(manifest.online_whitelist_namespaces.empty());
395
396  base::hash_set<std::string> urls = manifest.explicit_urls;
397  const size_t kExpected = 3;
398  ASSERT_EQ(kExpected, urls.size());
399  EXPECT_TRUE(urls.find("https://www.foo.com/relative/secureschemesameorigin")
400      != urls.end());
401  EXPECT_TRUE(urls.find("https://www.foo.com/secureschemesameorigin") !=
402      urls.end());
403  EXPECT_FALSE(urls.find("http://www.xyz.com/secureschemedifforigin") !=
404      urls.end());
405  EXPECT_TRUE(urls.find("https://www.xyz.com/secureschemedifforigin") !=
406      urls.end());
407}
408
409TEST(AppCacheManifestParserTest, PatternMatching) {
410  const GURL kUrl("http://foo.com/manifest");
411  const std::string kManifestBody(
412      "CACHE MANIFEST\r"
413      "CACHE: \r"
414      "http://foo.com/page.html\r"
415      "CHROMIUM-INTERCEPT:\r"
416      "http://foo.com/intercept_prefix return /prefix\r"
417      "http://foo.com/intercept_pattern return /pattern isPattern\r"
418      "http://foo.com/*/intercept_pattern?query return /pattern isPattern\r"
419      "FALLBACK:\r"
420      "http://foo.com/fallback_prefix  /prefix wrongAnnotation\r"
421      "http://foo.com/fallback_pattern* /pattern\tisPattern    \r"
422      "NETWORK:\r"
423      "*\r"
424      "isPattern\r"  // should not be interpretted as a pattern
425      "http://foo.com/network_pattern* isPattern\r");
426
427
428  Manifest manifest;
429  EXPECT_TRUE(ParseManifest(kUrl, kManifestBody.c_str(),
430                            kManifestBody.length(), manifest));
431  EXPECT_TRUE(manifest.online_whitelist_all);
432  EXPECT_EQ(1u, manifest.explicit_urls.size());
433  EXPECT_EQ(3u, manifest.intercept_namespaces.size());
434  EXPECT_EQ(2u, manifest.fallback_namespaces.size());
435  EXPECT_EQ(2u, manifest.online_whitelist_namespaces.size());
436  EXPECT_EQ(INTERCEPT_NAMESPACE, manifest.intercept_namespaces[0].type);
437  EXPECT_EQ(FALLBACK_NAMESPACE, manifest.fallback_namespaces[0].type);
438  EXPECT_EQ(NETWORK_NAMESPACE, manifest.online_whitelist_namespaces[0].type);
439  EXPECT_FALSE(manifest.intercept_namespaces[0].is_pattern);
440  EXPECT_TRUE(manifest.intercept_namespaces[1].is_pattern);
441  EXPECT_TRUE(manifest.intercept_namespaces[2].is_pattern);
442  EXPECT_FALSE(manifest.fallback_namespaces[0].is_pattern);
443  EXPECT_TRUE(manifest.fallback_namespaces[1].is_pattern);
444  EXPECT_FALSE(manifest.online_whitelist_namespaces[0].is_pattern);
445  EXPECT_TRUE(manifest.online_whitelist_namespaces[1].is_pattern);
446  EXPECT_EQ(
447      GURL("http://foo.com/*/intercept_pattern?query"),
448      manifest.intercept_namespaces[2].namespace_url);
449  EXPECT_EQ(
450      GURL("http://foo.com/pattern"),
451      manifest.intercept_namespaces[2].target_url);
452  EXPECT_EQ(
453      GURL("http://foo.com/fallback_pattern*"),
454      manifest.fallback_namespaces[1].namespace_url);
455  EXPECT_EQ(
456      GURL("http://foo.com/pattern"),
457      manifest.fallback_namespaces[1].target_url);
458  EXPECT_EQ(
459      GURL("http://foo.com/isPattern"),
460      manifest.online_whitelist_namespaces[0].namespace_url);
461  EXPECT_EQ(
462      GURL(),
463      manifest.online_whitelist_namespaces[0].target_url);
464  EXPECT_EQ(
465      GURL("http://foo.com/network_pattern*"),
466      manifest.online_whitelist_namespaces[1].namespace_url);
467  EXPECT_EQ(
468      GURL(),
469      manifest.online_whitelist_namespaces[1].target_url);
470}
471
472}  // namespace appcache
473