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

js/background/inject/inject.js (11 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) {
0 ignored issues
show
Compatibility Best Practice introduced by
You are extending the built-in type Array. This may have unintended consequences on other objects using this built-in type. Consider subclassing instead.
Loading history...
17
        return this.filter(function (item) {
18
            return typeof item === 'string' && item.indexOf(match) > -1;
19
        });
20
    };
21
22
    function removePasswordPicker() {
23
        activeForm = undefined;
24
        $j('.passwordPickerIframe').remove();
25
    }
26
27
    _this.removePasswordPicker = removePasswordPicker;
28
29
    function enterLoginDetails(login, allowSubmit) {
30
        var username;
31
32
        if (login.hasOwnProperty('username')) {
33
            username = (login.username !== '' ) ? login.username : login.email;
34
        }
35
        if (!username) {
36
            username = null;
37
        }
38
39
        fillPassword(username, login.password);
40
41
        if (activeForm) {
42
            API.runtime.sendMessage(API.runtime.id, {method: 'isAutoSubmitEnabled'}).then(function (isEnabled) {
43
                if (isEnabled && allowSubmit) {
44
                    submitLoginForm(username);
45
                }
46
            });
47
        }
48
    }
49
50
    _this.enterLoginDetails = enterLoginDetails;
51
52
    function submitLoginForm(username) {
53
        if (!activeForm) {
54
            // @TODO detect login form on the current page
55
            return;
56
        }
57
58
        var formEl = $j(activeForm).closest('form');
59
        var iframeUrl = API.extension.getURL('/html/inject/auto_login.html');
60
        $j('#loginPopupIframe').remove();
61
        var loginPopup = $j('<iframe class="loginPopupIframe" scrolling="no" frameborder="0" src="' + iframeUrl + '"></iframe>');
62
        var padding = parseInt($j(formEl).css('padding').replace('px', ''));
63
        var margin = parseInt($j(formEl).css('margin').replace('px', ''));
64
        var height = Math.round($j(formEl).height() + (padding * 2) + (margin * 2));
65
        var width = Math.round($j(formEl).width() + (padding * 2) + (margin * 2));
66
        loginPopup.attr('height', height);
67
        loginPopup.attr('width', width);
68
        loginPopup.css('position', 'absolute');
69
        loginPopup.css('z-index', getMaxZ() + 1);
70
        loginPopup.css('background-color', 'rgba(0, 0, 0, 0.73)');
71
        loginPopup.css('left', Math.floor($j(formEl).offset().left - padding - margin));
72
        loginPopup.css('top', Math.floor($j(formEl).offset().top - padding - margin));
73
        removePasswordPicker();
74
        $j(document.body).prepend(loginPopup);
75
        API.runtime.sendMessage(API.runtime.id, {'setIframeUsername': username}).then(function () {
76
            $j(formEl).submit();
77
            setTimeout(function () {
78
                loginPopup.remove();
79
            }, 2000);
80
        });
81
    }
82
83
    function getMaxZ() {
84
        return Math.max.apply(null,
85
            $j.map($j('body *'), function (e) {
86
                if ($j(e).css('position') !== 'static')
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $j(e).css("position") !== "static" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
87
                    return parseInt($j(e).css('z-index')) || 1;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
88
            }));
89
    }
90
91
    var activeForm;
92
93
    function showPasswordPicker(form) {
94
        var jPasswordPicker = $j('.passwordPickerIframe');
95
        if (jPasswordPicker.length > 1) {
96
            return;
97
        }
98
        var loginField = $j(form[0]);
99
        var loginFieldPos = loginField.offset();
100
        var loginFieldVisible = loginField.is(':visible');
101
102
        var position = $j(form[1]).position();
0 ignored issues
show
The assignment to variable position seems to be never used. Consider removing it.
Loading history...
103
        var passwordField = $j(form[1]);
104
        var passwordFieldPos = passwordField.offset();
105
        var passwordFieldVisible = loginField.is(':visible');
0 ignored issues
show
The variable passwordFieldVisible seems to be never used. Consider removing it.
Loading history...
106
        var left = (loginFieldPos) ? loginFieldPos.left : passwordFieldPos.left;
107
        var top = (loginFieldPos) ? loginFieldPos.top : passwordFieldPos.top;
108
        var maxZ = getMaxZ();
109
110
        if (loginFieldPos && passwordFieldPos.top > loginFieldPos.top) {
111
            //console.log('login fields below each other')
112
            top = passwordFieldPos.top + passwordField.height() + 10;
113
        } else {
114
            // console.log('login fields next to each other')
115
            if (loginFieldPos) {
116
                top = top + loginField.height() + 10;
117
            } else {
118
                top = top + passwordField.height() + 10;
119
            }
120
        }
121
        if (!loginFieldVisible) {
122
            left = passwordFieldPos.left;
123
        }
124
125
        var pickerUrl = API.extension.getURL('/html/inject/password_picker.html');
126
127
        var picker = $j('<iframe class="passwordPickerIframe" scrolling="no" height="385" width="350" frameborder="0" src="' + pickerUrl + '"></iframe>');
128
        picker.css('position', 'absolute');
129
        picker.css('left', left);
130
        picker.css('z-index', maxZ + 10);
131
        picker.css('top', top);
132
        $j('body').prepend($j(picker));
133
        activeForm = form;
134
        // picker.css('width', $j(form).width());
135
        $j('.passwordPickerIframe:not(:last)').remove();
136
    }
137
138
    function onFormIconClick(e) {
139
        e.preventDefault();
140
        e.stopPropagation();
141
        var offsetX = e.offsetX;
142
        var offsetRight = (e.data.width - offsetX);
143
        if (offsetRight < e.data.height) {
144
            showPasswordPicker(e.data.form);
145
        }
146
    }
147
148
    function createFormIcon(el, form) {
149
        var offset = el.offset();
0 ignored issues
show
The assignment to variable offset seems to be never used. Consider removing it.
Loading history...
150
        var width = el.width();
151
        var height = el.height() * 1;
152
        var margin = (el.css('margin')) ? parseInt(el.css('margin').replace('px', '')) : 0;
0 ignored issues
show
The variable margin seems to be never used. Consider removing it.
Loading history...
153
        var padding = (el.css('padding')) ? parseInt(el.css('padding').replace('px', '')) : 0;
0 ignored issues
show
The variable padding seems to be never used. Consider removing it.
Loading history...
154
155
        var pickerIcon = API.extension.getURL('/icons/icon.svg');
156
        $j(el).css('background-image', 'url("' + pickerIcon + '")');
157
        $j(el).css('background-repeat', 'no-repeat');
158
        //$j(el).css('background-position', '');
159
        $j(el).css('cssText', el.attr('style') + ' background-position: right 3px center !important;');
160
161
        $j(el).unbind('click', onFormIconClick);
162
        $j(el).click({width: width, height: height, form: form}, onFormIconClick);
163
    }
164
165
    function createPasswordPicker(form) {
166
        for (var i = 0; i < form.length; i++) {
167
            var el = $j(form[i]);
168
            createFormIcon(el, form);
169
        }
170
    }
171
172
    function formSubmitted(fields) {
173
        var user = fields[0].value;
174
        var pass = fields[1].value;
175
        var params = {
176
            username: user,
177
            password: pass
178
        };
179
        //Disable password mining
180
        //$j(fields[1]).attr('type', 'hidden');
181
        API.runtime.sendMessage(API.runtime.id, {method: "minedForm", args: params});
182
183
    }
184
185
    function inIframe() {
186
        try {
187
            return window.self !== window.top;
188
        } catch (e) {
189
            return true;
190
        }
191
    }
192
193
    function showDoorhanger(data) {
194
        if (inIframe()) {
195
            return;
196
        }
197
        data.data.currentLocation = window.location.href;
198
        API.runtime.sendMessage(API.runtime.id, {method: "setDoorhangerData", args: data});
199
        var pickerUrl = API.extension.getURL('/html/inject/doorhanger.html');
200
201
        var doorhanger = $j('<iframe id="password-toolbarIframe" style="display: none;" scrolling="no" height="60" width="100%" frameborder="0" src="' + pickerUrl + '"></iframe>');
202
        $j('#password-toolbarIframe').remove();
203
        doorhanger.css('z-index', getMaxZ() + 1);
204
        $j('body').prepend(doorhanger);
205
        $j('#password-toolbarIframe').fadeIn();
206
    }
207
208
    _this.showDoorhanger = showDoorhanger;
209
210
    function showUrlUpdateDoorhanger(data) {
211
        var buttons = ['cancel', 'updateUrl'];
212
        showDoorhanger({
213
            data: data.data,
214
            buttons: buttons
215
        });
216
    }
217
218
    _this.showUrlUpdateDoorhanger = showUrlUpdateDoorhanger;
219
220
    function checkForMined() {
221
        if (inIframe()) {
222
            return;
223
        }
224
225
        API.runtime.sendMessage(API.runtime.id, {method: "getMinedData"}).then(function (data) {
226
            if (!data) {
227
                return;
228
            }
229
            if (data.hasOwnProperty('username') && data.hasOwnProperty('password') && data.hasOwnProperty('url')) {
230
                var buttons = ['cancel', 'ignore', 'save'];
231
                showDoorhanger({data: data, buttons: buttons});
232
            }
233
        });
234
    }
235
236
237
    function closeDoorhanger() {
238
        $j('#password-toolbarIframe').hide(400);
239
        $j('#password-toolbarIframe').remove();
240
    }
241
242
    _this.closeDoorhanger = closeDoorhanger;
243
244
    function initForms() {
245
        API.runtime.sendMessage(API.runtime.id, {method: 'getRuntimeSettings'}).then(function (settings) {
246
            var enablePasswordPicker = settings.enablePasswordPicker;
247
            var url = window.location.href;
248
            var loginFields = getLoginFields();
249
            if (loginFields.length > 0) {
250
                for (var i = 0; i < loginFields.length; i++) {
251
                    var form = getFormFromElement(loginFields[i][0]);
252
                    if (enablePasswordPicker) {
253
                        createPasswordPicker(loginFields[i], form);
0 ignored issues
show
The call to createPasswordPicker seems to have too many arguments starting with form.
Loading history...
254
                    }
255
                    //Password miner
256
                    /* jshint ignore:start */
257
                    if (!settings.hasOwnProperty('ignored_sites') || settings.ignored_sites.findUrl(url) !== -1) {
258
                        $j(form).submit((function (loginFields) {
259
                            return function () {
260
                                formSubmitted(loginFields);
261
                            };
262
                        })(loginFields[i]));
263
                    }
264
                    /* jshint ignore:end */
265
                }
266
267
                API.runtime.sendMessage(API.runtime.id, {
268
                    method: "getCredentialsByUrl",
269
                    args: url
270
                }).then(function (logins) {
271
                    console.log('Found ' + logins.length + ' logins for this site');
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
272
                    if (logins.length === 1) {
273
                        API.runtime.sendMessage(API.runtime.id, {method: 'isAutoFillEnabled'}).then(function (isEnabled) {
274
                            if (isEnabled) {
275
                                enterLoginDetails(logins[0], false);
276
                            }
277
                        });
278
                    }
279
280
                });
281
            }
282
283
        });
284
    }
285
286
    function minedLoginSaved(args) {
287
        // If the login added by the user then this is true
288
        if (args.selfAdded) {
289
            enterLoginDetails(args.credential, false);
290
        }
291
    }
292
293
    _this.minedLoginSaved = minedLoginSaved;
294
295
    function resizeIframe(height) {
296
        $j('#password-toolbarIframe').height(60 + height);
297
    }
298
299
    _this.resizeIframe = resizeIframe;
300
301
    function copyText(text) {
302
        var txtToCopy = document.createElement('input');
303
        txtToCopy.style.left = '-300px';
304
        txtToCopy.style.position = 'absolute';
305
        txtToCopy.value = text;
306
        document.body.appendChild(txtToCopy);
307
        txtToCopy.select();
308
        document.execCommand('copy');
309
        txtToCopy.parentNode.removeChild(txtToCopy);
310
    }
311
312
    _this.copyText = copyText;
313
314
    function init() {
315
        checkForMined();
316
        initForms();
317
    }
318
319
    var readyStateCheckInterval = setInterval(function () {
320
        if (document.readyState === "complete") {
321
            clearInterval(readyStateCheckInterval);
322
            API.runtime.sendMessage(API.runtime.id, {method: 'getMasterPasswordSet'}).then(function (result) {
323
                if (result) {
324
                    init();
325
                    var body = document.getElementsByTagName('body')[0];
326
                    observeDOM(body, initForms);
327
                } else {
328
                    console.log('[Passman extension] Stopping, vault key not set');
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
329
                }
330
            });
331
        }
332
    }, 10);
333
334
    API.runtime.onMessage.addListener(function (msg, sender) {
335
        //console.log('Method call', msg.method);
336
        if (_this[msg.method]) {
337
            _this[msg.method](msg.args, sender);
338
        }
339
    });
340
});
341