Broaden Android accessibility autofill fallback
This commit is contained in:
@@ -25,26 +25,7 @@ final class AutofillCacheStore {
|
||||
if (entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
NormalizedTarget target = normalizeURL(webDomain);
|
||||
if (target.host.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<Entry> exactHost = new ArrayList<>();
|
||||
List<Entry> parentHost = new ArrayList<>();
|
||||
for (Entry entry : entries) {
|
||||
if (entryMatchesHost(entry, target.host)) {
|
||||
exactHost.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (entryMatchesParentHost(entry, target.host)) {
|
||||
parentHost.add(entry);
|
||||
}
|
||||
}
|
||||
Entry matched = chooseEntry(target, exactHost);
|
||||
if (matched != null) {
|
||||
return matched;
|
||||
}
|
||||
return chooseEntry(target, parentHost);
|
||||
return fromMatcherEntry(AutofillTargetMatcher.findBestMatch(toMatcherEntries(entries), webDomain));
|
||||
}
|
||||
|
||||
static Entry findByID(Context context, String entryID) {
|
||||
@@ -64,7 +45,7 @@ final class AutofillCacheStore {
|
||||
if (entries.isEmpty()) {
|
||||
return entries;
|
||||
}
|
||||
Entry direct = findBestMatch(context, rawTarget);
|
||||
Entry direct = fromMatcherEntry(AutofillTargetMatcher.findBestMatch(toMatcherEntries(entries), rawTarget));
|
||||
if (direct != null) {
|
||||
List<Entry> resolved = new ArrayList<>();
|
||||
resolved.add(direct);
|
||||
@@ -77,6 +58,30 @@ final class AutofillCacheStore {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private static List<AutofillTargetMatcher.Entry> toMatcherEntries(List<Entry> entries) {
|
||||
List<AutofillTargetMatcher.Entry> converted = new ArrayList<>(entries.size());
|
||||
for (Entry entry : entries) {
|
||||
converted.add(new AutofillTargetMatcher.Entry(
|
||||
entry.id,
|
||||
entry.title,
|
||||
entry.username,
|
||||
entry.password,
|
||||
entry.host,
|
||||
entry.url,
|
||||
entry.targets,
|
||||
entry.path
|
||||
));
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
private static Entry fromMatcherEntry(AutofillTargetMatcher.Entry entry) {
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
return new Entry(entry.id, entry.title, entry.username, entry.password, entry.host, entry.url, entry.targets, entry.path);
|
||||
}
|
||||
|
||||
private static List<Entry> readEntries(Context context) {
|
||||
File cacheFile = findCacheFile(context);
|
||||
if (cacheFile == null) {
|
||||
@@ -199,143 +204,7 @@ final class AutofillCacheStore {
|
||||
}
|
||||
|
||||
private static String normalizeHost(String raw) {
|
||||
return normalizeURL(raw).host;
|
||||
}
|
||||
|
||||
private static NormalizedTarget normalizeURL(String raw) {
|
||||
if (raw == null) {
|
||||
return new NormalizedTarget("", "", "");
|
||||
}
|
||||
String value = raw.trim().toLowerCase(Locale.US);
|
||||
if (value.startsWith("http://")) {
|
||||
value = value.substring("http://".length());
|
||||
} else if (value.startsWith("https://")) {
|
||||
value = value.substring("https://".length());
|
||||
}
|
||||
int slash = value.indexOf('/');
|
||||
if (slash >= 0) {
|
||||
value = value.substring(0, slash);
|
||||
}
|
||||
int colon = value.indexOf(':');
|
||||
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<>();
|
||||
int bestPrefixLen = -1;
|
||||
for (Entry entry : entries) {
|
||||
MatchQuality quality = bestTargetMatch(entry, target);
|
||||
if (quality.exact) {
|
||||
exact.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (quality.prefixLength <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (quality.prefixLength > bestPrefixLen) {
|
||||
prefix.clear();
|
||||
prefix.add(entry);
|
||||
bestPrefixLen = quality.prefixLength;
|
||||
} else if (quality.prefixLength == bestPrefixLen) {
|
||||
prefix.add(entry);
|
||||
}
|
||||
}
|
||||
if (exact.size() == 1) {
|
||||
return exact.get(0);
|
||||
}
|
||||
if (exact.size() > 1 || prefix.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return prefix.size() == 1 ? prefix.get(0) : null;
|
||||
}
|
||||
|
||||
private static boolean entryMatchesHost(Entry entry, String host) {
|
||||
for (NormalizedTarget target : entryTargets(entry)) {
|
||||
if (target.host.equals(host)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean entryMatchesParentHost(Entry entry, String host) {
|
||||
for (NormalizedTarget target : entryTargets(entry)) {
|
||||
if (!target.host.isEmpty() && host.endsWith("." + target.host)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<NormalizedTarget> entryTargets(Entry entry) {
|
||||
List<String> rawTargets = entry.targets;
|
||||
if (rawTargets.isEmpty()) {
|
||||
rawTargets = new ArrayList<>();
|
||||
rawTargets.add(entry.url);
|
||||
}
|
||||
List<NormalizedTarget> targets = new ArrayList<>();
|
||||
for (String rawTarget : rawTargets) {
|
||||
NormalizedTarget target = normalizeURL(rawTarget);
|
||||
if (!target.host.isEmpty()) {
|
||||
targets.add(target);
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
private static MatchQuality bestTargetMatch(Entry entry, NormalizedTarget target) {
|
||||
int bestPrefixLen = -1;
|
||||
for (NormalizedTarget entryTarget : entryTargets(entry)) {
|
||||
if (entryTarget.url.equals(target.url)) {
|
||||
return new MatchQuality(true, 0);
|
||||
}
|
||||
if (!"/".equals(entryTarget.path) && target.path.startsWith(entryTarget.path)) {
|
||||
bestPrefixLen = Math.max(bestPrefixLen, entryTarget.path.length());
|
||||
}
|
||||
}
|
||||
return new MatchQuality(false, bestPrefixLen);
|
||||
return AutofillTargetMatcher.normalize(raw).host;
|
||||
}
|
||||
|
||||
static final class Entry {
|
||||
@@ -359,26 +228,4 @@ final class AutofillCacheStore {
|
||||
this.path = new ArrayList<>(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class MatchQuality {
|
||||
final boolean exact;
|
||||
final int prefixLength;
|
||||
|
||||
MatchQuality(boolean exact, int prefixLength) {
|
||||
this.exact = exact;
|
||||
this.prefixLength = prefixLength;
|
||||
}
|
||||
}
|
||||
|
||||
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