Completed
Push — master ( d0679c...037b4f )
by Sander
33s
created

js/background/inject/inject.js (2 issues)

1
/* global API */
2
var $j = jQuery.noConflict();
3
4
$j(document).ready(function () {
5
6
    $j(document).click(function (event) {
7
        var passwordPickerRef = '.passwordPickerIframe';
8
        if (!$j(event.target).closest(passwordPickerRef).length) {
9
            if ($j(passwordPickerRef).is(":visible")) {
10
                removePasswordPicker();
11
            }
12
        }
13
    });
14
15
    var _this = this;
16
    Array.prototype.findUrl = function (match) {
17
        return this.filter(function (item) {
18
            var matchParse = processURL(match, false, false, true, false);
19
            return typeof item === 'string' && item.indexOf(matchParse) > -1;
20
        });
21
    };
22
23
    function removePasswordPicker() {
24
        activeForm = undefined;
25
        $j('.passwordPickerIframe').remove();
26
    }
27
28
    _this.removePasswordPicker = removePasswordPicker;
29
30
    function enterLoginDetails(login, allowSubmit) {
31
        var username;
32
33
        if (login.hasOwnProperty('username')) {
34
            username = (login.username !== '' ) ? login.username : login.email;
35
        }
36
        if (!username) {
37
            username = null;
38
        }
39
40
        fillPassword(username, login.password);
41
42
        if (activeForm) {
43
            API.runtime.sendMessage(API.runtime.id, {method: 'isAutoSubmitEnabled'}).then(function (isEnabled) {
44
                if (isEnabled && allowSubmit) {
45
                    submitLoginForm(username);
46
                }
47
            });
48
        }
49
    }
50
51
    _this.enterLoginDetails = enterLoginDetails;
52
53
    function enterCustomFields(login,settings) {
54
		var customFieldPattern=/^\#(.*)$/;
55
		var elementId;
56
		var element=false;
0 ignored issues
show
The variable element seems to be never used. Consider removing it.
Loading history...
57
		
58
		/* parhaps wise to try / catch this as this is non essential and no reason to abort previous processing */
59
		try{
60
			/* do we have custom_fields for this entry */
61
			if(login.hasOwnProperty('custom_fields')&&login.custom_fields.length){
62
				/* yes we do, iterate over all the custom_fields values */
63
				for(var i=0,len=login.custom_fields.length;i<len;i++){
64
					/* does this custom field label begin with a hash? */
65
					if(customFieldPattern.test(login.custom_fields[i].label)){
66
						/* set variable elementid to whatever element we are trying to auto fill */
67
						elementId=customFieldPattern.exec(login.custom_fields[i].label)[1];
68
						enterCustomFieldElement(elementId, login.custom_fields[i].value);
69
					}
70
					else if ($j('label[for]:contains(' + login.custom_fields[i].label + ')').length) {
71
						elementId=$j('label[for]:contains(' + login.custom_fields[i].label + ')').attr('for');
72
						enterCustomFieldElement(elementId, login.custom_fields[i].value);
73
					}
74
				}
75
			}
76
		}
77
		catch(e){
78
			if(settings.debug){
79
                console.log('While attempting to auto fill custom fields the following exception was thrown: '+e);
80
			}
81
		}
82
	}
83
    
84
    function enterCustomFieldElement(elementId, value) {
85
    	/* check to see if element id exist */
86
	if($j('#'+elementId).length){
87
		element=$j('#'+elementId);
0 ignored issues
show
The variable element seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.element.
Loading history...
88
	}
89
	else if($j('input[name$="'+elementId+'"]').length){ /* maybe element name exist */
90
		element=$j('input[name$="'+elementId+'"]');
91
	}
92
	else{ /* neither element id or name exist */
93
		element=false;
94
	}
95
	/* if we have an element and it is type text, number or password, lets auto fill it */
96
	if(element&&(element[0].type==='text'||element[0].type==='number'||element[0].type==='password')){
97
		element.val(value);
98
	}
99
    }
100
	
101
    function submitLoginForm(username) {
102
        if (!activeForm) {
103
            // @TODO detect login form on the current page
104
            return;
105
        }
106
107
        var formEl = $j(activeForm).closest('form');
108
        var iframeUrl = API.extension.getURL('/html/inject/auto_login.html');
109
        $j('#loginPopupIframe').remove();
110
        var loginPopup = $j('<iframe class="loginPopupIframe" scrolling="no" frameborder="0" src="' + iframeUrl + '"></iframe>');
111
        var padding = parseInt($j(formEl).css('padding').replace('px', ''));
112
        var margin = parseInt($j(formEl).css('margin').replace('px', ''));
113
        var height = Math.round($j(formEl).height() + (padding * 2) + (margin * 2));
114
        var width = Math.round($j(formEl).width() + (padding * 2) + (margin * 2));
115
        loginPopup.attr('height', height);
116
        loginPopup.attr('width', width);
117
        loginPopup.css('position', 'absolute');
118
        loginPopup.css('z-index', getMaxZ() + 1);
119
        loginPopup.css('background-color', 'rgba(0, 0, 0, 0.73)');
120
        loginPopup.css('left', Math.floor($j(formEl).offset().left - padding - margin));
121
        loginPopup.css('top', Math.floor($j(formEl).offset().top - padding - margin));
122
        removePasswordPicker();
123
        $j(document.body).prepend(loginPopup);
124
        API.runtime.sendMessage(API.runtime.id, {'setIframeUsername': username}).then(function () {
125
            $j(formEl).submit();
126
            setTimeout(function () {
127
                loginPopup.remove();
128
            }, 2000);
129
        });
130
    }
131
132
    function getMaxZ() {
133
        return Math.max.apply(null,
134
            $j.map($j('body *'), function (e) {
135
                if ($j(e).css('position') !== 'static')
136
                    return parseInt($j(e).css('z-index')) || 1;
137
            }));
138
    }
139
140
    var activeForm;
141
142
    function showPasswordPicker(form) {
143
        var jPasswordPicker = $j('.passwordPickerIframe');
144
        if (jPasswordPicker.length > 1) {
145
            return;
146
        }
147
        var loginField = $j(form[0]);
148
        var loginFieldPos = loginField.offset();
149
        var loginFieldVisible = loginField.is(':visible');
150
151
        var position = $j(form[1]).position();
152
        var passwordField = $j(form[1]);
153
        var passwordFieldPos = passwordField.offset();
154
        var passwordFieldVisible = loginField.is(':visible');
155
        var left = (loginFieldPos) ? loginFieldPos.left : passwordFieldPos.left;
156
        var top = (loginFieldPos) ? loginFieldPos.top : passwordFieldPos.top;
157
        var maxZ = getMaxZ();
158
159
        if (loginFieldPos && passwordFieldPos.top > loginFieldPos.top) {
160
            //console.log('login fields below each other')
161
            top = passwordFieldPos.top + passwordField.height() + 10;
162
        } else {
163
            // console.log('login fields next to each other')
164
            if (loginFieldPos) {
165
                top = top + loginField.height() + 10;
166
            } else {
167
                top = top + passwordField.height() + 10;
168
            }
169
        }
170
        if (!loginFieldVisible) {
171
            left = passwordFieldPos.left;
172
        }
173
174
        var pickerUrl = API.extension.getURL('/html/inject/password_picker.html');
175
176
        var picker = $j('<iframe class="passwordPickerIframe" scrolling="no" height="385" width="350" frameborder="0" src="' + pickerUrl + '"></iframe>');
177
        picker.css('position', 'absolute');
178
        picker.css('left', left);
179
        picker.css('z-index', maxZ + 10);
180
        picker.css('top', top);
181
        $j('body').prepend($j(picker));
182
        activeForm = form;
183
        // picker.css('width', $j(form).width());
184
        $j('.passwordPickerIframe:not(:last)').remove();
185
    }
186
187
    function onFormIconClick(e) {
188
        e.preventDefault();
189
        e.stopPropagation();
190
        var offsetX = e.offsetX;
191
        var offsetRight = (e.data.width - offsetX);
192
        if (offsetRight < e.data.height) {
193
            showPasswordPicker(e.data.form);
194
        }
195
    }
196
197
    function createFormIcon(el, form) {
198
        var offset = el.offset();
199
        var width = el.width();
200
        var height = el.height() * 1;
201
        var margin = (el.css('margin')) ? parseInt(el.css('margin').replace('px', '')) : 0;
202
        var padding = (el.css('padding')) ? parseInt(el.css('padding').replace('px', '')) : 0;
203
204
        var pickerIcon = API.extension.getURL('/icons/icon.svg');
205
        $j(el).css('background-image', 'url("' + pickerIcon + '")');
206
        $j(el).css('background-repeat', 'no-repeat');
207
        //$j(el).css('background-position', '');
208
        $j(el).css('cssText', el.attr('style') + ' background-position: right 3px center !important;');
209
210
        $j(el).unbind('click', onFormIconClick);
211
        $j(el).click({width: width, height: height, form: form}, onFormIconClick);
212
    }
213
214
    function createPasswordPicker(form) {
215
        for (var i = 0; i < form.length; i++) {
216
            var el = $j(form[i]);
217
            createFormIcon(el, form);
218
        }
219
    }
220
221
    function formSubmitted(fields) {
222
        var user = fields[0].value;
223
        var pass = fields[1].value;
224
        var params = {
225
            username: user,
226
            password: pass
227
        };
228
        //Disable password mining
229
        //$j(fields[1]).attr('type', 'hidden');
230
        API.runtime.sendMessage(API.runtime.id, {method: "minedForm", args: params});
231
232
    }
233
234
    function inIframe() {
235
        try {
236
            return window.self !== window.top;
237
        } catch (e) {
238
            return true;
239
        }
240
    }
241
242
    function showDoorhanger(data) {
243
        if (inIframe()) {
244
            return;
245
        }
246
        data.data.currentLocation = window.location.href;
247
        API.runtime.sendMessage(API.runtime.id, {method: "setDoorhangerData", args: data});
248
        var pickerUrl = API.extension.getURL('/html/inject/doorhanger.html');
249
250
        var doorhanger = $j('<iframe id="password-toolbarIframe" style="display: none;" scrolling="no" height="60" width="100%" frameborder="0" src="' + pickerUrl + '"></iframe>');
251
        $j('#password-toolbarIframe').remove();
252
        doorhanger.css('z-index', getMaxZ() + 1);
253
        $j('body').prepend(doorhanger);
254
        $j('#password-toolbarIframe').fadeIn();
255
    }
256
257
    _this.showDoorhanger = showDoorhanger;
258
259
    function showUrlUpdateDoorhanger(data) {
260
        var buttons = ['cancel', 'updateUrl'];
261
        showDoorhanger({
262
            data: data.data,
263
            buttons: buttons
264
        });
265
    }
266
267
    _this.showUrlUpdateDoorhanger = showUrlUpdateDoorhanger;
268
269
    function checkForMined() {
270
        if (inIframe()) {
271
            return;
272
        }
273
274
        API.runtime.sendMessage(API.runtime.id, {method: "getMinedData"}).then(function (data) {
275
            if (!data) {
276
                return;
277
            }
278
            if (data.hasOwnProperty('username') && data.hasOwnProperty('password') && data.hasOwnProperty('url')) {
279
                var buttons = ['cancel', 'ignore', 'save'];
280
                showDoorhanger({data: data, buttons: buttons});
281
            }
282
        });
283
    }
284
285
286
    function closeDoorhanger() {
287
        $j('#password-toolbarIframe').hide(400);
288
        $j('#password-toolbarIframe').remove();
289
    }
290
291
    _this.closeDoorhanger = closeDoorhanger;
292
293
    function initForms() {
294
        API.runtime.sendMessage(API.runtime.id, {method: 'getRuntimeSettings'}).then(function (settings) {
295
            var enablePasswordPicker = settings.enablePasswordPicker;
296
            var url = window.location.href;
297
            var loginFields = getLoginFields();
298
            if (!settings.hasOwnProperty('ignored_sites') || settings.ignored_sites.findUrl(url).length !== 0) {
299
                return;
300
            }
301
302
            if (loginFields.length > 0) {
303
                for (var i = 0; i < loginFields.length; i++) {
304
                    var form = getFormFromElement(loginFields[i][0]);
305
                        if (enablePasswordPicker) {
306
                            createPasswordPicker(loginFields[i], form);
307
                        }
308
309
                        //Password miner
310
                        /* jshint ignore:start */
311
                        $j(form).submit((function (loginFields) {
312
                            return function () {
313
                                formSubmitted(loginFields);
314
                            };
315
                        })(loginFields[i]));
316
                        /* jshint ignore:end */
317
                }
318
319
                API.runtime.sendMessage(API.runtime.id, {
320
                    method: "getCredentialsByUrl",
321
                    args: url
322
                }).then(function (logins) {
323
                    console.log('Found ' + logins.length + ' logins for this site');
324
                    if (logins.length === 1) {
325
                        API.runtime.sendMessage(API.runtime.id, {method: 'isAutoFillEnabled'}).then(function (isEnabled) {
326
                            if (isEnabled) {
327
                                enterLoginDetails(logins[0], false);
328
                            }
329
                        });
330
                    }
331
                });
332
            }
333
334
            API.runtime.sendMessage(API.runtime.id, {
335
                method: "getCredentialsByUrl",
336
                args: url
337
            }).then(function (logins) {
338
                if (logins.length === 1) {
339
                    API.runtime.sendMessage(API.runtime.id, {method: 'isAutoFillEnabled'}).then(function (isEnabled) {
340
                        if (isEnabled) {
341
                            enterCustomFields(logins[0], settings);
342
                        }
343
                    });
344
                    }
345
            });
346
            
347
        });
348
    }
349
350
    function minedLoginSaved(args) {
351
        // If the login added by the user then this is true
352
        if (args.selfAdded) {
353
            showDoorhanger({
354
                data: args,
355
                buttons: ['cancel']
356
            });
357
            enterLoginDetails(args.credential, false);
358
        }
359
    }
360
361
    _this.minedLoginSaved = minedLoginSaved;
362
363
    function resizeIframe(height) {
364
        $j('#password-toolbarIframe').height(60 + height);
365
    }
366
367
    _this.resizeIframe = resizeIframe;
368
369
    function copyText(text) {
370
        var txtToCopy = document.createElement('input');
371
        txtToCopy.style.left = '-300px';
372
        txtToCopy.style.position = 'absolute';
373
        txtToCopy.value = text;
374
        document.body.appendChild(txtToCopy);
375
        txtToCopy.select();
376
        document.execCommand('copy');
377
        txtToCopy.parentNode.removeChild(txtToCopy);
378
    }
379
380
    _this.copyText = copyText;
381
382
    function init() {
383
        checkForMined();
384
        initForms();
385
    }
386
387
    var readyStateCheckInterval = setInterval(function () {
388
        if (document.readyState === "complete") {
389
            clearInterval(readyStateCheckInterval);
390
            API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'}).then(function (result) {
391
                if (result) {
392
                    init();
393
                    var body = document.getElementsByTagName('body')[0];
394
                    if(body) {
395
                        observeDOM(body, initForms);
396
                    }
397
                } else {
398
                    console.log('[Passman extension] Stopping, vault key not set');
399
                }
400
            });
401
        }
402
    }, 10);
403
404
    API.runtime.onMessage.addListener(function (msg, sender) {
405
        //console.log('Method call', msg.method);
406
        if (_this[msg.method]) {
407
            _this[msg.method](msg.args, sender);
408
        }
409
    });
410
});
411