| Index: components/arc/intent_helper/intent_filter.cc
|
| diff --git a/components/arc/intent_helper/intent_filter.cc b/components/arc/intent_helper/intent_filter.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1a0f79c45de5c93f2c27e2c16b4f3a5ee358290f
|
| --- /dev/null
|
| +++ b/components/arc/intent_helper/intent_filter.cc
|
| @@ -0,0 +1,226 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/arc/intent_helper/intent_filter.h"
|
| +
|
| +#include "base/compiler_specific.h"
|
| +#include "base/strings/string_util.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace arc {
|
| +
|
| +IntentFilter::IntentFilter(const mojom::IntentFilterPtr& mojo_intent_filter) {
|
| + for (const mojom::AuthorityEntryPtr& authorityptr :
|
| + mojo_intent_filter->data_authorities) {
|
| + authorities_.emplace_back(authorityptr);
|
| + }
|
| + for (const mojom::PatternMatcherPtr& pattern :
|
| + mojo_intent_filter->data_paths) {
|
| + paths_.emplace_back(pattern);
|
| + }
|
| + for (const mojom::PatternMatcherPtr& pattern :
|
| + mojo_intent_filter->data_scheme_specific_parts) {
|
| + scheme_specific_parts_.emplace_back(pattern);
|
| + }
|
| +}
|
| +
|
| +IntentFilter::IntentFilter(const IntentFilter& other) = default;
|
| +
|
| +IntentFilter::~IntentFilter() = default;
|
| +
|
| +// Logically, this maps to IntentFilter#match, but this code only deals with
|
| +// view intents for http/https URLs and so it really only implements the
|
| +// #matchData part of the match code.
|
| +bool IntentFilter::match(const GURL& url) const {
|
| + // Chrome-side code only receives view intents for http/https URLs, so this
|
| + // match code really only implements the matchData part of the android
|
| + // IntentFilter class.
|
| + if (!url.SchemeIsHTTPOrHTTPS()) {
|
| + return false;
|
| + }
|
| +
|
| + if (!scheme_specific_parts_.empty() && hasDataSchemeSpecificPart(url)) {
|
| + return true;
|
| + }
|
| +
|
| + // If there isn't any matching ssp, we need to match an authority.
|
| + if (!authorities_.empty()) {
|
| + return matchDataAuthority(url) && (paths_.empty() || hasDataPath(url));
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Transcribed from android's IntentFilter#hasDataSchemeSpecificPart.
|
| +bool IntentFilter::hasDataSchemeSpecificPart(const GURL& url) const {
|
| + // The scheme-specific-part is the content of the URL minus the ref fragment.
|
| + GURL::Replacements replacements;
|
| + replacements.ClearRef();
|
| + const std::string ssp = url.ReplaceComponents(replacements).GetContent();
|
| +
|
| + for (const PatternMatcher& pattern : scheme_specific_parts_) {
|
| + if (pattern.match(ssp)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// Transcribed from android's IntentFilter#hasDataPath.
|
| +bool IntentFilter::hasDataPath(const GURL& url) const {
|
| + const std::string path = url.path();
|
| + for (const PatternMatcher& pattern : paths_) {
|
| + if (pattern.match(path)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// Transcribed from android's IntentFilter#matchDataAuthority.
|
| +bool IntentFilter::matchDataAuthority(const GURL& url) const {
|
| + for (const AuthorityEntry& authority : authorities_) {
|
| + if (authority.match(url)) {
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +IntentFilter::AuthorityEntry::AuthorityEntry(
|
| + const mojom::AuthorityEntryPtr& entry)
|
| + : host_(entry->host.get()), port_(entry->port) {
|
| + // Wildcards are only allowed at the front of the host string.
|
| + wild_ = !host_.empty() && host_[0] == '*';
|
| + if (wild_) {
|
| + host_ = host_.substr(1);
|
| + }
|
| +
|
| + // TODO: Not i18n-friendly. Figure out how to correctly deal with IDNs.
|
| + host_ = base::ToLowerASCII(host_);
|
| +}
|
| +
|
| +// Transcribed from android's IntentFilter.AuthorityEntry#match.
|
| +bool IntentFilter::AuthorityEntry::match(const GURL& url) const {
|
| + if (!url.has_host())
|
| + return false;
|
| +
|
| + if (port_ >= 0 && port_ != url.IntPort()) {
|
| + return false;
|
| + }
|
| +
|
| + if (wild_) {
|
| + return base::EndsWith(url.host_piece(), host_,
|
| + base::CompareCase::INSENSITIVE_ASCII);
|
| + } else {
|
| + // TODO: Not i18n-friendly. Figure out how to correctly deal with IDNs.
|
| + return host_ == base::ToLowerASCII(url.host_piece());
|
| + }
|
| +}
|
| +
|
| +IntentFilter::PatternMatcher::PatternMatcher(
|
| + const mojom::PatternMatcherPtr& pattern)
|
| + : pattern_(pattern->pattern.get()), match_type_(pattern->type) {}
|
| +
|
| +// Transcribed from android's PatternMatcher#matchPattern.
|
| +bool IntentFilter::PatternMatcher::match(const std::string& str) const {
|
| + if (str.empty()) {
|
| + return false;
|
| + }
|
| + switch (match_type_) {
|
| + case mojom::PatternType::PATTERN_LITERAL:
|
| + return str == pattern_;
|
| + case mojom::PatternType::PATTERN_PREFIX:
|
| + return base::StartsWith(str, pattern_,
|
| + base::CompareCase::INSENSITIVE_ASCII);
|
| + case mojom::PatternType::PATTERN_SIMPLE_GLOB:
|
| + return matchGlob(str);
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Transcribed from android's PatternMatcher#matchPattern.
|
| +bool IntentFilter::PatternMatcher::matchGlob(const std::string& str) const {
|
| +#define GET_CHAR(s, i) (UNLIKELY(i >= s.length())) ? '\0' : s[i]
|
| +
|
| + const size_t NP = pattern_.length();
|
| + const size_t NS = str.length();
|
| + if (NP == 0) {
|
| + return NS == 0;
|
| + }
|
| + size_t ip = 0, is = 0;
|
| + char nextChar = GET_CHAR(pattern_, 0);
|
| + while (ip < NP && is < NS) {
|
| + char c = nextChar;
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + const bool escaped = (c == '\\');
|
| + if (escaped) {
|
| + c = nextChar;
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + }
|
| + if (nextChar == '*') {
|
| + if (!escaped && c == '.') {
|
| + if (ip >= (NP - 1)) {
|
| + // At the end with a pattern match
|
| + return true;
|
| + }
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + // Consume everything until the next char in the pattern is found.
|
| + if (nextChar == '\\') {
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + }
|
| + do {
|
| + if (GET_CHAR(str, is) == nextChar) {
|
| + break;
|
| + }
|
| + ++is;
|
| + } while (is < NS);
|
| + if (is == NS) {
|
| + // Next char in the pattern didn't exist in the match.
|
| + return false;
|
| + }
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + ++is;
|
| + } else {
|
| + // Consume only characters matching the one before '*'.
|
| + do {
|
| + if (GET_CHAR(str, is) != c) {
|
| + break;
|
| + }
|
| + ++is;
|
| + } while (is < NS);
|
| + ++ip;
|
| + nextChar = GET_CHAR(pattern_, ip);
|
| + }
|
| + } else {
|
| + if (c != '.' && GET_CHAR(str, is) != c)
|
| + return false;
|
| + ++is;
|
| + }
|
| + }
|
| +
|
| + if (ip >= NP && is >= NS) {
|
| + // Reached the end of both strings
|
| + return true;
|
| + }
|
| +
|
| + // One last check: we may have finished the match string, but still have a
|
| + // '.*' at the end of the pattern, which is still a match.
|
| + if (ip == NP - 2 && GET_CHAR(pattern_, ip) == '.' &&
|
| + GET_CHAR(pattern_, ip + 1) == '*') {
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +
|
| +#undef GET_CHAR
|
| +}
|
| +
|
| +} // namespace arc
|
|
|