Disambiguate same-host Android fill matches
This commit is contained in:
@@ -35,20 +35,26 @@ final class AutofillCacheStore {
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String normalizedDomain = normalizeHost(webDomain);
|
||||
if (normalizedDomain.isEmpty()) {
|
||||
return entries.get(0);
|
||||
NormalizedTarget target = normalizeURL(webDomain);
|
||||
if (target.host.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Entry fallback = null;
|
||||
List<Entry> exactHost = new ArrayList<>();
|
||||
List<Entry> parentHost = new ArrayList<>();
|
||||
for (Entry entry : entries) {
|
||||
if (entry.host.equals(normalizedDomain)) {
|
||||
return entry;
|
||||
if (entry.host.equals(target.host)) {
|
||||
exactHost.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (fallback == null && normalizedDomain.endsWith("." + entry.host)) {
|
||||
fallback = entry;
|
||||
if (!entry.host.isEmpty() && target.host.endsWith("." + entry.host)) {
|
||||
parentHost.add(entry);
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
Entry matched = chooseEntry(target, exactHost);
|
||||
if (matched != null) {
|
||||
return matched;
|
||||
}
|
||||
return chooseEntry(target, parentHost);
|
||||
}
|
||||
|
||||
private static File findCacheFile(Context context) {
|
||||
@@ -101,6 +107,7 @@ final class AutofillCacheStore {
|
||||
String username = "";
|
||||
String password = "";
|
||||
String host = "";
|
||||
String url = "";
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String name = reader.nextName();
|
||||
@@ -114,6 +121,9 @@ final class AutofillCacheStore {
|
||||
case "password":
|
||||
password = nextString(reader);
|
||||
break;
|
||||
case "url":
|
||||
url = nextString(reader);
|
||||
break;
|
||||
case "host":
|
||||
host = normalizeHost(nextString(reader));
|
||||
break;
|
||||
@@ -123,7 +133,7 @@ final class AutofillCacheStore {
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
return new Entry(title, username, password, host);
|
||||
return new Entry(title, username, password, host, url);
|
||||
}
|
||||
|
||||
private static String nextString(JsonReader reader) throws IOException {
|
||||
@@ -135,8 +145,12 @@ final class AutofillCacheStore {
|
||||
}
|
||||
|
||||
private static String normalizeHost(String raw) {
|
||||
return normalizeURL(raw).host;
|
||||
}
|
||||
|
||||
private static NormalizedTarget normalizeURL(String raw) {
|
||||
if (raw == null) {
|
||||
return "";
|
||||
return new NormalizedTarget("", "", "");
|
||||
}
|
||||
String value = raw.trim().toLowerCase(Locale.US);
|
||||
if (value.startsWith("http://")) {
|
||||
@@ -152,20 +166,113 @@ final class AutofillCacheStore {
|
||||
if (colon >= 0) {
|
||||
value = value.substring(0, colon);
|
||||
}
|
||||
String host = value;
|
||||
String path = "/";
|
||||
int schemeSep = raw.indexOf("://");
|
||||
String original = raw.trim();
|
||||
if (schemeSep < 0) {
|
||||
original = "https://" + original;
|
||||
}
|
||||
try {
|
||||
java.net.URI uri = java.net.URI.create(original);
|
||||
if (uri.getHost() != null) {
|
||||
host = uri.getHost().toLowerCase(Locale.US);
|
||||
}
|
||||
path = cleanPath(uri.getPath());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
path = "/";
|
||||
}
|
||||
return new NormalizedTarget(host, path, host + path);
|
||||
}
|
||||
|
||||
private static String cleanPath(String raw) {
|
||||
if (raw == null || raw.trim().isEmpty() || "/".equals(raw.trim())) {
|
||||
return "/";
|
||||
}
|
||||
String value = raw.trim();
|
||||
while (value.endsWith("/") && value.length() > 1) {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
}
|
||||
if (!value.startsWith("/")) {
|
||||
value = "/" + value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Entry chooseEntry(NormalizedTarget target, List<Entry> entries) {
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (entries.size() == 1) {
|
||||
return entries.get(0);
|
||||
}
|
||||
|
||||
List<Entry> exact = new ArrayList<>();
|
||||
List<Entry> prefix = new ArrayList<>();
|
||||
for (Entry entry : entries) {
|
||||
NormalizedTarget entryTarget = normalizeURL(entry.url);
|
||||
if (entryTarget.host.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (entryTarget.url.equals(target.url)) {
|
||||
exact.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (!"/".equals(entryTarget.path) && target.path.startsWith(entryTarget.path)) {
|
||||
prefix.add(entry);
|
||||
}
|
||||
}
|
||||
if (exact.size() == 1) {
|
||||
return exact.get(0);
|
||||
}
|
||||
if (exact.size() > 1 || prefix.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Entry best = null;
|
||||
int bestLen = -1;
|
||||
boolean ambiguous = false;
|
||||
for (Entry entry : prefix) {
|
||||
int pathLen = normalizeURL(entry.url).path.length();
|
||||
if (pathLen > bestLen) {
|
||||
best = entry;
|
||||
bestLen = pathLen;
|
||||
ambiguous = false;
|
||||
} else if (pathLen == bestLen) {
|
||||
ambiguous = true;
|
||||
}
|
||||
}
|
||||
if (ambiguous) {
|
||||
return null;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
static final class Entry {
|
||||
final String title;
|
||||
final String username;
|
||||
final String password;
|
||||
final String host;
|
||||
final String url;
|
||||
|
||||
Entry(String title, String username, String password, String host) {
|
||||
Entry(String title, String username, String password, String host, String url) {
|
||||
this.title = title;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.host = host;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NormalizedTarget {
|
||||
final String host;
|
||||
final String path;
|
||||
final String url;
|
||||
|
||||
NormalizedTarget(String host, String path, String url) {
|
||||
this.host = host;
|
||||
this.path = path;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user