Chromium Code Reviews| 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..8f80a69187aaf753dff95e74a374f5c47bb7e0c8 |
| --- /dev/null |
| +++ b/components/arc/intent_helper/intent_filter.cc |
| @@ -0,0 +1,244 @@ |
| +// 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" |
| + |
|
Yusuke Sato
2016/07/12 10:52:58
IWYU: base/compiler_specific.h for UNLIKELY
Ben Kwa
2016/07/12 16:05:23
Done.
|
| +#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_.push_back(AuthorityEntry(authorityptr)); |
|
Yusuke Sato
2016/07/12 10:52:57
nit: .emplace_back(authorityptr); ?
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + } |
| + for (const mojom::PatternMatcherPtr& pattern: |
| + mojo_intent_filter->data_paths) { |
| + paths_.push_back(PatternMatcher(pattern)); |
|
Yusuke Sato
2016/07/12 10:52:57
same
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + } |
| + for (const mojom::PatternMatcherPtr& pattern: |
| + mojo_intent_filter->data_scheme_specific_parts) { |
| + scheme_specific_parts_.push_back(PatternMatcher(pattern)); |
|
Yusuke Sato
2016/07/12 10:52:57
same
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + } |
| +} |
| + |
| +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()) { |
|
rickyz (no longer on Chrome)
2016/07/11 23:26:44
Does this mean the intent filters are already filt
Ben Kwa
2016/07/12 16:05:23
The android side should already filter the filters
|
| + 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( |
|
Yusuke Sato
2016/07/12 10:52:57
Would you mind adding a test or two for this metho
Ben Kwa
2016/07/12 16:05:23
Definitely. I'll add the tests as a follow-up CL.
|
| + const GURL& url) const { |
| + // The scheme-specific-part is the content of the URL minus the ref fragment. |
| + std::string ssp; |
| + if (url.has_ref()) { |
|
rickyz (no longer on Chrome)
2016/07/11 23:26:44
Hm, does has_ref return true for http://example.co
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + GURL::Replacements replacements; |
| + replacements.ClearRef(); |
| + ssp = url.ReplaceComponents(replacements).GetContent(); |
| + } else { |
| + ssp = url.GetContent(); |
| + } |
| + |
| + for (const PatternMatcher& pattern: scheme_specific_parts_) { |
|
rickyz (no longer on Chrome)
2016/07/11 23:26:44
nit: space before : here and elsewhere (might be g
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + 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_.length() > 0 && host_[0] == '*'; |
|
Yusuke Sato
2016/07/12 10:52:58
nit:
!host_.empty() &&
would probably easier to
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + if (wild_) { |
| + host_ = host_.substr(1); |
| + } |
| + |
| + // TODO: Not i18n-friendly. Figure out how to correctly deal with IDNs. |
| + host_ = base::ToLowerASCII(host_); |
|
Yusuke Sato
2016/07/12 10:52:57
nit: you can probably call base::ToLowerASCII() on
Ben Kwa
2016/07/12 16:05:23
I'm actually going to keep this here in order to a
|
| +} |
| + |
| +// Transcribed from android's IntentFilter.AuthorityEntry#match. |
| +bool IntentFilter::AuthorityEntry::match( |
| + const GURL& url) const { |
| + if (!url.has_host()) return false; |
| + // TODO: Not i18n-friendly. Figure out how to correctly deal with IDNs. |
| + std::string host = base::ToLowerASCII(url.host_piece()); |
|
Yusuke Sato
2016/07/12 10:52:57
nit: same. you can probably move base::ToLowerASCI
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + const int port = url.IntPort(); |
| + |
| + if (port_ >= 0 && port_ != port) { |
| + return false; |
| + } |
| + |
| + if (wild_) { |
| + return base::EndsWith(host, host_, base::CompareCase::INSENSITIVE_ASCII); |
| + } else { |
| + return host == host_; |
| + } |
| +} |
| + |
|
Yusuke Sato
2016/07/12 10:52:58
nit: remove this line
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + |
| +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.length() == 0) { |
|
Yusuke Sato
2016/07/12 10:52:57
nit: same. if (str.empty())
Ben Kwa
2016/07/12 16:05:23
Done.
|
| + 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 |