enable compatibility mode for autofill closes #290, closes #1061

This commit is contained in:
Philipp Crocoll
2020-02-17 11:59:04 +01:00
parent 01ea54932c
commit 9d34dfe23e
4 changed files with 209 additions and 32 deletions

View File

@@ -1,4 +1,99 @@
<?xml version="1.0" encoding="utf-8" ?>
<autofill-service
xmlns:android="http://schemas.android.com/apk/res/android"
/>
>
<compatibility-package
android:name="com.android.chrome"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.chrome.beta"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.chrome.dev"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.chrome.canary"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.microsoft.emmx"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.opera.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.opera.browser.beta"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.opera.mini.native"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.opera.mini.native.beta"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.sec.android.app.sbrowser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.sec.android.app.sbrowser.beta"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.fennec_aurora"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.fennec_fdroid"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.firefox"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.firefox_beta"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.fenix"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.fenix.nightly"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.reference.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.mozilla.rocket"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.brave.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.google.android.apps.chrome"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.google.android.apps.chrome_dev"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.yandex.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.codeaurora.swe.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.amazon.cloud9"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="mark.via.gp"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.bromite.bromite"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="org.chromium.chrome"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.kiwibrowser.browser"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.ecosia.android"
android:maxLongVersionCode="10000000000"/>
<compatibility-package
android:name="com.vivaldi.browser"
android:maxLongVersionCode="10000000000"/>
</autofill-service>

View File

@@ -35,6 +35,44 @@ namespace keepass2android.services.AutofillBase
{
}
public static HashSet<string> CompatBrowsers = new HashSet<string>
{
"org.mozilla.firefox",
"org.mozilla.firefox_beta",
"com.microsoft.emmx",
"com.android.chrome",
"com.chrome.beta",
"com.android.browser",
"com.brave.browser",
"com.opera.browser",
"com.opera.browser.beta",
"com.opera.mini.native",
"com.chrome.dev",
"com.chrome.canary",
"com.google.android.apps.chrome",
"com.google.android.apps.chrome_dev",
"com.yandex.browser",
"com.sec.android.app.sbrowser",
"com.sec.android.app.sbrowser.beta",
"org.codeaurora.swe.browser",
"com.amazon.cloud9",
"mark.via.gp",
"org.bromite.bromite",
"org.chromium.chrome",
"com.kiwibrowser.browser",
"com.ecosia.android",
"com.opera.mini.native.beta",
"org.mozilla.fennec_aurora",
"org.mozilla.fennec_fdroid",
"com.qwant.liberty",
"com.opera.touch",
"org.mozilla.fenix",
"org.mozilla.fenix.nightly",
"org.mozilla.reference.browser",
"org.mozilla.rocket",
"org.torproject.torbrowser",
"com.vivaldi.browser",
};
public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
{
@@ -81,9 +119,16 @@ namespace keepass2android.services.AutofillBase
AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset);
AddDisableDataset(query, autofillIds, responseBuilder, isManual);
if (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
parser.AutofillFields.GetAutofillIds()).Build());
if (PreferenceManager.GetDefaultSharedPreferences(this)
.GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
{
if (!CompatBrowsers.Contains(parser.PackageId))
{
responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
parser.AutofillFields.GetAutofillIds()).Build());
}
}
callback.OnSuccess(responseBuilder.Build());
}

View File

@@ -28,6 +28,8 @@ namespace keepass2android.services.AutofillBase
private PublicSuffixRuleCache domainSuffixParserCache;
public FilledAutofillFieldCollection ClientFormData { get; set; }
public string PackageId { get; set; }
public StructureParser(Context context, AssistStructure structure)
{
mContext = context;
@@ -63,6 +65,7 @@ namespace keepass2android.services.AutofillBase
for (int i = 0; i < nodes; i++)
{
var node = Structure.GetWindowNodeAt(i);
var view = node.RootViewNode;
ParseLocked(forFill, isManualRequest, view, ref webDomain);
}
@@ -78,23 +81,33 @@ namespace keepass2android.services.AutofillBase
{
passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList();
}
foreach (var passwordField in passwordFields)
{
var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
if (usernameField != null)
{
usernameFields.Add(usernameField);
}
}
//for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
//let's assume it is a username field:
if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
{
usernameFields.Add(_editTextsWithoutHint.First());
usernameFields = _editTextsWithoutHint.Where(HasUsernameHint).ToList();
if (usernameFields.Any() == false)
{
foreach (var passwordField in passwordFields)
{
var usernameField = _editTextsWithoutHint
.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
if (usernameField != null)
{
usernameFields.Add(usernameField);
}
}
}
if (usernameFields.Any() == false)
{
//for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
//let's assume it is a username field:
if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
{
usernameFields.Add(_editTextsWithoutHint.First());
}
}
}
//force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints)
@@ -149,14 +162,31 @@ namespace keepass2android.services.AutofillBase
}
return webDomain;
}
private static bool HasPasswordHint(AssistStructure.ViewNode f)
private static readonly HashSet<string> _passwordHints = new HashSet<string> { "password","passwort" };
private static bool HasPasswordHint(AssistStructure.ViewNode f)
{
return (f.IdEntry?.ToLowerInvariant().Contains("password") ?? false)
|| (f.Hint?.ToLowerInvariant().Contains("password") ?? false);
}
return ContainsAny(f.IdEntry, _passwordHints) ||
ContainsAny(f.Hint, _passwordHints);
}
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
private static readonly HashSet<string> _usernameHints = new HashSet<string> { "email","e-mail","username" };
private static bool HasUsernameHint(AssistStructure.ViewNode f)
{
return ContainsAny(f.IdEntry, _usernameHints) ||
ContainsAny(f.Hint, _usernameHints);
}
private static bool ContainsAny(string value, IEnumerable<string> terms)
{
if (string.IsNullOrWhiteSpace(value))
{
return false;
}
var lowerValue = value.ToLowerInvariant();
return terms.Any(t => lowerValue.Contains(t));
}
private static bool IsInputTypeClass(InputTypes inputType, InputTypes inputTypeClass)
{
if (!InputTypes.MaskClass.HasFlag(inputTypeClass))
throw new Exception("invalid inputTypeClas");
@@ -198,8 +228,13 @@ namespace keepass2android.services.AutofillBase
void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain)
{
String webDomain = viewNode.WebDomain;
if ((PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
(viewNode.IdPackage != "android"))
{
PackageId = viewNode.IdPackage;
}
DomainName outDomain;
DomainName outDomain;
if (DomainName.TryParse(webDomain, domainSuffixParserCache, out outDomain))
{
webDomain = outDomain.RegisterableDomainName;
@@ -224,9 +259,13 @@ namespace keepass2android.services.AutofillBase
if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused &&
isManualRequest)
viewHints[0] = "on";
CommonUtil.logd("viewHints=" + viewHints);
CommonUtil.logd("class=" + viewNode.ClassName);
CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
if (viewHints != null && viewHints.Any())
{
CommonUtil.logd("viewHints=" + viewHints);
CommonUtil.logd("class=" + viewNode.ClassName);
CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
}
if (viewNode?.HtmlInfo?.Tag == "input")
{
foreach (var p in viewNode.HtmlInfo.Attributes)

View File

@@ -15,7 +15,7 @@ using AutofillServiceBase = keepass2android.services.AutofillBase.AutofillServic
namespace keepass2android.services
{
[Service(Label = AppNames.AppName, Permission=Manifest.Permission.BindAutofillService)]
[Service(Label = AppNames.AppName, Permission=Manifest.Permission.BindAutofillService)]
[IntentFilter(new [] {"android.service.autofill.AutofillService"})]
[MetaData("android.autofill", Resource = "@xml/autofillservice")]
[Register("keepass2android.services.Kp2aAutofillService")]
@@ -41,8 +41,6 @@ namespace keepass2android.services
protected override void HandleSaveRequest(StructureParser parser, string query)
{
var intent = new Intent(this, typeof(SelectCurrentDbActivity));
Dictionary<string, string> outputFields = new Dictionary<string, string>();