Completed
Push — master ( 4e8691...51d7d6 )
by Sander
55s
created

js/lib/findForm.js (1 issue)

Labels
1
var formManager = function(){
2
    /**
3
     Code based on:
4
     @url https://dxr.mozilla.org/firefox/source/toolkit/components/passwordmgr/src/nsLoginManager.js#655
5
     */
6
    var settings = {};
7
8
    return {
9
        _init_: function () {
10
            API.runtime.sendMessage(API.runtime.id, {method: 'getSetting', args: 'debug'}).then(function (result) {
11
                settings.debug = (result);
12
            });
13
        },
14
        /*
15
         * _isAutoCompleteDisabled
16
         *
17
         * Returns true if the page requests autocomplete be disabled for the
18
         * specified form input.
19
         */
20
        isAutocompleteDisabled: function (element) {
21
            return !!(element && element.hasAttribute("autocomplete") && element.getAttribute("autocomplete").toLowerCase() === "off");
22
        },
23
24
        /*
25
         * _getPasswordFields
26
         *
27
         * Returns an array of password field elements for the specified form.
28
         * If no pw fields are found, or if more than 3 are found, then null
29
         * is returned.
30
         *
31
         * skipEmptyFields can be set to ignore password fields with no value.
32
         */
33
        _getPasswordFields: function (form, skipEmptyFields) {
34
            // Locate the password fields in the form.
35
            var pwFields = [];
36
            for (var i = 0; i < form.elements.length; i++) {
37
                if (form.elements[i].type !== "password"){
38
                    continue;
39
                }
40
41
42
                if (skipEmptyFields && !form.elements[i].value){
43
                    continue;
44
                }
45
46
                pwFields[pwFields.length] = {
47
                    index: i,
48
                    element: form.elements[i]
49
                };
50
            }
51
52
            // If too few or too many fields, bail out.
53
            if (pwFields.length === 0) {
54
                this.log('(form ignored ('+ form.action +') -- no password fields.)');
55
                return null;
56
            } else if (pwFields.length > 3) {
57
                this.log('(form ignored -- too many password fields. [got ' +
58
                    pwFields.length + "])");
59
                return null;
60
            }
61
62
            return pwFields;
63
        },
64
        /*
65
         * _getFormFields
66
         *
67
         * Returns the username and password fields found in the form.
68
         * Can handle complex forms by trying to figure out what the
69
         * relevant fields are.
70
         *
71
         * Returns: [usernameField, newPasswordField, oldPasswordField]
72
         *
73
         * usernameField may be null.
74
         * newPasswordField will always be non-null.
75
         * oldPasswordField may be null. If null, newPasswordField is just
76
         * "theLoginField". If not null, the form is apparently a
77
         * change-password field, with oldPasswordField containing the password
78
         * that is being changed.
79
         */
80
        getFormFields: function (form, isSubmission) {
81
            var usernameField = null;
82
83
            // Locate the password field(s) in the form. Up to 3 supported.
84
            // If there's no password field, there's nothing for us to do.
85
            var pwFields = this._getPasswordFields(form, isSubmission);
86
            if (!pwFields){
87
                return [null, null, null];
88
            }
89
90
91
            // Locate the username field in the form by searching backwards
92
            // from the first passwordfield, assume the first text field is the
93
            // username. We might not find a username field if the user is
94
            // already logged in to the site.
95
            for (var i = pwFields[0].index - 1; i >= 0; i--) {
96
                if (form.elements[i].type.toLowerCase() === "text" || form.elements[i].type.toLowerCase() === "email") {
97
                    usernameField = form.elements[i];
98
                    break;
99
                }
100
            }
101
102
            if (!usernameField){
103
                this.log('(form ('+ form.action +') ignored -- no username field found)');
104
            }
105
106
107
108
            // If we're not submitting a form (it's a page load), there are no
109
            // password field values for us to use for identifying fields. So,
110
            // just assume the first password field is the one to be filled in.
111
            if (!isSubmission || pwFields.length === 1){
112
                return [usernameField, pwFields[0].element, null];
113
            }
114
115
116
117
            // Try to figure out WTF is in the form based on the password values.
118
            var oldPasswordField, newPasswordField;
119
            var pw1 = pwFields[0].element.value;
120
            var pw2 = pwFields[1].element.value;
121
            var pw3 = (pwFields[2] ? pwFields[2].element.value : null);
122
123
            if (pwFields.length === 3) {
124
                // Look for two identical passwords, that's the new password
125
126
                if (pw1 === pw2 && pw2 === pw3) {
127
                    // All 3 passwords the same? Weird! Treat as if 1 pw field.
128
                    newPasswordField = pwFields[0].element;
129
                    oldPasswordField = null;
130
                } else if (pw1 === pw2) {
131
                    newPasswordField = pwFields[0].element;
132
                    oldPasswordField = pwFields[2].element;
133
                } else if (pw2 === pw3) {
134
                    oldPasswordField = pwFields[0].element;
135
                    newPasswordField = pwFields[2].element;
136
                } else if (pw1 === pw3) {
137
                    // A bit odd, but could make sense with the right page layout.
138
                    newPasswordField = pwFields[0].element;
139
                    oldPasswordField = pwFields[1].element;
140
                } else {
141
                    // We can't tell which of the 3 passwords should be saved.
142
                    this.log("(form ignored -- all 3 pw fields differ)");
143
                    return [null, null, null];
144
                }
145
            } else { // pwFields.length == 2
146
                if (pw1 === pw2) {
147
                    // Treat as if 1 pw field
148
                    newPasswordField = pwFields[0].element;
149
                    oldPasswordField = null;
150
                } else {
151
                    // Just assume that the 2nd password is the new password
152
                    oldPasswordField = pwFields[0].element;
153
                    newPasswordField = pwFields[1].element;
154
                }
155
            }
156
157
            return [usernameField, newPasswordField, oldPasswordField];
158
        },
159
        log: function (str) {
160
            if(settings.debug){
161
                console.log(str);
162
            }
163
        }
164
    };
165
}();
166
167
function getLoginFields(isSubmission) {
168
    var forms = document.forms;
169
    var loginForms = [];
170
171
    for (var i = 0; i < forms.length; i++) {
172
        var form = forms[i];
173
        var result = formManager.getFormFields(form, isSubmission);
174
        var usernameField = result[0];
175
        var passwordField = result[1];
176
        // Need a valid password field to do anything.
177
        if (passwordField === null){
178
            continue;
179
        }
180
        loginForms.push([usernameField, passwordField]);
181
    }
182
    return loginForms;
183
}
184
185
function getFormFromElement(elem) {
186
    if(elem) {
187
        while (elem.parentNode) {
188
            if (elem.parentNode.nodeName.toLowerCase() === "form") {
189
                return elem.parentNode;
190
            }
191
            elem = elem.parentNode;
192
        }
193
    }
194
}
195
196
function dispatchEvents(element){
197
    var eventNames = [ 'click', 'focus', 'keypress', 'keydown', 'keyup', 'input', 'blur', 'change' ];
198
    eventNames.forEach(function(eventName) {
199
        element.dispatchEvent(new Event(eventName, {"bubbles":true}));
0 ignored issues
show
The variable Event seems to be never declared. If this is a global, consider adding a /** global: Event */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
200
    });
201
}
202
203
function fillPassword(user, password) {
204
    var loginFields = getLoginFields();
205
    for (var i = 0; i < loginFields.length; i++) {
206
        if(user && loginFields[i][0]){
207
            loginFields[i][0].value = user;
208
            if(loginFields[i][0].offsetParent) {
209
                dispatchEvents(loginFields[i][0]);
210
            }
211
        }
212
        if(password && loginFields[i][1]) {
213
            loginFields[i][1].value = password;
214
            if(loginFields[i][1].offsetParent) {
215
                dispatchEvents(loginFields[i][1]);
216
            }
217
        }
218
    }
219
220
}
221
formManager._init_();