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