1 // Copyright (c) 2012 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 "components/content_settings/core/common/content_settings_pattern.h"
6
7 #include <vector>
8
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/string_util.h"
12 #include "components/content_settings/core/common/content_settings_pattern_parser.h"
13 #include "net/base/dns_util.h"
14 #include "net/base/net_util.h"
15 #include "url/gurl.h"
16
17 namespace {
18
19 // The component supports only one scheme for simplicity.
20 const char* non_port_non_domain_wildcard_scheme = NULL;
21
GetDefaultPort(const std::string & scheme)22 std::string GetDefaultPort(const std::string& scheme) {
23 if (scheme == url::kHttpScheme)
24 return "80";
25 if (scheme == url::kHttpsScheme)
26 return "443";
27 return std::string();
28 }
29
30 // Returns true if |sub_domain| is a sub domain or equls |domain|. E.g.
31 // "mail.google.com" is a sub domain of "google.com" but "evilhost.com" is not a
32 // subdomain of "host.com".
IsSubDomainOrEqual(const std::string & sub_domain,const std::string & domain)33 bool IsSubDomainOrEqual(const std::string& sub_domain,
34 const std::string& domain) {
35 // The empty string serves as wildcard. Each domain is a subdomain of the
36 // wildcard.
37 if (domain.empty())
38 return true;
39 const size_t match = sub_domain.rfind(domain);
40 if (match == std::string::npos ||
41 (match > 0 && sub_domain[match - 1] != '.') ||
42 (match + domain.length() != sub_domain.length())) {
43 return false;
44 }
45 return true;
46 }
47
48 // Compares two domain names.
CompareDomainNames(const std::string & str1,const std::string & str2)49 int CompareDomainNames(const std::string& str1, const std::string& str2) {
50 std::vector<std::string> domain_name1;
51 std::vector<std::string> domain_name2;
52
53 base::SplitString(str1, '.', &domain_name1);
54 base::SplitString(str2, '.', &domain_name2);
55
56 int i1 = static_cast<int>(domain_name1.size()) - 1;
57 int i2 = static_cast<int>(domain_name2.size()) - 1;
58 int rv;
59 while (i1 >= 0 && i2 >= 0) {
60 // domain names are stored in puny code. So it's fine to use the compare
61 // method.
62 rv = domain_name1[i1].compare(domain_name2[i2]);
63 if (rv != 0)
64 return rv;
65 --i1;
66 --i2;
67 }
68
69 if (i1 > i2)
70 return 1;
71
72 if (i1 < i2)
73 return -1;
74
75 // The domain names are identical.
76 return 0;
77 }
78
79 typedef ContentSettingsPattern::BuilderInterface BuilderInterface;
80
81 } // namespace
82
83 // ////////////////////////////////////////////////////////////////////////////
84 // ContentSettingsPattern::Builder
85 //
86 class ContentSettingsPattern::Builder :
87 public ContentSettingsPattern::BuilderInterface {
88 public:
89 explicit Builder(bool use_legacy_validate);
90 virtual ~Builder();
91
92 // BuilderInterface:
93 virtual BuilderInterface* WithPort(const std::string& port) OVERRIDE;
94 virtual BuilderInterface* WithPortWildcard() OVERRIDE;
95 virtual BuilderInterface* WithHost(const std::string& host) OVERRIDE;
96 virtual BuilderInterface* WithDomainWildcard() OVERRIDE;
97 virtual BuilderInterface* WithScheme(const std::string& scheme) OVERRIDE;
98 virtual BuilderInterface* WithSchemeWildcard() OVERRIDE;
99 virtual BuilderInterface* WithPath(const std::string& path) OVERRIDE;
100 virtual BuilderInterface* WithPathWildcard() OVERRIDE;
101 virtual BuilderInterface* Invalid() OVERRIDE;
102 virtual ContentSettingsPattern Build() OVERRIDE;
103
104 private:
105 // Canonicalizes the pattern parts so that they are ASCII only, either
106 // in original (if it was already ASCII) or punycode form. Returns true if
107 // the canonicalization was successful.
108 static bool Canonicalize(PatternParts* parts);
109
110 // Returns true when the pattern |parts| represent a valid pattern.
111 static bool Validate(const PatternParts& parts);
112
113 static bool LegacyValidate(const PatternParts& parts);
114
115 bool is_valid_;
116
117 bool use_legacy_validate_;
118
119 PatternParts parts_;
120
121 DISALLOW_COPY_AND_ASSIGN(Builder);
122 };
123
Builder(bool use_legacy_validate)124 ContentSettingsPattern::Builder::Builder(bool use_legacy_validate)
125 : is_valid_(true),
126 use_legacy_validate_(use_legacy_validate) {}
127
~Builder()128 ContentSettingsPattern::Builder::~Builder() {}
129
WithPort(const std::string & port)130 BuilderInterface* ContentSettingsPattern::Builder::WithPort(
131 const std::string& port) {
132 parts_.port = port;
133 parts_.is_port_wildcard = false;
134 return this;
135 }
136
WithPortWildcard()137 BuilderInterface* ContentSettingsPattern::Builder::WithPortWildcard() {
138 parts_.port = "";
139 parts_.is_port_wildcard = true;
140 return this;
141 }
142
WithHost(const std::string & host)143 BuilderInterface* ContentSettingsPattern::Builder::WithHost(
144 const std::string& host) {
145 parts_.host = host;
146 return this;
147 }
148
WithDomainWildcard()149 BuilderInterface* ContentSettingsPattern::Builder::WithDomainWildcard() {
150 parts_.has_domain_wildcard = true;
151 return this;
152 }
153
WithScheme(const std::string & scheme)154 BuilderInterface* ContentSettingsPattern::Builder::WithScheme(
155 const std::string& scheme) {
156 parts_.scheme = scheme;
157 parts_.is_scheme_wildcard = false;
158 return this;
159 }
160
WithSchemeWildcard()161 BuilderInterface* ContentSettingsPattern::Builder::WithSchemeWildcard() {
162 parts_.scheme = "";
163 parts_.is_scheme_wildcard = true;
164 return this;
165 }
166
WithPath(const std::string & path)167 BuilderInterface* ContentSettingsPattern::Builder::WithPath(
168 const std::string& path) {
169 parts_.path = path;
170 parts_.is_path_wildcard = false;
171 return this;
172 }
173
WithPathWildcard()174 BuilderInterface* ContentSettingsPattern::Builder::WithPathWildcard() {
175 parts_.path = "";
176 parts_.is_path_wildcard = true;
177 return this;
178 }
179
Invalid()180 BuilderInterface* ContentSettingsPattern::Builder::Invalid() {
181 is_valid_ = false;
182 return this;
183 }
184
Build()185 ContentSettingsPattern ContentSettingsPattern::Builder::Build() {
186 if (!is_valid_)
187 return ContentSettingsPattern();
188 if (!Canonicalize(&parts_))
189 return ContentSettingsPattern();
190 if (use_legacy_validate_) {
191 is_valid_ = LegacyValidate(parts_);
192 } else {
193 is_valid_ = Validate(parts_);
194 }
195 if (!is_valid_)
196 return ContentSettingsPattern();
197
198 // A pattern is invalid if canonicalization is not idempotent.
199 // This check is here because it should be checked no matter
200 // use_legacy_validate_ is.
201 PatternParts parts(parts_);
202 if (!Canonicalize(&parts))
203 return ContentSettingsPattern();
204 if (ContentSettingsPattern(parts_, true) !=
205 ContentSettingsPattern(parts, true)) {
206 return ContentSettingsPattern();
207 }
208
209 return ContentSettingsPattern(parts_, is_valid_);
210 }
211
212 // static
Canonicalize(PatternParts * parts)213 bool ContentSettingsPattern::Builder::Canonicalize(PatternParts* parts) {
214 // Canonicalize the scheme part.
215 const std::string scheme(base::StringToLowerASCII(parts->scheme));
216 parts->scheme = scheme;
217
218 if (parts->scheme == std::string(url::kFileScheme) &&
219 !parts->is_path_wildcard) {
220 GURL url(std::string(url::kFileScheme) +
221 std::string(url::kStandardSchemeSeparator) + parts->path);
222 parts->path = url.path();
223 }
224
225 // Canonicalize the host part.
226 const std::string host(parts->host);
227 url::CanonHostInfo host_info;
228 std::string canonicalized_host(net::CanonicalizeHost(host, &host_info));
229 if (host_info.IsIPAddress() && parts->has_domain_wildcard)
230 return false;
231 canonicalized_host = net::TrimEndingDot(canonicalized_host);
232
233 parts->host = "";
234 if ((host.find('*') == std::string::npos) &&
235 !canonicalized_host.empty()) {
236 // Valid host.
237 parts->host += canonicalized_host;
238 }
239 return true;
240 }
241
242 // static
Validate(const PatternParts & parts)243 bool ContentSettingsPattern::Builder::Validate(const PatternParts& parts) {
244 // Sanity checks first: {scheme, port} wildcards imply empty {scheme, port}.
245 if ((parts.is_scheme_wildcard && !parts.scheme.empty()) ||
246 (parts.is_port_wildcard && !parts.port.empty())) {
247 NOTREACHED();
248 return false;
249 }
250
251 // file:// URL patterns have an empty host and port.
252 if (parts.scheme == std::string(url::kFileScheme)) {
253 if (parts.has_domain_wildcard || !parts.host.empty() || !parts.port.empty())
254 return false;
255 if (parts.is_path_wildcard)
256 return parts.path.empty();
257 return (!parts.path.empty() &&
258 parts.path != "/" &&
259 parts.path.find("*") == std::string::npos);
260 }
261
262 // If the pattern is for an extension URL test if it is valid.
263 if (IsNonWildcardDomainNonPortScheme(parts.scheme) &&
264 parts.port.empty() &&
265 !parts.is_port_wildcard) {
266 return true;
267 }
268
269 // Non-file patterns are invalid if either the scheme, host or port part is
270 // empty.
271 if ((parts.scheme.empty() && !parts.is_scheme_wildcard) ||
272 (parts.host.empty() && !parts.has_domain_wildcard) ||
273 (parts.port.empty() && !parts.is_port_wildcard)) {
274 return false;
275 }
276
277 if (parts.host.find("*") != std::string::npos)
278 return false;
279
280 // Test if the scheme is supported or a wildcard.
281 if (!parts.is_scheme_wildcard &&
282 parts.scheme != std::string(url::kHttpScheme) &&
283 parts.scheme != std::string(url::kHttpsScheme)) {
284 return false;
285 }
286 return true;
287 }
288
289 // static
LegacyValidate(const PatternParts & parts)290 bool ContentSettingsPattern::Builder::LegacyValidate(
291 const PatternParts& parts) {
292 // If the pattern is for a "file-pattern" test if it is valid.
293 if (parts.scheme == std::string(url::kFileScheme) &&
294 !parts.is_scheme_wildcard &&
295 parts.host.empty() &&
296 parts.port.empty())
297 return true;
298
299 // If the pattern is for an extension URL test if it is valid.
300 if (IsNonWildcardDomainNonPortScheme(parts.scheme) &&
301 !parts.is_scheme_wildcard &&
302 !parts.host.empty() &&
303 !parts.has_domain_wildcard &&
304 parts.port.empty() &&
305 !parts.is_port_wildcard)
306 return true;
307
308 // Non-file patterns are invalid if either the scheme, host or port part is
309 // empty.
310 if ((!parts.is_scheme_wildcard) ||
311 (parts.host.empty() && !parts.has_domain_wildcard) ||
312 (!parts.is_port_wildcard))
313 return false;
314
315 // Test if the scheme is supported or a wildcard.
316 if (!parts.is_scheme_wildcard &&
317 parts.scheme != std::string(url::kHttpScheme) &&
318 parts.scheme != std::string(url::kHttpsScheme)) {
319 return false;
320 }
321 return true;
322 }
323
324 // ////////////////////////////////////////////////////////////////////////////
325 // ContentSettingsPattern::PatternParts
326 //
PatternParts()327 ContentSettingsPattern::PatternParts::PatternParts()
328 : is_scheme_wildcard(false),
329 has_domain_wildcard(false),
330 is_port_wildcard(false),
331 is_path_wildcard(false) {}
332
~PatternParts()333 ContentSettingsPattern::PatternParts::~PatternParts() {}
334
335 // ////////////////////////////////////////////////////////////////////////////
336 // ContentSettingsPattern
337 //
338
339 // The version of the pattern format implemented. Version 1 includes the
340 // following patterns:
341 // - [*.]domain.tld (matches domain.tld and all sub-domains)
342 // - host (matches an exact hostname)
343 // - a.b.c.d (matches an exact IPv4 ip)
344 // - [a:b:c:d:e:f:g:h] (matches an exact IPv6 ip)
345 // - file:///tmp/test.html (a complete URL without a host)
346 // Version 2 adds a resource identifier for plugins.
347 // TODO(jochen): update once this feature is no longer behind a flag.
348 const int ContentSettingsPattern::kContentSettingsPatternVersion = 1;
349
350 // static
CreateBuilder(bool validate)351 BuilderInterface* ContentSettingsPattern::CreateBuilder(
352 bool validate) {
353 return new Builder(validate);
354 }
355
356 // static
Wildcard()357 ContentSettingsPattern ContentSettingsPattern::Wildcard() {
358 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
359 ContentSettingsPattern::CreateBuilder(true));
360 builder->WithSchemeWildcard()->WithDomainWildcard()->WithPortWildcard()->
361 WithPathWildcard();
362 return builder->Build();
363 }
364
365 // static
FromURL(const GURL & url)366 ContentSettingsPattern ContentSettingsPattern::FromURL(
367 const GURL& url) {
368 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
369 ContentSettingsPattern::CreateBuilder(false));
370
371 const GURL* local_url = &url;
372 if (url.SchemeIsFileSystem() && url.inner_url()) {
373 local_url = url.inner_url();
374 }
375 if (local_url->SchemeIsFile()) {
376 builder->WithScheme(local_url->scheme())->WithPath(local_url->path());
377 } else {
378 // Please keep the order of the ifs below as URLs with an IP as host can
379 // also have a "http" scheme.
380 if (local_url->HostIsIPAddress()) {
381 builder->WithScheme(local_url->scheme())->WithHost(local_url->host());
382 } else if (local_url->SchemeIs(url::kHttpScheme)) {
383 builder->WithSchemeWildcard()->WithDomainWildcard()->WithHost(
384 local_url->host());
385 } else if (local_url->SchemeIs(url::kHttpsScheme)) {
386 builder->WithScheme(local_url->scheme())->WithDomainWildcard()->WithHost(
387 local_url->host());
388 } else {
389 // Unsupported scheme
390 }
391 if (local_url->port().empty()) {
392 if (local_url->SchemeIs(url::kHttpsScheme))
393 builder->WithPort(GetDefaultPort(url::kHttpsScheme));
394 else
395 builder->WithPortWildcard();
396 } else {
397 builder->WithPort(local_url->port());
398 }
399 }
400 return builder->Build();
401 }
402
403 // static
FromURLNoWildcard(const GURL & url)404 ContentSettingsPattern ContentSettingsPattern::FromURLNoWildcard(
405 const GURL& url) {
406 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
407 ContentSettingsPattern::CreateBuilder(false));
408
409 const GURL* local_url = &url;
410 if (url.SchemeIsFileSystem() && url.inner_url()) {
411 local_url = url.inner_url();
412 }
413 if (local_url->SchemeIsFile()) {
414 builder->WithScheme(local_url->scheme())->WithPath(local_url->path());
415 } else {
416 builder->WithScheme(local_url->scheme())->WithHost(local_url->host());
417 if (local_url->port().empty()) {
418 builder->WithPort(GetDefaultPort(local_url->scheme()));
419 } else {
420 builder->WithPort(local_url->port());
421 }
422 }
423 return builder->Build();
424 }
425
426 // static
FromString(const std::string & pattern_spec)427 ContentSettingsPattern ContentSettingsPattern::FromString(
428 const std::string& pattern_spec) {
429 scoped_ptr<ContentSettingsPattern::BuilderInterface> builder(
430 ContentSettingsPattern::CreateBuilder(false));
431 content_settings::PatternParser::Parse(pattern_spec,
432 builder.get());
433 return builder->Build();
434 }
435
436 // static
SetNonWildcardDomainNonPortScheme(const char * scheme)437 void ContentSettingsPattern::SetNonWildcardDomainNonPortScheme(
438 const char* scheme) {
439 DCHECK(scheme);
440 DCHECK(!non_port_non_domain_wildcard_scheme ||
441 non_port_non_domain_wildcard_scheme == scheme);
442 non_port_non_domain_wildcard_scheme = scheme;
443 }
444
445 // static
IsNonWildcardDomainNonPortScheme(const std::string & scheme)446 bool ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(
447 const std::string& scheme) {
448 DCHECK(non_port_non_domain_wildcard_scheme);
449 return scheme == non_port_non_domain_wildcard_scheme;
450 }
451
ContentSettingsPattern()452 ContentSettingsPattern::ContentSettingsPattern()
453 : is_valid_(false) {
454 }
455
ContentSettingsPattern(const PatternParts & parts,bool valid)456 ContentSettingsPattern::ContentSettingsPattern(
457 const PatternParts& parts,
458 bool valid)
459 : parts_(parts),
460 is_valid_(valid) {
461 }
462
Matches(const GURL & url) const463 bool ContentSettingsPattern::Matches(
464 const GURL& url) const {
465 // An invalid pattern matches nothing.
466 if (!is_valid_)
467 return false;
468
469 const GURL* local_url = &url;
470 if (url.SchemeIsFileSystem() && url.inner_url()) {
471 local_url = url.inner_url();
472 }
473
474 // Match the scheme part.
475 const std::string scheme(local_url->scheme());
476 if (!parts_.is_scheme_wildcard &&
477 parts_.scheme != scheme) {
478 return false;
479 }
480
481 // File URLs have no host. Matches if the pattern has the path wildcard set,
482 // or if the path in the URL is identical to the one in the pattern.
483 // For filesystem:file URLs, the path used is the filesystem type, so all
484 // filesystem:file:///temporary/... are equivalent.
485 // TODO(markusheintz): Content settings should be defined for all files on
486 // a machine. Unless there is a good use case for supporting paths for file
487 // patterns, stop supporting path for file patterns.
488 if (!parts_.is_scheme_wildcard && scheme == url::kFileScheme)
489 return parts_.is_path_wildcard ||
490 parts_.path == std::string(local_url->path());
491
492 // Match the host part.
493 const std::string host(net::TrimEndingDot(local_url->host()));
494 if (!parts_.has_domain_wildcard) {
495 if (parts_.host != host)
496 return false;
497 } else {
498 if (!IsSubDomainOrEqual(host, parts_.host))
499 return false;
500 }
501
502 // Ignore the port if the scheme doesn't support it.
503 if (IsNonWildcardDomainNonPortScheme(parts_.scheme))
504 return true;
505
506 // Match the port part.
507 std::string port(local_url->port());
508
509 // Use the default port if the port string is empty. GURL returns an empty
510 // string if no port at all was specified or if the default port was
511 // specified.
512 if (port.empty()) {
513 port = GetDefaultPort(scheme);
514 }
515
516 if (!parts_.is_port_wildcard &&
517 parts_.port != port ) {
518 return false;
519 }
520
521 return true;
522 }
523
MatchesAllHosts() const524 bool ContentSettingsPattern::MatchesAllHosts() const {
525 return parts_.has_domain_wildcard && parts_.host.empty();
526 }
527
ToString() const528 std::string ContentSettingsPattern::ToString() const {
529 if (IsValid())
530 return content_settings::PatternParser::ToString(parts_);
531 else
532 return std::string();
533 }
534
Compare(const ContentSettingsPattern & other) const535 ContentSettingsPattern::Relation ContentSettingsPattern::Compare(
536 const ContentSettingsPattern& other) const {
537 // Two invalid patterns are identical in the way they behave. They don't match
538 // anything and are represented as an empty string. So it's fair to treat them
539 // as identical.
540 if ((this == &other) ||
541 (!is_valid_ && !other.is_valid_))
542 return IDENTITY;
543
544 if (!is_valid_ && other.is_valid_)
545 return DISJOINT_ORDER_POST;
546 if (is_valid_ && !other.is_valid_)
547 return DISJOINT_ORDER_PRE;
548
549 // If either host, port or scheme are disjoint return immediately.
550 Relation host_relation = CompareHost(parts_, other.parts_);
551 if (host_relation == DISJOINT_ORDER_PRE ||
552 host_relation == DISJOINT_ORDER_POST)
553 return host_relation;
554
555 Relation port_relation = ComparePort(parts_, other.parts_);
556 if (port_relation == DISJOINT_ORDER_PRE ||
557 port_relation == DISJOINT_ORDER_POST)
558 return port_relation;
559
560 Relation scheme_relation = CompareScheme(parts_, other.parts_);
561 if (scheme_relation == DISJOINT_ORDER_PRE ||
562 scheme_relation == DISJOINT_ORDER_POST)
563 return scheme_relation;
564
565 if (host_relation != IDENTITY)
566 return host_relation;
567 if (port_relation != IDENTITY)
568 return port_relation;
569 return scheme_relation;
570 }
571
operator ==(const ContentSettingsPattern & other) const572 bool ContentSettingsPattern::operator==(
573 const ContentSettingsPattern& other) const {
574 return Compare(other) == IDENTITY;
575 }
576
operator !=(const ContentSettingsPattern & other) const577 bool ContentSettingsPattern::operator!=(
578 const ContentSettingsPattern& other) const {
579 return !(*this == other);
580 }
581
operator <(const ContentSettingsPattern & other) const582 bool ContentSettingsPattern::operator<(
583 const ContentSettingsPattern& other) const {
584 return Compare(other) < 0;
585 }
586
operator >(const ContentSettingsPattern & other) const587 bool ContentSettingsPattern::operator>(
588 const ContentSettingsPattern& other) const {
589 return Compare(other) > 0;
590 }
591
592 // static
CompareScheme(const ContentSettingsPattern::PatternParts & parts,const ContentSettingsPattern::PatternParts & other_parts)593 ContentSettingsPattern::Relation ContentSettingsPattern::CompareScheme(
594 const ContentSettingsPattern::PatternParts& parts,
595 const ContentSettingsPattern::PatternParts& other_parts) {
596 if (parts.is_scheme_wildcard && !other_parts.is_scheme_wildcard)
597 return ContentSettingsPattern::SUCCESSOR;
598 if (!parts.is_scheme_wildcard && other_parts.is_scheme_wildcard)
599 return ContentSettingsPattern::PREDECESSOR;
600
601 int result = parts.scheme.compare(other_parts.scheme);
602 if (result == 0)
603 return ContentSettingsPattern::IDENTITY;
604 if (result > 0)
605 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
606 return ContentSettingsPattern::DISJOINT_ORDER_POST;
607 }
608
609 // static
CompareHost(const ContentSettingsPattern::PatternParts & parts,const ContentSettingsPattern::PatternParts & other_parts)610 ContentSettingsPattern::Relation ContentSettingsPattern::CompareHost(
611 const ContentSettingsPattern::PatternParts& parts,
612 const ContentSettingsPattern::PatternParts& other_parts) {
613 if (!parts.has_domain_wildcard && !other_parts.has_domain_wildcard) {
614 // Case 1: No host starts with a wild card
615 int result = CompareDomainNames(parts.host, other_parts.host);
616 if (result == 0)
617 return ContentSettingsPattern::IDENTITY;
618 if (result < 0)
619 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
620 return ContentSettingsPattern::DISJOINT_ORDER_POST;
621 } else if (parts.has_domain_wildcard && !other_parts.has_domain_wildcard) {
622 // Case 2: |host| starts with a domain wildcard and |other_host| does not
623 // start with a domain wildcard.
624 // Examples:
625 // "this" host: [*.]google.com
626 // "other" host: google.com
627 //
628 // [*.]google.com
629 // mail.google.com
630 //
631 // [*.]mail.google.com
632 // google.com
633 //
634 // [*.]youtube.com
635 // google.de
636 //
637 // [*.]youtube.com
638 // mail.google.com
639 //
640 // *
641 // google.de
642 if (IsSubDomainOrEqual(other_parts.host, parts.host)) {
643 return ContentSettingsPattern::SUCCESSOR;
644 } else {
645 if (CompareDomainNames(parts.host, other_parts.host) < 0)
646 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
647 return ContentSettingsPattern::DISJOINT_ORDER_POST;
648 }
649 } else if (!parts.has_domain_wildcard && other_parts.has_domain_wildcard) {
650 // Case 3: |host| starts NOT with a domain wildcard and |other_host| starts
651 // with a domain wildcard.
652 if (IsSubDomainOrEqual(parts.host, other_parts.host)) {
653 return ContentSettingsPattern::PREDECESSOR;
654 } else {
655 if (CompareDomainNames(parts.host, other_parts.host) < 0)
656 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
657 return ContentSettingsPattern::DISJOINT_ORDER_POST;
658 }
659 } else if (parts.has_domain_wildcard && other_parts.has_domain_wildcard) {
660 // Case 4: |host| and |other_host| both start with a domain wildcard.
661 // Examples:
662 // [*.]google.com
663 // [*.]google.com
664 //
665 // [*.]google.com
666 // [*.]mail.google.com
667 //
668 // [*.]youtube.com
669 // [*.]google.de
670 //
671 // [*.]youtube.com
672 // [*.]mail.google.com
673 //
674 // [*.]youtube.com
675 // *
676 //
677 // *
678 // [*.]youtube.com
679 if (parts.host == other_parts.host) {
680 return ContentSettingsPattern::IDENTITY;
681 } else if (IsSubDomainOrEqual(other_parts.host, parts.host)) {
682 return ContentSettingsPattern::SUCCESSOR;
683 } else if (IsSubDomainOrEqual(parts.host, other_parts.host)) {
684 return ContentSettingsPattern::PREDECESSOR;
685 } else {
686 if (CompareDomainNames(parts.host, other_parts.host) < 0)
687 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
688 return ContentSettingsPattern::DISJOINT_ORDER_POST;
689 }
690 }
691
692 NOTREACHED();
693 return ContentSettingsPattern::IDENTITY;
694 }
695
696 // static
ComparePort(const ContentSettingsPattern::PatternParts & parts,const ContentSettingsPattern::PatternParts & other_parts)697 ContentSettingsPattern::Relation ContentSettingsPattern::ComparePort(
698 const ContentSettingsPattern::PatternParts& parts,
699 const ContentSettingsPattern::PatternParts& other_parts) {
700 if (parts.is_port_wildcard && !other_parts.is_port_wildcard)
701 return ContentSettingsPattern::SUCCESSOR;
702 if (!parts.is_port_wildcard && other_parts.is_port_wildcard)
703 return ContentSettingsPattern::PREDECESSOR;
704
705 int result = parts.port.compare(other_parts.port);
706 if (result == 0)
707 return ContentSettingsPattern::IDENTITY;
708 if (result > 0)
709 return ContentSettingsPattern::DISJOINT_ORDER_PRE;
710 return ContentSettingsPattern::DISJOINT_ORDER_POST;
711 }
712