Home | History | Annotate | Download | only in common
      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 
     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".
     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.
     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 
    124 ContentSettingsPattern::Builder::Builder(bool use_legacy_validate)
    125     : is_valid_(true),
    126       use_legacy_validate_(use_legacy_validate) {}
    127 
    128 ContentSettingsPattern::Builder::~Builder() {}
    129 
    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 
    137 BuilderInterface* ContentSettingsPattern::Builder::WithPortWildcard() {
    138   parts_.port = "";
    139   parts_.is_port_wildcard = true;
    140   return this;
    141 }
    142 
    143 BuilderInterface* ContentSettingsPattern::Builder::WithHost(
    144     const std::string& host) {
    145   parts_.host = host;
    146   return this;
    147 }
    148 
    149 BuilderInterface* ContentSettingsPattern::Builder::WithDomainWildcard() {
    150   parts_.has_domain_wildcard = true;
    151   return this;
    152 }
    153 
    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 
    161 BuilderInterface* ContentSettingsPattern::Builder::WithSchemeWildcard() {
    162   parts_.scheme = "";
    163   parts_.is_scheme_wildcard = true;
    164   return this;
    165 }
    166 
    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 
    174 BuilderInterface* ContentSettingsPattern::Builder::WithPathWildcard() {
    175   parts_.path = "";
    176   parts_.is_path_wildcard = true;
    177   return this;
    178 }
    179 
    180 BuilderInterface* ContentSettingsPattern::Builder::Invalid() {
    181   is_valid_ = false;
    182   return this;
    183 }
    184 
    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
    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
    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
    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 //
    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 
    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
    351 BuilderInterface* ContentSettingsPattern::CreateBuilder(
    352     bool validate) {
    353   return new Builder(validate);
    354 }
    355 
    356 // static
    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
    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
    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
    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
    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
    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 
    452 ContentSettingsPattern::ContentSettingsPattern()
    453   : is_valid_(false) {
    454 }
    455 
    456 ContentSettingsPattern::ContentSettingsPattern(
    457     const PatternParts& parts,
    458     bool valid)
    459     : parts_(parts),
    460       is_valid_(valid) {
    461 }
    462 
    463 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 
    524 bool ContentSettingsPattern::MatchesAllHosts() const {
    525   return parts_.has_domain_wildcard && parts_.host.empty();
    526 }
    527 
    528 std::string ContentSettingsPattern::ToString() const {
    529   if (IsValid())
    530     return content_settings::PatternParser::ToString(parts_);
    531   else
    532     return std::string();
    533 }
    534 
    535 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 
    572 bool ContentSettingsPattern::operator==(
    573     const ContentSettingsPattern& other) const {
    574   return Compare(other) == IDENTITY;
    575 }
    576 
    577 bool ContentSettingsPattern::operator!=(
    578     const ContentSettingsPattern& other) const {
    579   return !(*this == other);
    580 }
    581 
    582 bool ContentSettingsPattern::operator<(
    583     const ContentSettingsPattern& other) const {
    584   return Compare(other) < 0;
    585 }
    586 
    587 bool ContentSettingsPattern::operator>(
    588     const ContentSettingsPattern& other) const {
    589   return Compare(other) > 0;
    590 }
    591 
    592 // static
    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
    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
    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