mirror of
https://github.com/bitwarden/android.git
synced 2024-12-21 08:42:39 +03:00
added webview support for app extension. moved safari extension to same code as webview.
This commit is contained in:
parent
fac4401e97
commit
ae5b637786
4 changed files with 252 additions and 298 deletions
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
@ -9,6 +10,7 @@ using CoreGraphics;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using Microsoft.Practices.Unity;
|
using Microsoft.Practices.Unity;
|
||||||
using MobileCoreServices;
|
using MobileCoreServices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
using XLabs.Ioc;
|
using XLabs.Ioc;
|
||||||
using XLabs.Ioc.Unity;
|
using XLabs.Ioc.Unity;
|
||||||
|
@ -37,6 +39,9 @@ namespace Bit.iOS.Extension
|
||||||
private const string AppExtensionGeneratedPasswordRequireSymbolsKey = "password_require_symbols";
|
private const string AppExtensionGeneratedPasswordRequireSymbolsKey = "password_require_symbols";
|
||||||
private const string AppExtensionGeneratedPasswordForbiddenCharactersKey = "password_forbidden_characters";
|
private const string AppExtensionGeneratedPasswordForbiddenCharactersKey = "password_forbidden_characters";
|
||||||
|
|
||||||
|
private const string AppExtensionWebViewPageFillScript = "fillScript";
|
||||||
|
private const string AppExtensionWebViewPageDetails = "pageDetails";
|
||||||
|
|
||||||
private const string UTTypeAppExtensionFindLoginAction = "org.appextension.find-login-action";
|
private const string UTTypeAppExtensionFindLoginAction = "org.appextension.find-login-action";
|
||||||
private const string UTTypeAppExtensionSaveLoginAction = "org.appextension.save-login-action";
|
private const string UTTypeAppExtensionSaveLoginAction = "org.appextension.save-login-action";
|
||||||
private const string UTTypeAppExtensionChangePasswordAction = "org.appextension.change-password-action";
|
private const string UTTypeAppExtensionChangePasswordAction = "org.appextension.change-password-action";
|
||||||
|
@ -59,6 +64,7 @@ namespace Bit.iOS.Extension
|
||||||
public string OldPassword { get; set; }
|
public string OldPassword { get; set; }
|
||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
public PasswordGenerationOptions PasswordOptions { get; set; }
|
public PasswordGenerationOptions PasswordOptions { get; set; }
|
||||||
|
public PageDetails Details { get; set; }
|
||||||
|
|
||||||
private void SetIoc()
|
private void SetIoc()
|
||||||
{
|
{
|
||||||
|
@ -90,34 +96,29 @@ namespace Bit.iOS.Extension
|
||||||
Resolver.SetResolver(new UnityResolver(container));
|
Resolver.SetResolver(new UnityResolver(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void DidReceiveMemoryWarning()
|
|
||||||
{
|
|
||||||
base.DidReceiveMemoryWarning();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void LoadView()
|
public override void LoadView()
|
||||||
{
|
{
|
||||||
foreach(var item in ExtensionContext.InputItems)
|
foreach(var item in ExtensionContext.InputItems)
|
||||||
{
|
{
|
||||||
|
var processed = false;
|
||||||
foreach(var itemProvider in item.Attachments)
|
foreach(var itemProvider in item.Attachments)
|
||||||
{
|
{
|
||||||
if(ProcessWebUrlProvider(itemProvider))
|
if(ProcessWebUrlProvider(itemProvider)
|
||||||
{
|
|| ProcessFindLoginProvider(itemProvider)
|
||||||
break;
|
|| ProcessFindLoginBrowserProvider(itemProvider, UTTypeAppExtensionFillBrowserAction)
|
||||||
}
|
|| ProcessFindLoginBrowserProvider(itemProvider, UTTypeAppExtensionFillWebViewAction)
|
||||||
else if(ProcessFindLoginProvider(itemProvider))
|
|| ProcessSaveLoginProvider(itemProvider)
|
||||||
{
|
|| ProcessChangePasswordProvider(itemProvider))
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(ProcessSaveLoginProvider(itemProvider))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(ProcessChangePasswordProvider(itemProvider))
|
|
||||||
{
|
{
|
||||||
|
processed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(processed)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
View = new UIView(new CGRect(x: 0.0, y: 0, width: 320.0, height: 200.0));
|
View = new UIView(new CGRect(x: 0.0, y: 0, width: 320.0, height: 200.0));
|
||||||
|
@ -130,19 +131,20 @@ namespace Bit.iOS.Extension
|
||||||
private void Button_TouchUpInside(object sender, EventArgs e)
|
private void Button_TouchUpInside(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
NSDictionary itemData = null;
|
NSDictionary itemData = null;
|
||||||
if(ProviderType == UTType.PropertyList)
|
if(ProviderType == UTTypeAppExtensionFindLoginAction)
|
||||||
{
|
|
||||||
itemData = new NSDictionary(
|
|
||||||
"username", "me@example.com",
|
|
||||||
"password", "mypassword",
|
|
||||||
"autoSubmit", true);
|
|
||||||
}
|
|
||||||
else if(ProviderType == UTTypeAppExtensionFindLoginAction)
|
|
||||||
{
|
{
|
||||||
itemData = new NSDictionary(
|
itemData = new NSDictionary(
|
||||||
AppExtensionUsernameKey, "me@example.com",
|
AppExtensionUsernameKey, "me@example.com",
|
||||||
AppExtensionPasswordKey, "mypassword");
|
AppExtensionPasswordKey, "mypassword");
|
||||||
}
|
}
|
||||||
|
else if(ProviderType == UTType.PropertyList
|
||||||
|
|| ProviderType == UTTypeAppExtensionFillBrowserAction
|
||||||
|
|| ProviderType == UTTypeAppExtensionFillWebViewAction)
|
||||||
|
{
|
||||||
|
var fillScript = new FillScript(Details);
|
||||||
|
var scriptJson = JsonConvert.SerializeObject(fillScript);
|
||||||
|
itemData = new NSDictionary(AppExtensionWebViewPageFillScript, scriptJson);
|
||||||
|
}
|
||||||
else if(ProviderType == UTTypeAppExtensionSaveLoginAction)
|
else if(ProviderType == UTTypeAppExtensionSaveLoginAction)
|
||||||
{
|
{
|
||||||
itemData = new NSDictionary(
|
itemData = new NSDictionary(
|
||||||
|
@ -155,10 +157,6 @@ namespace Bit.iOS.Extension
|
||||||
AppExtensionPasswordKey, "mynewpassword",
|
AppExtensionPasswordKey, "mynewpassword",
|
||||||
AppExtensionOldPasswordKey, "myoldpassword");
|
AppExtensionOldPasswordKey, "myoldpassword");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
|
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
|
||||||
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
|
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
|
||||||
|
@ -197,6 +195,7 @@ namespace Bit.iOS.Extension
|
||||||
Debug.WriteLine("BW LOG, Password: " + Password);
|
Debug.WriteLine("BW LOG, Password: " + Password);
|
||||||
Debug.WriteLine("BW LOG, Old Password: " + OldPassword);
|
Debug.WriteLine("BW LOG, Old Password: " + OldPassword);
|
||||||
Debug.WriteLine("BW LOG, Notes: " + Notes);
|
Debug.WriteLine("BW LOG, Notes: " + Notes);
|
||||||
|
Debug.WriteLine("BW LOG, Details: " + Details);
|
||||||
|
|
||||||
if(PasswordOptions != null)
|
if(PasswordOptions != null)
|
||||||
{
|
{
|
||||||
|
@ -221,7 +220,9 @@ namespace Bit.iOS.Extension
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Url = new Uri(result.ValueForKey(new NSString("url")) as NSString);
|
Url = new Uri(result.ValueForKey(new NSString(AppExtensionUrlStringKey)) as NSString);
|
||||||
|
var jsonStr = result.ValueForKey(new NSString(AppExtensionWebViewPageDetails)) as NSString;
|
||||||
|
Details = DeserializeString<PageDetails>(jsonStr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,6 +240,21 @@ namespace Bit.iOS.Extension
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ProcessFindLoginBrowserProvider(NSItemProvider itemProvider, string action)
|
||||||
|
{
|
||||||
|
return ProcessItemProvider(itemProvider, action, (dict) =>
|
||||||
|
{
|
||||||
|
var version = dict[AppExtensionVersionNumberKey] as NSNumber;
|
||||||
|
var url = dict[AppExtensionUrlStringKey] as NSString;
|
||||||
|
if(url != null)
|
||||||
|
{
|
||||||
|
Url = new Uri(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
Details = DeserializeDictionary<PageDetails>(dict[AppExtensionWebViewPageDetails] as NSDictionary);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private bool ProcessSaveLoginProvider(NSItemProvider itemProvider)
|
private bool ProcessSaveLoginProvider(NSItemProvider itemProvider)
|
||||||
{
|
{
|
||||||
return ProcessItemProvider(itemProvider, UTTypeAppExtensionSaveLoginAction, (dict) =>
|
return ProcessItemProvider(itemProvider, UTTypeAppExtensionSaveLoginAction, (dict) =>
|
||||||
|
@ -251,7 +267,6 @@ namespace Bit.iOS.Extension
|
||||||
var password = dict[AppExtensionPasswordKey] as NSString;
|
var password = dict[AppExtensionPasswordKey] as NSString;
|
||||||
var notes = dict[AppExtensionNotesKey] as NSString;
|
var notes = dict[AppExtensionNotesKey] as NSString;
|
||||||
var fields = dict[AppExtensionFieldsKey] as NSDictionary;
|
var fields = dict[AppExtensionFieldsKey] as NSDictionary;
|
||||||
var passwordGenerationOptions = dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary;
|
|
||||||
|
|
||||||
if(url != null)
|
if(url != null)
|
||||||
{
|
{
|
||||||
|
@ -263,7 +278,7 @@ namespace Bit.iOS.Extension
|
||||||
Username = username;
|
Username = username;
|
||||||
Password = password;
|
Password = password;
|
||||||
Notes = notes;
|
Notes = notes;
|
||||||
PasswordOptions = new PasswordGenerationOptions(passwordGenerationOptions);
|
PasswordOptions = DeserializeDictionary<PasswordGenerationOptions>(dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +295,6 @@ namespace Bit.iOS.Extension
|
||||||
var oldPassword = dict[AppExtensionOldPasswordKey] as NSString;
|
var oldPassword = dict[AppExtensionOldPasswordKey] as NSString;
|
||||||
var notes = dict[AppExtensionNotesKey] as NSString;
|
var notes = dict[AppExtensionNotesKey] as NSString;
|
||||||
var fields = dict[AppExtensionFieldsKey] as NSDictionary;
|
var fields = dict[AppExtensionFieldsKey] as NSDictionary;
|
||||||
var passwordGenerationOptions = dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary;
|
|
||||||
|
|
||||||
if(url != null)
|
if(url != null)
|
||||||
{
|
{
|
||||||
|
@ -292,31 +306,147 @@ namespace Bit.iOS.Extension
|
||||||
Password = password;
|
Password = password;
|
||||||
OldPassword = oldPassword;
|
OldPassword = oldPassword;
|
||||||
Notes = notes;
|
Notes = notes;
|
||||||
PasswordOptions = new PasswordGenerationOptions(passwordGenerationOptions);
|
PasswordOptions = DeserializeDictionary<PasswordGenerationOptions>(dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private T DeserializeDictionary<T>(NSDictionary dict)
|
||||||
|
{
|
||||||
|
if(dict != null)
|
||||||
|
{
|
||||||
|
NSError jsonError;
|
||||||
|
var jsonData = NSJsonSerialization.Serialize(dict, NSJsonWritingOptions.PrettyPrinted, out jsonError);
|
||||||
|
if(jsonData != null)
|
||||||
|
{
|
||||||
|
var jsonString = new NSString(jsonData, NSStringEncoding.UTF8);
|
||||||
|
return DeserializeString<T>(jsonString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T DeserializeString<T>(NSString jsonString)
|
||||||
|
{
|
||||||
|
if(jsonString != null)
|
||||||
|
{
|
||||||
|
var convertedObject = JsonConvert.DeserializeObject<T>(jsonString.ToString());
|
||||||
|
return convertedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
public class PasswordGenerationOptions
|
public class PasswordGenerationOptions
|
||||||
{
|
{
|
||||||
public PasswordGenerationOptions(NSDictionary dict)
|
|
||||||
{
|
|
||||||
if(dict == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(dict));
|
|
||||||
}
|
|
||||||
|
|
||||||
MinLength = (dict[AppExtensionGeneratedPasswordMinLengthKey] as NSNumber)?.Int32Value ?? 0;
|
|
||||||
MaxLength = (dict[AppExtensionGeneratedPasswordMaxLengthKey] as NSNumber)?.Int32Value ?? 0;
|
|
||||||
RequireDigits = (dict[AppExtensionGeneratedPasswordRequireDigitsKey] as NSNumber)?.BoolValue ?? false;
|
|
||||||
RequireSymbols = (dict[AppExtensionGeneratedPasswordRequireSymbolsKey] as NSNumber)?.BoolValue ?? false;
|
|
||||||
ForbiddenCharacters = (dict[AppExtensionGeneratedPasswordForbiddenCharactersKey] as NSString)?.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MinLength { get; set; }
|
public int MinLength { get; set; }
|
||||||
public int MaxLength { get; set; }
|
public int MaxLength { get; set; }
|
||||||
public bool RequireDigits { get; set; }
|
public bool RequireDigits { get; set; }
|
||||||
public bool RequireSymbols { get; set; }
|
public bool RequireSymbols { get; set; }
|
||||||
public string ForbiddenCharacters { get; set; }
|
public string ForbiddenCharacters { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class PageDetails
|
||||||
|
{
|
||||||
|
public string DocumentUUID { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Url { get; set; }
|
||||||
|
public string DocumentUrl { get; set; }
|
||||||
|
public string TabUrl { get; set; }
|
||||||
|
public Dictionary<string, Form> Forms { get; set; }
|
||||||
|
public List<Field> Fields { get; set; }
|
||||||
|
public long CollectedTimestamp { get; set; }
|
||||||
|
|
||||||
|
public class Form
|
||||||
|
{
|
||||||
|
public string OpId { get; set; }
|
||||||
|
public string HtmlName { get; set; }
|
||||||
|
public string HtmlId { get; set; }
|
||||||
|
public string HtmlAction { get; set; }
|
||||||
|
public string HtmlMethod { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Field
|
||||||
|
{
|
||||||
|
public string OpId { get; set; }
|
||||||
|
public int ElementNumber { get; set; }
|
||||||
|
public bool Visible { get; set; }
|
||||||
|
public bool Viewable { get; set; }
|
||||||
|
public string HtmlId { get; set; }
|
||||||
|
public string HtmlName { get; set; }
|
||||||
|
public string HtmlClass { get; set; }
|
||||||
|
public string LabelRight { get; set; }
|
||||||
|
public string LabelLeft { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public bool Disabled { get; set; }
|
||||||
|
public bool Readonly { get; set; }
|
||||||
|
public string OnePasswordFieldType { get; set; }
|
||||||
|
public string Form { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FillScript
|
||||||
|
{
|
||||||
|
public FillScript(PageDetails pageDetails)
|
||||||
|
{
|
||||||
|
if(pageDetails == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentUUID = pageDetails.DocumentUUID;
|
||||||
|
|
||||||
|
var loginForm = pageDetails.Forms.FirstOrDefault(form => pageDetails.Fields.Any(f => f.Form == form.Key && f.Type == "password")).Value;
|
||||||
|
if(loginForm == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Script = new List<List<string>>();
|
||||||
|
|
||||||
|
var password = pageDetails.Fields.FirstOrDefault(f =>
|
||||||
|
f.Form == loginForm.OpId
|
||||||
|
&& f.Type == "password");
|
||||||
|
|
||||||
|
var username = pageDetails.Fields.LastOrDefault(f =>
|
||||||
|
f.Form == loginForm.OpId
|
||||||
|
&& (f.Type == "text" || f.Type == "email")
|
||||||
|
&& f.ElementNumber < password.ElementNumber);
|
||||||
|
|
||||||
|
if(username != null)
|
||||||
|
{
|
||||||
|
Script.Add(new List<string> { "click_on_opid", username.OpId });
|
||||||
|
Script.Add(new List<string> { "fill_by_opid", username.OpId, "me@example.com" });
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.Add(new List<string> { "click_on_opid", password.OpId });
|
||||||
|
Script.Add(new List<string> { "fill_by_opid", password.OpId, "mypassword" });
|
||||||
|
|
||||||
|
if(loginForm.HtmlAction != null)
|
||||||
|
{
|
||||||
|
AutoSubmit = new Submit { FocusOpId = password.OpId };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "script")]
|
||||||
|
public List<List<string>> Script { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "autosubmit")]
|
||||||
|
public Submit AutoSubmit { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "documentUUID")]
|
||||||
|
public object DocumentUUID { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "properties")]
|
||||||
|
public object Properties { get; set; } = new object();
|
||||||
|
[JsonProperty(PropertyName = "options")]
|
||||||
|
public object Options { get; set; } = new object();
|
||||||
|
[JsonProperty(PropertyName = "metadata")]
|
||||||
|
public object MetaData { get; set; } = new object();
|
||||||
|
|
||||||
|
public class Submit
|
||||||
|
{
|
||||||
|
[JsonProperty(PropertyName = "focusOpid")]
|
||||||
|
public string FocusOpId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,270 +6,87 @@ BitwardenExtension.prototype = {
|
||||||
console.log(arguments);
|
console.log(arguments);
|
||||||
|
|
||||||
var args = {
|
var args = {
|
||||||
url: document.URL
|
'url_string': document.URL,
|
||||||
|
pageDetails: this.collect(document)
|
||||||
};
|
};
|
||||||
|
|
||||||
arguments.completionFunction(args);
|
arguments.completionFunction(args);
|
||||||
},
|
},
|
||||||
finalize: function (arguments) {
|
finalize: function (arguments) {
|
||||||
console.log('Finalize');
|
console.log('Finalize');
|
||||||
console.log(arguments);
|
console.log(arguments);
|
||||||
|
|
||||||
if (arguments.username || arguments.password) {
|
if (arguments.fillScript) {
|
||||||
this.fillDocument(arguments.username, arguments.password, arguments.autoSubmit);
|
this.fill(document, JSON.parse(arguments.fillScript));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getSubmitButton: function (form) {
|
|
||||||
var button;
|
|
||||||
for (var i = 0; i < form.elements.length; i++) {
|
|
||||||
if (form.elements[i].type == 'submit') {
|
|
||||||
button = form.elements[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!button) {
|
|
||||||
console.log('cannot locate submit button');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Thanks Mozilla!
|
|
||||||
// ref: http://mxr.mozilla.org/firefox/source/toolkit/components/passwordmgr/src/nsLoginManager.js?raw=1
|
|
||||||
|
|
||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is mozilla.org code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Corporation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Justin Dolske <dolske@mozilla.com> (original author)
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns an array of password field elements for the specified form.
|
1Password Extension
|
||||||
* If no pw fields are found, or if more than 3 are found, then null
|
|
||||||
* is returned.
|
|
||||||
*
|
|
||||||
* skipEmptyFields can be set to ignore password fields with no value.
|
|
||||||
*/
|
|
||||||
getPasswordFields: function (form, skipEmptyFields) {
|
|
||||||
// Locate the password fields in the form.
|
|
||||||
var pwFields = [];
|
|
||||||
for (var i = 0; i < form.elements.length; i++) {
|
|
||||||
if (form.elements[i].type != 'password') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipEmptyFields && !form.elements[i].value) {
|
Lovingly handcrafted by Dave Teare, Michael Fey, Rad Azzouz, and Roustem Karimov.
|
||||||
continue;
|
Copyright (c) 2014 AgileBits. All rights reserved.
|
||||||
}
|
|
||||||
|
|
||||||
pwFields[pwFields.length] = {
|
================================================================================
|
||||||
index: i,
|
|
||||||
element: form.elements[i]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// If too few or too many fields, bail out.
|
Copyright (c) 2014 AgileBits Inc.
|
||||||
if (pwFields.length == 0) {
|
|
||||||
console.log('form ignored -- no password fields.');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else if (pwFields.length > 3) {
|
|
||||||
console.log('form ignored -- too many password fields. got ' + pwFields.length + '.');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pwFields;
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
collect: function(document, undefined) {
|
||||||
|
document.elementsByOPID={};
|
||||||
|
function n(d,e){function f(a,b){var c=a[b];if('string'==typeof c)return c;c=a.getAttribute(b);return'string'==typeof c?c:null}function h(a,b){if(-1===['text','password'].indexOf(b.type.toLowerCase())||!(l.test(a.value)||l.test(a.htmlID)||l.test(a.htmlName)||l.test(a.placeholder)||l.test(a['label-tag'])||l.test(a['label-data'])||l.test(a['label-aria'])))return!1;if(!a.visible)return!0;if('password'==b.type.toLowerCase())return!1;var c=b.type,d=b.value;b.focus();b.value!==d&&(b.value=d);return c!==
|
||||||
|
b.type}function r(a){switch(m(a.type)){case 'checkbox':return a.checked?'✓':'';case 'hidden':a=a.value;if(!a||'number'!=typeof a.length)return'';254<a.length&&(a=a.substr(0,254)+'...SNIPPED');return a;default:return a.value}}function v(a){return a.options?(a=Array.prototype.slice.call(a.options).map(function(a){var c=a.text,c=c?m(c).replace(/\\s/mg,'').replace(/[~`!@$%^&*()\\-_+=:;'\"\\[\\]|\\\\,<.>\\?]/mg,''):null;return[c?c:null,a.value]}),{options:a}):null}function F(a){var b;for(a=a.parentElement||a.parentNode;a&&
|
||||||
|
'td'!=m(a.tagName);)a=a.parentElement||a.parentNode;if(!a||void 0===a)return null;b=a.parentElement||a.parentNode;if('tr'!=b.tagName.toLowerCase())return null;b=b.previousElementSibling;if(!b||'tr'!=(b.tagName+'').toLowerCase()||b.cells&&a.cellIndex>=b.cells.length)return null;a=s(b.cells[a.cellIndex]);return a=u(a)}function A(a){var b=d.documentElement,c=a.getBoundingClientRect(),e=b.getBoundingClientRect(),f=c.left-b.clientLeft,b=c.top-b.clientTop;return a.offsetParent?0>f||f>e.width||0>b||b>e.height?
|
||||||
|
w(a):(e=a.ownerDocument.elementFromPoint(f+3,b+3))?'label'===m(e.tagName)?e===B(a):e.tagName===a.tagName:!1:!1}function w(a){for(var b;a!==d&&a;a=a.parentNode){b=t.getComputedStyle?t.getComputedStyle(a,null):a.style;if(!b)return!0;if('none'===b.display||'hidden'==b.visibility)return!1}return a===d}function B(a){var b=[];a.id&&(b=b.concat(Array.prototype.slice.call(x(d,'label[for='+JSON.stringify(a.id)+']'))));a.name&&(b=b.concat(Array.prototype.slice.call(x(d,'label[for='+JSON.stringify(a.name)+']'))));
|
||||||
|
if(0<b.length)return b.map(function(a){return s(a)}).join('');for(;a&&a!=d;a=a.parentNode)if('label'===m(a.tagName))return s(a);return null}function g(a,b,c,d){void 0!==d&&d===c||null===c||void 0===c||(a[b]=c)}function m(a){return'string'===typeof a?a.toLowerCase():(''+a).toLowerCase()}function x(a,b){var c=[];try{c=a.querySelectorAll(b)}catch(d){}return c}var t=d.defaultView?d.defaultView:window,p,l=RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|passe|contraseña|senha|密码|adgangskode|hasło|wachtwoord)',
|
||||||
|
'i');p=Array.prototype.slice.call(x(d,'form')).map(function(a,b){var c={},d='__form__'+b;a.opid=d;c.opid=d;g(c,'htmlName',f(a,'name'));g(c,'htmlID',f(a,'id'));g(c,'htmlAction',y(f(a,'action')));g(c,'htmlMethod',f(a,'method'));return c});var q=Array.prototype.slice.call(z(d)).map(function(a,b){var c={},e='__'+b,k=-1==a.maxLength?999:a.maxLength;if(!k||'number'===typeof k&&isNaN(k))k=999;d.elementsByOPID[e]=a;a.opid=e;c.opid=e;c.elementNumber=b;g(c,'maxLength',Math.min(k,999),999);c.visible=w(a);c.viewable=
|
||||||
|
A(a);g(c,'htmlID',f(a,'id'));g(c,'htmlName',f(a,'name'));g(c,'htmlClass',f(a,'class'));g(c,'tabindex',f(a,'tabindex'));if('hidden'!=m(a.type)){g(c,'label-tag',B(a));g(c,'label-data',f(a,'data-label'));g(c,'label-aria',f(a,'aria-label'));g(c,'label-top',F(a));e=[];for(k=a;k&&k.nextSibling;){k=k.nextSibling;if(C(k))break;D(e,k)}g(c,'label-right',e.join(''));e=[];E(a,e);e=e.reverse().join('');g(c,'label-left',e);g(c,'placeholder',f(a,'placeholder'))}g(c,'rel',f(a,'rel'));g(c,'type',m(f(a,'type')));g(c,
|
||||||
|
'value',r(a));g(c,'checked',a.checked,!1);g(c,'autoCompleteType',a.getAttribute('x-autocompletetype')||a.getAttribute('autocompletetype')||a.getAttribute('autocomplete'),'off');g(c,'disabled',a.disabled);g(c,'readonly',a.a||a.readOnly);g(c,'selectInfo',v(a));g(c,'aria-hidden','true'==a.getAttribute('aria-hidden'),!1);g(c,'aria-disabled','true'==a.getAttribute('aria-disabled'),!1);g(c,'aria-haspopup','true'==a.getAttribute('aria-haspopup'),!1);g(c,'data-unmasked',a.dataset.unmasked);g(c,'data-stripe',
|
||||||
|
f(a,'data-stripe'));g(c,'onepasswordFieldType',a.dataset.onepasswordFieldType||a.type);g(c,'onepasswordDesignation',a.dataset.onepasswordDesignation);g(c,'onepasswordSignInUrl',a.dataset.onepasswordSignInUrl);g(c,'onepasswordSectionTitle',a.dataset.onepasswordSectionTitle);g(c,'onepasswordSectionFieldKind',a.dataset.onepasswordSectionFieldKind);g(c,'onepasswordSectionFieldTitle',a.dataset.onepasswordSectionFieldTitle);g(c,'onepasswordSectionFieldValue',a.dataset.onepasswordSectionFieldValue);a.form&&
|
||||||
|
(c.form=f(a.form,'opid'));g(c,'fakeTested',h(c,a),!1);return c});q.filter(function(a){return a.fakeTested}).forEach(function(a){var b=d.elementsByOPID[a.opid];b.getBoundingClientRect();var c=b.value;!b||b&&'function'!==typeof b.click||b.click();b.focus();G(b,'keydown');G(b,'keyup');G(b,'keypress');b.value!==c&&(b.value=c);b.click&&b.click();a.postFakeTestVisible=w(b);a.postFakeTestViewable=A(b);a.postFakeTestType=b.type;a=b.value;var c=b.ownerDocument.createEvent('HTMLEvents'),e=b.ownerDocument.createEvent('HTMLEvents');
|
||||||
|
G(b,'keydown');G(b,'keyup');G(b,'keypress');e.initEvent('input',!0,!0);b.dispatchEvent(e);c.initEvent('change',!0,!0);b.dispatchEvent(c);b.blur();b.value!==a&&(b.value=a)});p={documentUUID:e,title:d.title,url:t.location.href,documentUrl:d.location.href,tabUrl:t.location.href,forms:function(a){var b={};a.forEach(function(a){b[a.opid]=a});return b}(p),fields:q,collectedTimestamp:(new Date).getTime()};(q=document.querySelector('[data-onepassword-display-title]'))&&q.dataset[DISPLAY_TITLE_ATTRIBUE]&&
|
||||||
|
(p.displayTitle=q.dataset.onepasswordTitle);return p};document.elementForOPID=H;function G(d,e){var f;f=d.ownerDocument.createEvent('KeyboardEvent');f.initKeyboardEvent?f.initKeyboardEvent(e,!0,!0):f.initKeyEvent&&f.initKeyEvent(e,!0,!0,null,!1,!1,!1,!1,0,0);d.dispatchEvent(f)}window.LOGIN_TITLES=[/^\\W*log\\W*[oi]n\\W*$/i,/log\\W*[oi]n (?:securely|now)/i,/^\\W*sign\\W*[oi]n\\W*$/i,'continue','submit','weiter','accès','вход','connexion','entrar','anmelden','accedi','valider','登录','लॉग इन करें'];window.LOGIN_RED_HERRING_TITLES=['already have an account','sign in with'];
|
||||||
|
window.REGISTER_TITLES='register;sign up;signup;join;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');window.SEARCH_TITLES='search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');window.FORGOT_PASSWORD_TITLES='forgot geändert vergessen hilfe changeemail español'.split(' ');window.REMEMBER_ME_TITLES=['remember me','rememberme','keep me signed in'];window.BACK_TITLES=['back','назад'];
|
||||||
|
function s(d){return d.textContent||d.innerText}function u(d){var e=null;d&&(e=d.replace(/^\\s+|\\s+$|\\r?\\n.*$/mg,''),e=0<e.length?e:null);return e}function D(d,e){var f;f='';3===e.nodeType?f=e.nodeValue:1===e.nodeType&&(f=s(e));(f=u(f))&&d.push(f)}function C(d){var e;d&&void 0!==d?(e='select option input form textarea button table iframe body head script'.split(' '),d?(d=d?(d.tagName||'').toLowerCase():'',e=e.constructor==Array?0<=e.indexOf(d):d===e):e=!1):e=!0;return e}
|
||||||
|
function E(d,e,f){var h;for(f||(f=0);d&&d.previousSibling;){d=d.previousSibling;if(C(d))return;D(e,d)}if(d&&0===e.length){for(h=null;!h;){d=d.parentElement||d.parentNode;if(!d)return;for(h=d.previousSibling;h&&!C(h)&&h.lastChild;)h=h.lastChild}C(h)||(D(e,h),0===e.length&&E(h,e,f+1))}}
|
||||||
|
function H(d){var e;if(void 0===d||null===d)return null;try{var f=Array.prototype.slice.call(z(document)),h=f.filter(function(e){return e.opid==d});if(0<h.length)e=h[0],1<h.length&&console.warn('More than one element found with opid '+d);else{var r=parseInt(d.split('__')[1],10);isNaN(r)||(e=f[r])}}catch(v){console.error('An unexpected error occurred: '+v)}finally{return e}};var I=/^[\\/\\?]/;function y(d){if(!d)return null;if(0==d.indexOf('http'))return d;var e=window.location.protocol+'//'+window.location.hostname;window.location.port&&''!=window.location.port&&(e+=':'+window.location.port);d.match(I)||(d='/'+d);return e+d}function z(d){var e=[];try{e=d.querySelectorAll('input, select, button')}catch(f){}return e};
|
||||||
|
return JSON.stringify(n(document, 'oneshotUUID'));
|
||||||
},
|
},
|
||||||
/*
|
fill: function(document, fillScript, undefined) {
|
||||||
* Returns the username and password fields found in the form.
|
var f=!0,h=!0;
|
||||||
* Can handle complex forms by trying to figure out what the
|
function l(a){var b=null;return a?0===a.indexOf('https://')&&'http:'===document.location.protocol&&(b=document.querySelectorAll('input[type=password]'),0<b.length&&(confirmResult=confirm('Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page.\\n\\nDo you still wish to fill this login?'),0==confirmResult))?!0:!1:!1}
|
||||||
* relevant fields are.
|
function k(a){var b,c=[],d=a.properties,e=1,g;d&&d.delay_between_operations&&(e=d.delay_between_operations);if(!l(a.savedURL)){g=function(a,b){var d=a[0];void 0===d?b():('delay'===d.operation||'delay'===d[0]?e=d.parameters?d.parameters[0]:d[1]:c.push(m(d)),setTimeout(function(){g(a.slice(1),b)},e))};if(b=a.options)b.hasOwnProperty('animate')&&(h=b.animate),b.hasOwnProperty('markFilling')&&(f=b.markFilling);a.itemType&&'fillPassword'===a.itemType&&(f=!1);a.hasOwnProperty('script')&&(b=a.script,g(b,
|
||||||
*
|
function(){c=Array.prototype.concat.apply(c,void 0);a.hasOwnProperty('autosubmit')&&'function'==typeof autosubmit&&(a.itemType&&'fillLogin'!==a.itemType||setTimeout(function(){autosubmit(a.autosubmit,d.allow_clicky_autosubmit)},AUTOSUBMIT_DELAY));'object'==typeof protectedGlobalPage&&protectedGlobalPage.a('fillItemResults',{documentUUID:documentUUID,fillContextIdentifier:a.fillContextIdentifier,usedOpids:c},function(){fillingItemType=null})}))}}
|
||||||
* Returns: [usernameField, newPasswordField, oldPasswordField]
|
var v={fill_by_opid:n,fill_by_query:p,click_on_opid:q,click_on_query:r,touch_all_fields:s,simple_set_value_by_query:t,focus_by_opid:u,delay:null};function m(a){var b;if(a.hasOwnProperty('operation')&&a.hasOwnProperty('parameters'))b=a.operation,a=a.parameters;else if('[object Array]'===Object.prototype.toString.call(a))b=a[0],a=a.splice(1);else return null;return v.hasOwnProperty(b)?v[b].apply(this,a):null}function n(a,b){var c;return(c=w(a))?(x(c,b),c.opid):null}
|
||||||
*
|
function p(a,b){var c;c=y(a);return Array.prototype.map.call(Array.prototype.slice.call(c),function(a){x(a,b);return a.opid},this)}function t(a,b){var c,d=[];c=y(a);Array.prototype.forEach.call(Array.prototype.slice.call(c),function(a){void 0!==a.value&&(a.value=b,d.push(a.opid))});return d}function u(a){if(a=w(a))'function'===typeof a.click&&a.click(),'function'===typeof a.focus&&a.focus();return null}function q(a){return(a=w(a))?z(a)?a.opid:null:null}
|
||||||
* usernameField may be null.
|
function r(a){a=y(a);return Array.prototype.map.call(Array.prototype.slice.call(a),function(a){z(a);'function'===typeof a.click&&a.click();'function'===typeof a.focus&&a.focus();return a.opid},this)}function s(){A()};var B={'true':!0,y:!0,1:!0,yes:!0,'✓':!0},C=200;function x(a,b){var c;if(a&&null!==b&&void 0!==b)switch(f&&a.form&&!a.form.opfilled&&(a.form.opfilled=!0),a.type?a.type.toLowerCase():null){case 'checkbox':c=b&&1<=b.length&&B.hasOwnProperty(b.toLowerCase())&&!0===B[b.toLowerCase()];a.checked===c||D(a,function(a){a.checked=c});break;case 'radio':!0===B[b.toLowerCase()]&&a.click();break;default:a.value==b||D(a,function(a){a.value=b})}}
|
||||||
* newPasswordField will always be non-null.
|
function D(a,b){E(a);b(a);F(a);G(a)&&(a.className+=' com-agilebits-onepassword-extension-animated-fill',setTimeout(function(){a&&a.className&&(a.className=a.className.replace(/(\\s)?com-agilebits-onepassword-extension-animated-fill/,''))},C))};document.elementForOPID=w;function H(a,b){var c;c=a.ownerDocument.createEvent('KeyboardEvent');c.initKeyboardEvent?c.initKeyboardEvent(b,!0,!0):c.initKeyEvent&&c.initKeyEvent(b,!0,!0,null,!1,!1,!1,!1,0,0);a.dispatchEvent(c)}function E(a){var b=a.value;z(a);a.focus();H(a,'keydown');H(a,'keyup');H(a,'keypress');a.value!==b&&(a.value=b)}
|
||||||
* oldPasswordField may be null. If null, newPasswordField is just
|
function F(a){var b=a.value,c=a.ownerDocument.createEvent('HTMLEvents'),d=a.ownerDocument.createEvent('HTMLEvents');H(a,'keydown');H(a,'keyup');H(a,'keypress');d.initEvent('input',!0,!0);a.dispatchEvent(d);c.initEvent('change',!0,!0);a.dispatchEvent(c);a.blur();a.value!==b&&(a.value=b)}function z(a){if(!a||a&&'function'!==typeof a.click)return!1;a.click();return!0}
|
||||||
* "theLoginField". If not null, the form is apparently a
|
function I(){var a=RegExp('((\\\\b|_|-)pin(\\\\b|_|-)|password|passwort|kennwort|passe|contraseña|senha|密码|adgangskode|hasło|wachtwoord)','i');return Array.prototype.slice.call(y("input[type='text']")).filter(function(b){return b.value&&a.test(b.value)},this)}function A(){I().forEach(function(a){E(a);a.click&&a.click();F(a)})}
|
||||||
* change-password field, with oldPasswordField containing the password
|
window.LOGIN_TITLES=[/^\\W*log\\W*[oi]n\\W*$/i,/log\\W*[oi]n (?:securely|now)/i,/^\\W*sign\\W*[oi]n\\W*$/i,'continue','submit','weiter','accès','вход','connexion','entrar','anmelden','accedi','valider','登录','लॉग इन करें'];window.LOGIN_RED_HERRING_TITLES=['already have an account','sign in with'];window.REGISTER_TITLES='register;sign up;signup;join;регистрация;inscription;regístrate;cadastre-se;registrieren;registrazione;注册;साइन अप करें'.split(';');window.SEARCH_TITLES='search find поиск найти искать recherche suchen buscar suche ricerca procurar 検索'.split(' ');
|
||||||
* that is being changed.
|
window.FORGOT_PASSWORD_TITLES='forgot geändert vergessen hilfe changeemail español'.split(' ');window.REMEMBER_ME_TITLES=['remember me','rememberme','keep me signed in'];window.BACK_TITLES=['back','назад'];
|
||||||
*/
|
function G(a){var b;if(b=h)a:{b=a;for(var c=a.ownerDocument,c=c?c.defaultView:{},d;b&&b!==document;){d=c.getComputedStyle?c.getComputedStyle(b,null):b.style;if(!d){b=!0;break a}if('none'===d.display||'hidden'==d.visibility){b=!1;break a}b=b.parentNode}b=b===document}return b?-1!=='email text password number tel url'.split(' ').indexOf(a.type||''):!1}
|
||||||
getFormFields: function (form, isSubmission) {
|
function w(a){var b;if(void 0===a||null===a)return null;try{var c=Array.prototype.slice.call(y('input, select, button')),d=c.filter(function(b){return b.opid==a});if(0<d.length)b=d[0],1<d.length&&console.warn('More than one element found with opid '+a);else{var e=parseInt(a.split('__')[1],10);isNaN(e)||(b=c[e])}}catch(g){console.error('An unexpected error occurred: '+g)}finally{return b}};function y(a){var b=document,c=[];try{c=b.querySelectorAll(a)}catch(d){}return c};
|
||||||
var usernameField = null,
|
k(fillScript);
|
||||||
submitButton = null;
|
return JSON.stringify({'success': true});
|
||||||
|
|
||||||
// Locate the password field(s) in the form. Up to 3 supported.
|
|
||||||
// If there's no password field, there's nothing for us to do.
|
|
||||||
var pwFields = this.getPasswordFields(form, isSubmission);
|
|
||||||
if (!pwFields) {
|
|
||||||
return [null, null, null, null];
|
|
||||||
}
|
|
||||||
|
|
||||||
submitButton = this.getSubmitButton(form);
|
|
||||||
|
|
||||||
// Locate the username field in the form by searching backwards
|
|
||||||
// from the first passwordfield, assume the first text field is the
|
|
||||||
// username. We might not find a username field if the user is
|
|
||||||
// already logged in to the site.
|
|
||||||
for (var i = pwFields[0].index - 1; i >= 0; i--) {
|
|
||||||
if (form.elements[i].type == 'text'
|
|
||||||
|| form.elements[i].type == 'email'
|
|
||||||
|| form.elements[i].type == 'tel') {
|
|
||||||
usernameField = form.elements[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!usernameField) {
|
|
||||||
console.log('form -- no username field found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're not submitting a form (it's a page load), there are no
|
|
||||||
// password field values for us to use for identifying fields. So,
|
|
||||||
// just assume the first password field is the one to be filled in.
|
|
||||||
if (!isSubmission || pwFields.length == 1) {
|
|
||||||
return [usernameField, pwFields[0].element, null, submitButton];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to figure out WTF is in the form based on the password values.
|
|
||||||
var oldPasswordField, newPasswordField;
|
|
||||||
var pw1 = pwFields[0].element.value;
|
|
||||||
var pw2 = pwFields[1].element.value;
|
|
||||||
var pw3 = (pwFields[2] ? pwFields[2].element.value : null);
|
|
||||||
|
|
||||||
if (pwFields.length == 3) {
|
|
||||||
// Look for two identical passwords, that's the new password
|
|
||||||
|
|
||||||
if (pw1 == pw2 && pw2 == pw3) {
|
|
||||||
// All 3 passwords the same? Weird! Treat as if 1 pw field.
|
|
||||||
newPasswordField = pwFields[0].element;
|
|
||||||
oldPasswordField = null;
|
|
||||||
}
|
|
||||||
else if (pw1 == pw2) {
|
|
||||||
newPasswordField = pwFields[0].element;
|
|
||||||
oldPasswordField = pwFields[2].element;
|
|
||||||
}
|
|
||||||
else if (pw2 == pw3) {
|
|
||||||
oldPasswordField = pwFields[0].element;
|
|
||||||
newPasswordField = pwFields[2].element;
|
|
||||||
}
|
|
||||||
else if (pw1 == pw3) {
|
|
||||||
// A bit odd, but could make sense with the right page layout.
|
|
||||||
newPasswordField = pwFields[0].element;
|
|
||||||
oldPasswordField = pwFields[1].element;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// We can't tell which of the 3 passwords should be saved.
|
|
||||||
console.log('form ignored -- all 3 pw fields differ');
|
|
||||||
return [null, null, null, null];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // pwFields.length == 2
|
|
||||||
if (pw1 == pw2) {
|
|
||||||
// Treat as if 1 pw field
|
|
||||||
newPasswordField = pwFields[0].element;
|
|
||||||
oldPasswordField = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Just assume that the 2nd password is the new password
|
|
||||||
oldPasswordField = pwFields[0].element;
|
|
||||||
newPasswordField = pwFields[1].element;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [usernameField, newPasswordField, oldPasswordField, submitButton];
|
|
||||||
},
|
|
||||||
fillDocument: function (username, password, autoSubmit) {
|
|
||||||
if (!password) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!document.forms || document.forms.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < document.forms.length; i++) {
|
|
||||||
var fields = this.getFormFields(document.forms[i], false);
|
|
||||||
var usernameField = fields[0],
|
|
||||||
passwordField = fields[1],
|
|
||||||
submitButton = fields[3];
|
|
||||||
|
|
||||||
if (!usernameField && !passwordField) {
|
|
||||||
console.log('cannot locate fields in form #' + i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxUsernameLength = Number.MAX_VALUE,
|
|
||||||
maxPasswordLength = Number.MAX_VALUE;
|
|
||||||
|
|
||||||
var filledUsername = false,
|
|
||||||
filledPassword = false;
|
|
||||||
|
|
||||||
if (username && usernameField) {
|
|
||||||
if (usernameField.maxLength >= 0) {
|
|
||||||
maxUsernameLength = usernameField.maxLength;
|
|
||||||
}
|
|
||||||
if (username.length <= maxUsernameLength) {
|
|
||||||
usernameField.value = username;
|
|
||||||
filledUsername = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (passwordField) {
|
|
||||||
if (passwordField.maxLength >= 0) {
|
|
||||||
maxPasswordLength = passwordField.maxLength;
|
|
||||||
}
|
|
||||||
if (password.length <= maxPasswordLength) {
|
|
||||||
passwordField.value = password;
|
|
||||||
filledPassword = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autoSubmit && filledPassword && filledPassword) {
|
|
||||||
setTimeout(function () {
|
|
||||||
if (submitButton) {
|
|
||||||
submitButton.click();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
document.forms[i].submit();
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,9 @@
|
||||||
<None Include="Info.plist" />
|
<None Include="Info.plist" />
|
||||||
<None Include="Entitlements.plist" />
|
<None Include="Entitlements.plist" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.Practices.ServiceLocation, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
@ -110,6 +112,10 @@
|
||||||
<HintPath>..\..\packages\Unity.3.5.1405-prerelease\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
|
<HintPath>..\..\packages\Unity.3.5.1405-prerelease\lib\portable-net45+wp80+win8+wpa81+MonoAndroid10+MonoTouch10\Microsoft.Practices.Unity.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\..\packages\Newtonsoft.Json.8.0.3\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="SQLite-net, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="SQLite-net, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\..\packages\sqlite-net-pcl.1.1.1\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll</HintPath>
|
<HintPath>..\..\packages\sqlite-net-pcl.1.1.1\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="CommonServiceLocator" version="1.3" targetFramework="xamarinios10" />
|
<package id="CommonServiceLocator" version="1.3" targetFramework="xamarinios10" />
|
||||||
|
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="xamarinios10" />
|
||||||
<package id="sqlite-net-pcl" version="1.1.1" targetFramework="xamarinios10" />
|
<package id="sqlite-net-pcl" version="1.1.1" targetFramework="xamarinios10" />
|
||||||
<package id="SQLitePCL.raw" version="0.8.6" targetFramework="xamarinios10" />
|
<package id="SQLitePCL.raw" version="0.8.6" targetFramework="xamarinios10" />
|
||||||
<package id="Unity" version="3.5.1405-prerelease" targetFramework="xamarinios10" />
|
<package id="Unity" version="3.5.1405-prerelease" targetFramework="xamarinios10" />
|
||||||
|
|
Loading…
Reference in a new issue