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
17namespace {
18
19// The component supports only one scheme for simplicity.
20const char* non_port_non_domain_wildcard_scheme = NULL;
21
22std::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".
33bool 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.
49int 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
79typedef ContentSettingsPattern::BuilderInterface BuilderInterface;
80
81}  // namespace
82
83// ////////////////////////////////////////////////////////////////////////////
84// ContentSettingsPattern::Builder
85//
86class 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
124ContentSettingsPattern::Builder::Builder(bool use_legacy_validate)
125    : is_valid_(true),
126      use_legacy_validate_(use_legacy_validate) {}
127
128ContentSettingsPattern::Builder::~Builder() {}
129
130BuilderInterface* ContentSettingsPattern::Builder::WithPort(
131    const std::string& port) {
132  parts_.port = port;
133  parts_.is_port_wildcard = false;
134  return this;
135}
136
137BuilderInterface* ContentSettingsPattern::Builder::WithPortWildcard() {
138  parts_.port = "";
139  parts_.is_port_wildcard = true;
140  return this;
141}
142
143BuilderInterface* ContentSettingsPattern::Builder::WithHost(
144    const std::string& host) {
145  parts_.host = host;
146  return this;
147}
148
149BuilderInterface* ContentSettingsPattern::Builder::WithDomainWildcard() {
150  parts_.has_domain_wildcard = true;
151  return this;
152}
153
154BuilderInterface* ContentSettingsPattern::Builder::WithScheme(
155    const std::string& scheme) {
156  parts_.scheme = scheme;
157  parts_.is_scheme_wildcard = false;
158  return this;
159}
160
161BuilderInterface* ContentSettingsPattern::Builder::WithSchemeWildcard() {
162  parts_.scheme = "";
163  parts_.is_scheme_wildcard = true;
164  return this;
165}
166
167BuilderInterface* ContentSettingsPattern::Builder::WithPath(
168    const std::string& path) {
169  parts_.path = path;
170  parts_.is_path_wildcard = false;
171  return this;
172}
173
174BuilderInterface* ContentSettingsPattern::Builder::WithPathWildcard() {
175  parts_.path = "";
176  parts_.is_path_wildcard = true;
177  return this;
178}
179
180BuilderInterface* ContentSettingsPattern::Builder::Invalid() {
181  is_valid_ = false;
182  return this;
183}
184
185ContentSettingsPattern 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
213bool 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
243bool 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
290bool 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//
327ContentSettingsPattern::PatternParts::PatternParts()
328        : is_scheme_wildcard(false),
329          has_domain_wildcard(false),
330          is_port_wildcard(false),
331          is_path_wildcard(false) {}
332
333ContentSettingsPattern::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.
348const int ContentSettingsPattern::kContentSettingsPatternVersion = 1;
349
350// static
351BuilderInterface* ContentSettingsPattern::CreateBuilder(
352    bool validate) {
353  return new Builder(validate);
354}
355
356// static
357ContentSettingsPattern 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
366ContentSettingsPattern 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
404ContentSettingsPattern 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
427ContentSettingsPattern 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
437void 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
446bool 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
452ContentSettingsPattern::ContentSettingsPattern()
453  : is_valid_(false) {
454}
455
456ContentSettingsPattern::ContentSettingsPattern(
457    const PatternParts& parts,
458    bool valid)
459    : parts_(parts),
460      is_valid_(valid) {
461}
462
463bool 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
524bool ContentSettingsPattern::MatchesAllHosts() const {
525  return parts_.has_domain_wildcard && parts_.host.empty();
526}
527
528std::string ContentSettingsPattern::ToString() const {
529  if (IsValid())
530    return content_settings::PatternParser::ToString(parts_);
531  else
532    return std::string();
533}
534
535ContentSettingsPattern::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
572bool ContentSettingsPattern::operator==(
573    const ContentSettingsPattern& other) const {
574  return Compare(other) == IDENTITY;
575}
576
577bool ContentSettingsPattern::operator!=(
578    const ContentSettingsPattern& other) const {
579  return !(*this == other);
580}
581
582bool ContentSettingsPattern::operator<(
583    const ContentSettingsPattern& other) const {
584  return Compare(other) < 0;
585}
586
587bool ContentSettingsPattern::operator>(
588    const ContentSettingsPattern& other) const {
589  return Compare(other) > 0;
590}
591
592// static
593ContentSettingsPattern::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
610ContentSettingsPattern::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
697ContentSettingsPattern::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