Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Unified Diff: components/arc/intent_helper/intent_filter.cc

Issue 2128913002: Implement chrome-side intent filtering. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Apply CL feedback. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « components/arc/intent_helper/intent_filter.h ('k') | components/arc/intent_helper/local_activity_resolver.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698