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