Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(573)

Side by Side Diff: chrome/browser/content_settings/content_settings_pattern.cc

Issue 7885006: Moving ContentSettingsPattern from chrome/browser/content_settings to chrome/common. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Keeping up to date with trunk. Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "chrome/browser/content_settings/content_settings_pattern.h"
6
7 #include <vector>
8
9 #include "base/memory/scoped_ptr.h"
10 #include "base/string_split.h"
11 #include "base/string_util.h"
12 #include "chrome/browser/content_settings/content_settings_pattern_parser.h"
13 #include "chrome/common/url_constants.h"
14 #include "net/base/dns_util.h"
15 #include "net/base/net_util.h"
16 #include "googleurl/src/gurl.h"
17 #include "googleurl/src/url_canon.h"
18
19 namespace {
20
21 std::string GetDefaultPort(const std::string& scheme) {
22 if (scheme == chrome::kHttpScheme)
23 return "80";
24 if (scheme == chrome::kHttpsScheme)
25 return "443";
26 return "";
27 }
28
29 // Returns true if |sub_domain| is a sub domain or equls |domain|. E.g.
30 // "mail.google.com" is a sub domain of "google.com" but "evilhost.com" is not a
31 // subdomain of "host.com".
32 bool IsSubDomainOrEqual(const std::string& sub_domain,
33 const std::string& domain) {
34 // The empty string serves as wildcard. Each domain is a subdomain of the
35 // wildcard.
36 if (domain.empty())
37 return true;
38 const size_t match = sub_domain.rfind(domain);
39 if (match == std::string::npos ||
40 (match > 0 && sub_domain[match - 1] != '.') ||
41 (match + domain.length() != sub_domain.length())) {
42 return false;
43 }
44 return true;
45 }
46
47 // Compares two domain names.
48 int CompareDomainNames(const std::string& str1, const std::string& str2) {
49 std::vector<std::string> domain_name1;
50 std::vector<std::string> domain_name2;
51
52 base::SplitString(str1, '.', &domain_name1);
53 base::SplitString(str2, '.', &domain_name2);
54
55 int i1 = domain_name1.size() - 1;
56 int i2 = domain_name2.size() - 1;
57 int rv;
58 while (i1 >= 0 && i2 >= 0) {
59 // domain names are stored in puny code. So it's fine to use the compare
60 // method.
61 rv = domain_name1[i1].compare(domain_name2[i2]);
62 if (rv != 0)
63 return rv;
64 --i1;
65 --i2;
66 }
67
68 if (i1 > i2)
69 return 1;
70
71 if (i1 < i2)
72 return -1;
73
74 // The domain names are identical.
75 return 0;
76 }
77
78 typedef ContentSettingsPattern::BuilderInterface BuilderInterface;
79
80 } // namespace
81
82 // ////////////////////////////////////////////////////////////////////////////
83 // ContentSettingsPattern::Builder
84 //
85 ContentSettingsPattern::Builder::Builder(bool use_legacy_validate)
86 : is_valid_(true),
87 use_legacy_validate_(use_legacy_validate) {}
88
89 ContentSettingsPattern::Builder::~Builder() {}
90
91 BuilderInterface* ContentSettingsPattern::Builder::WithPort(
92 const std::string& port) {
93 parts_.port = port;
94 parts_.is_port_wildcard = false;
95 return this;
96 }
97
98 BuilderInterface* ContentSettingsPattern::Builder::WithPortWildcard() {
99 parts_.port = "";
100 parts_.is_port_wildcard = true;
101 return this;
102 }
103
104 BuilderInterface* ContentSettingsPattern::Builder::WithHost(
105 const std::string& host) {
106 parts_.host = host;
107 return this;
108 }
109
110 BuilderInterface* ContentSettingsPattern::Builder::WithDomainWildcard() {
111 parts_.has_domain_wildcard = true;
112 return this;
113 }
114
115 BuilderInterface* ContentSettingsPattern::Builder::WithScheme(
116 const std::string& scheme) {
117 parts_.scheme = scheme;
118 parts_.is_scheme_wildcard = false;
119 return this;
120 }
121
122 BuilderInterface* ContentSettingsPattern::Builder::WithSchemeWildcard() {
123 parts_.scheme = "";
124 parts_.is_scheme_wildcard = true;
125 return this;
126 }
127
128 BuilderInterface* ContentSettingsPattern::Builder::WithPath(
129 const std::string& path) {
130 parts_.path = path;
131 return this;
132 }
133
134 BuilderInterface* ContentSettingsPattern::Builder::Invalid() {
135 is_valid_ = false;
136 return this;
137 }
138
139 ContentSettingsPattern ContentSettingsPattern::Builder::Build() {
140 if (!is_valid_)
141 return ContentSettingsPattern();
142 Canonicalize(&parts_);
143 if (use_legacy_validate_) {
144 is_valid_ = LegacyValidate(parts_);
145 } else {
146 is_valid_ = Validate(parts_);
147 }
148 return ContentSettingsPattern(parts_, is_valid_);
149 }
150
151 // static
152 void ContentSettingsPattern::Builder::Canonicalize(PatternParts* parts) {
153 // Canonicalize the scheme part.
154 const std::string scheme(StringToLowerASCII(parts->scheme));
155 parts->scheme = scheme;
156
157 if (parts->scheme == std::string(chrome::kFileScheme)) {
158 GURL url(std::string(chrome::kFileScheme) +
159 std::string(chrome::kStandardSchemeSeparator) + parts->path);
160 parts->path = url.path();
161 }
162
163 // Canonicalize the host part.
164 const std::string host(parts->host);
165 url_canon::CanonHostInfo host_info;
166 std::string canonicalized_host(net::CanonicalizeHost(host, &host_info));
167 canonicalized_host = net::TrimEndingDot(canonicalized_host);
168
169 parts->host = "";
170 if ((host.find('*') == std::string::npos) &&
171 !canonicalized_host.empty()) {
172 // Valid host.
173 parts->host += canonicalized_host;
174 }
175 }
176
177 // static
178 bool ContentSettingsPattern::Builder::Validate(const PatternParts& parts) {
179 // If the pattern is for a "file-pattern" test if it is valid.
180 if (parts.scheme == std::string(chrome::kFileScheme) &&
181 !parts.is_scheme_wildcard &&
182 parts.host.empty() &&
183 parts.port.empty())
184 return true;
185
186 // If the pattern is for an extension URL test if it is valid.
187 if (parts.scheme == std::string(chrome::kExtensionScheme) &&
188 !parts.is_scheme_wildcard &&
189 !parts.host.empty() &&
190 !parts.has_domain_wildcard &&
191 parts.port.empty() &&
192 !parts.is_port_wildcard)
193 return true;
194
195 // Non-file patterns are invalid if either the scheme, host or port part is
196 // empty.
197 if ((parts.scheme.empty() && !parts.is_scheme_wildcard) ||
198 (parts.host.empty() && !parts.has_domain_wildcard) ||
199 (parts.port.empty() && !parts.is_port_wildcard))
200 return false;
201
202 // Test if the scheme is supported or a wildcard.
203 if (!parts.is_scheme_wildcard &&
204 parts.scheme != std::string(chrome::kHttpScheme) &&
205 parts.scheme != std::string(chrome::kHttpsScheme)) {
206 return false;
207 }
208 return true;
209 }
210
211 // static
212 bool ContentSettingsPattern::Builder::LegacyValidate(
213 const PatternParts& parts) {
214 // If the pattern is for a "file-pattern" test if it is valid.
215 if (parts.scheme == std::string(chrome::kFileScheme) &&
216 !parts.is_scheme_wildcard &&
217 parts.host.empty() &&
218 parts.port.empty())
219 return true;
220
221 // If the pattern is for an extension URL test if it is valid.
222 if (parts.scheme == std::string(chrome::kExtensionScheme) &&
223 !parts.is_scheme_wildcard &&
224 !parts.host.empty() &&
225 !parts.has_domain_wildcard &&
226 parts.port.empty() &&
227 !parts.is_port_wildcard)
228 return true;
229
230 // Non-file patterns are invalid if either the scheme, host or port part is
231 // empty.
232 if ((!parts.is_scheme_wildcard) ||
233 (parts.host.empty() && !parts.has_domain_wildcard) ||
234 (!parts.is_port_wildcard))
235 return false;
236
237 // Test if the scheme is supported or a wildcard.
238 if (!parts.is_scheme_wildcard &&
239 parts.scheme != std::string(chrome::kHttpScheme) &&
240 parts.scheme != std::string(chrome::kHttpsScheme)) {
241 return false;
242 }
243 return true;
244 }
245
246 // ////////////////////////////////////////////////////////////////////////////
247 // ContentSettingsPattern::PatternParts
248 //
249 ContentSettingsPattern::PatternParts::PatternParts()
250 : is_scheme_wildcard(false),
251 has_domain_wildcard(false),
252 is_port_wildcard(false) {}
253
254 ContentSettingsPattern::PatternParts::~PatternParts() {}
255
256 // ////////////////////////////////////////////////////////////////////////////
257 // ContentSettingsPattern
258 //
259
260 // The version of the pattern format implemented. Version 1 includes the
261 // following patterns:
262 // - [*.]domain.tld (matches domain.tld and all sub-domains)
263 // - host (matches an exact hostname)
264 // - a.b.c.d (matches an exact IPv4 ip)
265 // - [a:b:c:d:e:f:g:h] (matches an exact IPv6 ip)
266 // - file:///tmp/test.html (a complete URL without a host)
267 // Version 2 adds a resource identifier for plugins.
268 // TODO(jochen): update once this feature is no longer behind a flag.
269 const int ContentSettingsPattern::kContentSettingsPatternVersion = 1;
270
271 // TODO(markusheintz): These two constants were moved to the Pattern Parser.
272 // Remove once the dependency of the ContentSettingsBaseProvider is removed.
273 const char* ContentSettingsPattern::kDomainWildcard = "[*.]";
274 const size_t ContentSettingsPattern::kDomainWildcardLength = 4;
275
276 // static
277 BuilderInterface* ContentSettingsPattern::CreateBuilder(
278 bool validate) {
279 return new Builder(validate);
280 }
281
282 // static
283 ContentSettingsPattern ContentSettingsPattern::FromURL(
284 const GURL& url) {
285 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
286 ContentSettingsPattern::CreateBuilder(false));
287
288 if (url.SchemeIsFile()) {
289 builder->WithScheme(url.scheme())->WithPath(url.path());
290 } else {
291 // Please keep the order of the ifs below as URLs with an IP as host can
292 // also have a "http" scheme.
293 if (url.HostIsIPAddress()) {
294 builder->WithScheme(url.scheme())->WithHost(url.host());
295 } else if (url.SchemeIs(chrome::kHttpScheme)) {
296 builder->WithSchemeWildcard()->WithDomainWildcard()->WithHost(url.host());
297 } else if (url.SchemeIs(chrome::kHttpsScheme)) {
298 builder->WithScheme(url.scheme())->WithDomainWildcard()->WithHost(
299 url.host());
300 } else {
301 // Unsupported scheme
302 }
303 if (url.port().empty()) {
304 if (url.SchemeIs(chrome::kHttpsScheme))
305 builder->WithPort(GetDefaultPort(chrome::kHttpsScheme));
306 else
307 builder->WithPortWildcard();
308 } else {
309 builder->WithPort(url.port());
310 }
311 }
312 return builder->Build();
313 }
314
315 // static
316 ContentSettingsPattern ContentSettingsPattern::FromURLNoWildcard(
317 const GURL& url) {
318 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
319 ContentSettingsPattern::CreateBuilder(false));
320
321 if (url.SchemeIsFile()) {
322 builder->WithScheme(url.scheme())->WithPath(url.path());
323 } else {
324 builder->WithScheme(url.scheme())->WithHost(url.host());
325 if (url.port().empty()) {
326 builder->WithPort(GetDefaultPort(url.scheme()));
327 } else {
328 builder->WithPort(url.port());
329 }
330 }
331 return builder->Build();
332 }
333
334 // static
335 ContentSettingsPattern ContentSettingsPattern::FromString(
336 const std::string& pattern_spec) {
337 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
338 ContentSettingsPattern::CreateBuilder(false));
339 content_settings::PatternParser::Parse(pattern_spec, builder.get());
340 return builder->Build();
341 }
342
343 // static
344 ContentSettingsPattern ContentSettingsPattern::LegacyFromString(
345 const std::string& pattern_spec) {
346 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
347 ContentSettingsPattern::CreateBuilder(true));
348 content_settings::PatternParser::Parse(pattern_spec, builder.get());
349 return builder->Build();
350 }
351
352 // static
353 ContentSettingsPattern ContentSettingsPattern::Wildcard() {
354 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
355 ContentSettingsPattern::CreateBuilder(true));
356 builder->WithSchemeWildcard()->WithDomainWildcard()->WithPortWildcard();
357 return builder->Build();
358 }
359
360 ContentSettingsPattern::ContentSettingsPattern()
361 : is_valid_(false) {
362 }
363
364 ContentSettingsPattern::ContentSettingsPattern(
365 const PatternParts& parts,
366 bool valid)
367 : parts_(parts),
368 is_valid_(valid) {
369 }
370
371 bool ContentSettingsPattern::Matches(
372 const GURL& url) const {
373 // An invalid pattern matches nothing.
374 if (!is_valid_)
375 return false;
376
377 // Match the scheme part.
378 const std::string scheme(url.scheme());
379 if (!parts_.is_scheme_wildcard &&
380 parts_.scheme != scheme) {
381 return false;
382 }
383
384 // File URLs have no host. For file URLs check if the url path matches the
385 // path in the pattern.
386 // TODO(markusheintz): This should change in the future. There should be only
387 // one setting for all file URLs. So the path should be ignored.
388 if (!parts_.is_scheme_wildcard &&
389 scheme == std::string(chrome::kFileScheme)) {
390 if (parts_.path == std::string(url.path()))
391 return true;
392 return false;
393 }
394
395 // Match the host part.
396 const std::string host(net::TrimEndingDot(url.host()));
397 if (!parts_.has_domain_wildcard) {
398 if (parts_.host != host)
399 return false;
400 } else {
401 if (!IsSubDomainOrEqual(host, parts_.host))
402 return false;
403 }
404
405 // For chrome extensions URLs ignore the port.
406 if (parts_.scheme == std::string(chrome::kExtensionScheme))
407 return true;
408
409 // Match the port part.
410 std::string port(url.port());
411
412 // Use the default port if the port string is empty. GURL returns an empty
413 // string if no port at all was specified or if the default port was
414 // specified.
415 if (port.empty()) {
416 port = GetDefaultPort(scheme);
417 }
418
419 if (!parts_.is_port_wildcard &&
420 parts_.port != port ) {
421 return false;
422 }
423
424 return true;
425 }
426
427 const std::string ContentSettingsPattern::ToString() const {
428 if (IsValid())
429 return content_settings::PatternParser::ToString(parts_);
430 else
431 return "";
432 }
433
434 ContentSettingsPattern::Relation ContentSettingsPattern::Compare(
435 const ContentSettingsPattern& other) const {
436 // Two invalid patterns are identical in the way they behave. They don't match
437 // anything and are represented as an empty string. So it's fair to treat them
438 // as identical.
439 if ((this == &other) ||
440 (!is_valid_ && !other.is_valid_))
441 return IDENTITY;
442
443 if (!is_valid_ && other.is_valid_)
444 return DISJOINT_ORDER_POST;
445 if (is_valid_ && !other.is_valid_)
446 return DISJOINT_ORDER_PRE;
447
448 // If either host, port or scheme are disjoint return immediately.
449 Relation host_relation = CompareHost(parts_, other.parts_);
450 if (host_relation == DISJOINT_ORDER_PRE ||
451 host_relation == DISJOINT_ORDER_POST)
452 return host_relation;
453
454 Relation port_relation = ComparePort(parts_, other.parts_);
455 if (port_relation == DISJOINT_ORDER_PRE ||
456 port_relation == DISJOINT_ORDER_POST)
457 return port_relation;
458
459 Relation scheme_relation = CompareScheme(parts_, other.parts_);
460 if (scheme_relation == DISJOINT_ORDER_PRE ||
461 scheme_relation == DISJOINT_ORDER_POST)
462 return scheme_relation;
463
464 if (host_relation != IDENTITY)
465 return host_relation;
466 if (port_relation != IDENTITY)
467 return port_relation;
468 return scheme_relation;
469 }
470
471 bool ContentSettingsPattern::operator==(
472 const ContentSettingsPattern& other) const {
473 return Compare(other) == IDENTITY;
474 }
475
476 bool ContentSettingsPattern::operator!=(
477 const ContentSettingsPattern& other) const {
478 return !(*this == other);
479 }
480
481 bool ContentSettingsPattern::operator<(
482 const ContentSettingsPattern& other) const {
483 return Compare(other) < 0;
484 }
485
486 bool ContentSettingsPattern::operator>(
487 const ContentSettingsPattern& other) const {
488 return Compare(other) > 0;
489 }
490
491 // static
492 ContentSettingsPattern::Relation ContentSettingsPattern::CompareHost(
493 const ContentSettingsPattern::PatternParts& parts,
494 const ContentSettingsPattern::PatternParts& other_parts) {
495 if (!parts.has_domain_wildcard && !other_parts.has_domain_wildcard) {
496 // Case 1: No host starts with a wild card
497 int result = CompareDomainNames(parts.host, other_parts.host);
498 if (result == 0)
499 return ContentSettingsPattern::IDENTITY;
500 if (result < 0)
501 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
502 return ContentSettingsPattern::DISJOINT_ORDER_POST;
503 } else if (parts.has_domain_wildcard && !other_parts.has_domain_wildcard) {
504 // Case 2: |host| starts with a domain wildcard and |other_host| does not
505 // start with a domain wildcard.
506 // Examples:
507 // "this" host: [*.]google.com
508 // "other" host: google.com
509 //
510 // [*.]google.com
511 // mail.google.com
512 //
513 // [*.]mail.google.com
514 // google.com
515 //
516 // [*.]youtube.com
517 // google.de
518 //
519 // [*.]youtube.com
520 // mail.google.com
521 //
522 // *
523 // google.de
524 if (IsSubDomainOrEqual(other_parts.host, parts.host)) {
525 return ContentSettingsPattern::SUCCESSOR;
526 } else {
527 if (CompareDomainNames(parts.host, other_parts.host) < 0)
528 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
529 return ContentSettingsPattern::DISJOINT_ORDER_POST;
530 }
531 } else if (!parts.has_domain_wildcard && other_parts.has_domain_wildcard) {
532 // Case 3: |host| starts NOT with a domain wildcard and |other_host| starts
533 // with a domain wildcard.
534 if (IsSubDomainOrEqual(parts.host, other_parts.host)) {
535 return ContentSettingsPattern::PREDECESSOR;
536 } else {
537 if (CompareDomainNames(parts.host, other_parts.host) < 0)
538 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
539 return ContentSettingsPattern::DISJOINT_ORDER_POST;
540 }
541 } else if (parts.has_domain_wildcard && other_parts.has_domain_wildcard) {
542 // Case 4: |host| and |other_host| both start with a domain wildcard.
543 // Examples:
544 // [*.]google.com
545 // [*.]google.com
546 //
547 // [*.]google.com
548 // [*.]mail.google.com
549 //
550 // [*.]youtube.com
551 // [*.]google.de
552 //
553 // [*.]youtube.com
554 // [*.]mail.google.com
555 //
556 // [*.]youtube.com
557 // *
558 //
559 // *
560 // [*.]youtube.com
561 if (parts.host == other_parts.host) {
562 return ContentSettingsPattern::IDENTITY;
563 } else if (IsSubDomainOrEqual(other_parts.host, parts.host)) {
564 return ContentSettingsPattern::SUCCESSOR;
565 } else if (IsSubDomainOrEqual(parts.host, other_parts.host)) {
566 return ContentSettingsPattern::PREDECESSOR;
567 } else {
568 if (CompareDomainNames(parts.host, other_parts.host) < 0)
569 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
570 return ContentSettingsPattern::DISJOINT_ORDER_POST;
571 }
572 }
573
574 NOTREACHED();
575 return ContentSettingsPattern::IDENTITY;
576 }
577
578 // static
579 ContentSettingsPattern::Relation ContentSettingsPattern::CompareScheme(
580 const ContentSettingsPattern::PatternParts& parts,
581 const ContentSettingsPattern::PatternParts& other_parts) {
582 if (parts.is_scheme_wildcard && !other_parts.is_scheme_wildcard)
583 return ContentSettingsPattern::SUCCESSOR;
584 if (!parts.is_scheme_wildcard && other_parts.is_scheme_wildcard)
585 return ContentSettingsPattern::PREDECESSOR;
586
587 int result = parts.scheme.compare(other_parts.scheme);
588 if (result == 0)
589 return ContentSettingsPattern::IDENTITY;
590 if (result > 0)
591 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
592 return ContentSettingsPattern::DISJOINT_ORDER_POST;
593 }
594
595 // static
596 ContentSettingsPattern::Relation ContentSettingsPattern::ComparePort(
597 const ContentSettingsPattern::PatternParts& parts,
598 const ContentSettingsPattern::PatternParts& other_parts) {
599 if (parts.is_port_wildcard && !other_parts.is_port_wildcard)
600 return ContentSettingsPattern::SUCCESSOR;
601 if (!parts.is_port_wildcard && other_parts.is_port_wildcard)
602 return ContentSettingsPattern::PREDECESSOR;
603
604 int result = parts.port.compare(other_parts.port);
605 if (result == 0)
606 return ContentSettingsPattern::IDENTITY;
607 if (result > 0)
608 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
609 return ContentSettingsPattern::DISJOINT_ORDER_POST;
610 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698