Completed
Push — master ( 5cd6e6...ca6f58 )
by Sander
14s
created

background.js ➔ ... ➔ storage.then   D

Complexity

Conditions 15
Paths 130

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
c 1
b 0
f 0
nc 130
nop 1
dl 0
loc 48
rs 4.7139

How to fix   Complexity   

Complexity

Complex classes like background.js ➔ ... ➔ storage.then often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        try {
53
            PAPI.decryptString(testMasterPasswordAgainst, password);
54
            return true;
55
        } catch (e) {
56
            return false;
57
        }
58
    }
59
60
    _self.isMasterPasswordValid = isMasterPasswordValid;
61
62
63
    var local_credentials = [];
64
    var local_vault = [];
0 ignored issues
show
Unused Code introduced by
The variable local_vault seems to be never used. Consider removing it.
Loading history...
65
    var encryptedFieldSettings = ['accounts'];
66
    _self.settings = {};
67
    _self.ticker = null;
68
    _self.running = false;
69
    function getSettings() {
70
71
        storage.get('settings').then(function (_settings) {
72
            if ((!_settings || Object.keys(_settings).length === 0 || !_settings.hasOwnProperty('accounts')) && !master_password) {
73
                return;
74
            }
75
            if (!master_password && _settings.hasOwnProperty('accounts') && _settings.accounts.length > 0) {
76
                _self.settings.isInstalled = 1;
77
                testMasterPasswordAgainst = _settings.accounts;
78
                return;
79
            }
80
81
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
82
                var field = encryptedFieldSettings[i];
83
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
84
            }
85
86
            _self.settings = _settings;
87
88
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
89
                _self.settings.ignored_sites = [];
90
            }
91
92
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
93
                _self.settings.no_results_found_tab = 'list';
94
            }
95
96
            if (!_self.settings.hasOwnProperty('enablePasswordPicker')) {
97
                _self.settings.enablePasswordPicker = !_self.settings.disablePasswordPicker;
98
            }
99
100
            if (!_self.settings.hasOwnProperty('enableAutoFill')) {
101
                _self.settings.enableAutoFill = !_self.settings.disableAutoFill;
102
            }
103
104
            if (!_self.settings.hasOwnProperty('enableUpdateUrl')) {
105
                _self.settings.enableUpdateUrl = true;
106
            }
107
108
            getCredentials();
109
110
            if (_self.running) {
111
                clearInterval(_self.ticker);
112
            }
113
            _self.running = true;
114
            _self.ticker = setInterval(function () {
115
116
            }, _self.settings.refreshTime * 1000);
117
118
        });
119
    }
120
121
    _self.getSettings = getSettings;
122
123
    function getRuntimeSettings() {
124
        return _self.settings;
125
    }
126
127
    _self.getRuntimeSettings = getRuntimeSettings;
128
129
    function getSetting(name) {
130
        return _self.settings[name];
131
    }
132
133
    _self.getSetting = getSetting;
134
135
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
136
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
137
            var field = encryptedFieldSettings[i];
138
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
139
        }
140
141
        if (!settings.hasOwnProperty('ignored_sites')) {
142
            settings.ignored_sites = [];
143
        }
144
145
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
146
            _self.settings.disable_browser_autofill = 'list';
147
        }
148
149
        //window.settings contains the run-time settings
150
        _self.settings = settings;
151
152
153
        storage.set('settings', settings).then(function () {
154
            getSettings();
155
        });
156
157
    }
158
159
    _self.saveSettings = saveSettings;
160
161
    function resetSettings() {
162
        storage.set('settings', {});
163
        _self.settings = {};
164
    }
165
166
    _self.resetSettings = resetSettings;
167
168
169
    function getCredentials() {
170
        if (!master_password) {
171
            return;
172
        }
173
        //console.log('Loading vault with the following settings: ', settings);
174
        var tmpList = [];
175
176
        for (var i = 0; i < _self.settings.accounts.length; i++) {
177
            var account = _self.settings.accounts[i];
178
            /* jshint ignore:start */
179
            (function (inner_account) {
180
                PAPI.getVault(inner_account, function (vault) {
181
                    if (vault.hasOwnProperty('error')) {
182
                        return;
183
                    }
184
                    var _credentials = vault.credentials;
185
                    for (var i = 0; i < _credentials.length; i++) {
186
                        var key = inner_account.vault_password;
187
                        var credential = _credentials[i];
188
                        if (credential.hidden === 1) {
189
                            continue;
190
                        }
191
                        var usedKey = key;
192
                        //Shared credentials are not implemented yet
193
                        if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
194
                            usedKey = PAPI.decryptString(credential.shared_key, key);
195
196
                        }
197
                        credential = PAPI.decryptCredential(credential, usedKey);
198
                        credential.account = inner_account;
199
                        if (credential.delete_time === 0) {
200
                            tmpList.push(credential);
201
                        }
202
203
                    }
204
                    delete vault.credentials;
205
                    local_vault = vault;
0 ignored issues
show
Unused Code introduced by
The variable local_vault seems to be never used. Consider removing it.
Loading history...
206
                    local_credentials = tmpList;
207
208
                    getSharedCredentials(inner_account);
209
210
211
                });
212
            }(account));
213
            /* jshint ignore:end */
214
        }
215
    }
216
217
    _self.getCredentials = getCredentials;
218
219
    function getSharedCredentials(account) {
220
        PAPI.getCredendialsSharedWithUs(account, account.vault.guid, function (credentials) {
221
            for (var i = 0; i < credentials.length; i++) {
222
                var _shared_credential = credentials[i];
223
                var _shared_credential_data;
224
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, account.vault_password);
225
                try {
226
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
227
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
228
229
                }
230
                if (_shared_credential_data) {
231
                    delete _shared_credential.credential_data;
232
                    _shared_credential_data.acl = _shared_credential;
233
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ 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...
234
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
235
                    _shared_credential_data.account = account;
236
                    local_credentials.push(_shared_credential_data);
237
                }
238
            }
239
            updateTabsIcon();
240
        });
241
    }
242
243
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
244
        if (!master_password) {
245
            return [];
246
        }
247
        if (!_url || _url === '') {
248
            return [];
249
        }
250
        if (Array.isArray(_url)) {
251
            _url = _url.pop();
252
        }
253
254
        var p = document.createElement('a');
255
        p.href = _url;
256
        if (p.pathname) {
257
            //_url = _url.substring(0, _url.lastIndexOf("/"));
258
        }
259
260
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
261
        var found_list = [];
262
        for (var i = 0; i < local_credentials.length; i++) {
263
            var credential_url = local_credentials[i].url;
264
            if (!/^(ht)tps?:\/\//i.test(credential_url) && credential_url !== '' && _url) {
265
                try {
266
                    var protocol = _url.split('://').shift();
267
                    credential_url = protocol + "://" + credential_url;
268
                } catch (e) {
269
                    //ignore
270
                }
271
            }
272
            credential_url = processURL(credential_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
273
            if (credential_url) {
274
                if (credential_url.split("\n").indexOf(url) !== -1) {
275
                    found_list.push(local_credentials[i]);
276
                }
277
            }
278
279
        }
280
        return found_list;
281
    }
282
283
    _self.getCredentialsByUrl = getCredentialsByUrl;
284
285
286
    function saveCredential(credential) {
287
        //@TODO save shared password
288
        if (!credential.credential_id) {
289
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
290
                local_credentials.push(createdCredential);
291
            });
292
        } else {
293
            var credential_index;
294
            for (var i = 0; i < local_credentials.length; i++) {
295
                if (local_credentials[i].guid === credential.guid) {
296
                    credential_index = i;
297
                    break;
298
                }
299
            }
300
301
            if (credential.hasOwnProperty('acl')) {
302
                var permissons = new SharingACL(credential.acl.permissions.permission);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ 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...
303
                if (!permissons.hasPermission(0x02)) {
304
                    return;
305
                }
306
            }
307
308
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
309
                if (credential_index) {
310
                    local_credentials[credential_index] = updatedCredential;
311
                }
312
            });
313
        }
314
    }
315
316
    _self.saveCredential = saveCredential;
317
318
    function getCredentialByGuid(guid) {
319
        for (var i = 0; i < local_credentials.length; i++) {
320
            var credential = local_credentials[i];
321
            if (credential.guid === guid) {
322
                return credential;
323
            }
324
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
325
    }
326
327
    _self.getCredentialByGuid = getCredentialByGuid;
328
329
    function getCredentialForHTTPAuth(req) {
330
        return getCredentialsByUrl(req.url)[0];
331
    }
332
333
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
334
335
    var mined_data = [];
336
337
    function minedForm(data, sender) {
338
        var url = sender.url;
339
        var existingLogins = getCredentialsByUrl(sender.url);
340
        var title = API.i18n.getMessage('detected_new_login') + ':';
341
        var minedMatchingID = null;
342
        for (var j = 0; j < existingLogins.length; j++) {
343
            var login = existingLogins[j];
344
            if (login.username === data.username) {
345
                if (login.password !== data.password) {
346
                    minedMatchingID = login.guid;
347
                    title = API.i18n.getMessage('detected_changed_login') + ':';
348
                }
349
                else {
350
                    //console.log('No changes detected');
351
                    delete mined_data[sender.tab.id];
352
                    return;
353
                }
354
            }
355
        }
356
        mined_data[sender.tab.id] = {
357
            title: title,
358
            url: url,
359
            username: data.username,
360
            password: data.password,
361
            label: sender.title,
362
            guid: minedMatchingID
363
        };
364
365
        //console.log('Done mining, ', mined_data, sender.tab.id);
366
    }
367
368
    _self.minedForm = minedForm;
369
370
    function getMinedData(args, sender) {
371
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
372
        var senderUrl = sender.tab.url;
373
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
374
        if (!_self.settings) {
375
            return null;
376
        }
377
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
378
            return mined_data[sender.tab.id];
379
        }
380
        var matches = _self.settings.ignored_sites.filter(function (item) {
381
            return typeof item === 'string' && site.indexOf(item) > -1;
382
        });
383
384
        if (matches.length !== 0) {
385
            return null;
386
        }
387
        return mined_data[sender.tab.id];
388
    }
389
390
    _self.getMinedData = getMinedData;
391
392
    function clearMined(args, sender) {
393
        delete mined_data[sender.tab.id];
394
    }
395
396
    _self.clearMined = clearMined;
397
398
    function saveMinedCallback(args) {
399
        createIconForTab(args.sender.tab);
400
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
401
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
402
            });
403
        });
404
    }
405
406
    function ignoreSite(_url) {
407
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
408
            _self.settings.ignored_sites = [];
409
        }
410
        var site = processURL(_url, false, false, true, false);
411
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
412
            _self.settings.ignored_sites.push(site);
413
            saveSettings(_self.settings);
414
        }
415
    }
416
417
    _self.ignoreSite = ignoreSite;
418
419
    function ignoreURL(url) {
420
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
421
            _self.settings.ignored_sites = [];
422
        }
423
        if (_self.settings.ignored_sites.indexOf(url) === -1) {
424
            _self.settings.ignored_sites.push(url);
425
            saveSettings(_self.settings);
426
        }
427
    }
428
429
    _self.ignoreURL = ignoreURL;
430
431
    function passToParent(args, sender) {
432
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
433
        });
434
    }
435
436
    _self.passToParent = passToParent;
437
438
    function getActiveTab(opt) {
439
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
440
            var tab = tabs[0];
441
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
442
            });
443
        });
444
    }
445
446
    _self.getActiveTab = getActiveTab;
447
448
    function updateCredentialUrlDoorhanger(login) {
449
        if(!_self.settings.enableUpdateUrl){
450
            return;
451
        }
452
453
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
454
            var tab = tabs[0];
455
            var data = login;
456
            data.url = tab.url;
457
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
458
            API.tabs.sendMessage(tab.id, {
459
                method: 'showUrlUpdateDoorhanger',
460
                args: {data: data}
461
            });
462
        });
463
    }
464
465
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
466
467
    function updateCredentialUrl(data, sender) {
468
        mined_data[sender.tab.id] = data;
469
        saveMined({}, sender);
470
471
    }
472
473
    _self.updateCredentialUrl = updateCredentialUrl;
474
475
    function saveMined(args, sender) {
476
        var data = mined_data[sender.tab.id];
477
        var credential = {},
478
            credential_index;
479
480
        if (data.guid === null) {
481
            credential = PAPI.newCredential();
482
        } else {
483
            for (var i = 0; i < local_credentials.length; i++) {
484
                if (local_credentials[i].guid === data.guid) {
485
                    credential = local_credentials[i];
486
                    credential_index = i;
487
                    break;
488
                }
489
            }
490
        }
491
        if (!credential.hasOwnProperty('account')) {
492
            credential.account = args.account;
493
        }
494
        credential.username = data.username;
495
        credential.password = data.password;
496
        credential.url = sender.tab.url;
497
        if (credential.guid !== null) {
498
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
499
                updatedCredential.account = credential.account;
500
                if (credential_index) {
501
                    local_credentials[credential_index] = updatedCredential;
502
                }
503
                saveMinedCallback({credential: credential, updated: true, sender: sender});
504
                delete mined_data[sender.tab.id];
505
            });
506
        } else {
507
            credential.label = sender.tab.title;
508
            credential.vault_id = credential.account.vault.vault_id;
509
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
510
                createdCredential.account = credential.account;
511
                saveMinedCallback({credential: credential, updated: false, sender: sender});
512
                local_credentials.push(createdCredential);
513
                delete mined_data[sender.tab.id];
514
            });
515
        }
516
    }
517
518
    _self.saveMined = saveMined;
519
520
    function searchCredential(searchText) {
521
        searchText = searchText.toLowerCase();
522
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
523
        var results = [];
524
        for (var i = 0; i < local_credentials.length; i++) {
525
            var credential = local_credentials[i];
526
            for (var f = 0; f < searchFields.length; f++) {
527
                var field = searchFields[f];
528
                if (!credential[field]) {
529
                    continue;
530
                }
531
532
                var field_value = credential[field].toLowerCase();
533
                if (field_value.indexOf(searchText) !== -1) {
534
                    results.push(credential);
535
                    break;
536
                }
537
            }
538
        }
539
        return results;
540
    }
541
542
    _self.searchCredential = searchCredential;
543
544
545
    function injectCreateCredential(args, sender) {
546
        var account = getRuntimeSettings().accounts[args.vaultIndex];
547
        var credential = PAPI.newCredential();
548
        credential.label = args.label;
549
        credential.username = args.username;
550
        credential.password = args.password;
551
        credential.vault_id = account.vault_id;
552
        credential.url = sender.tab.url;
553
        PAPI.createCredential(account, credential, account.vault_password, function (createdCredential) {
554
            credential.account = account;
555
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
556
            local_credentials.push(createdCredential);
557
558
        });
559
    }
560
561
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ 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...
562
563
    function isVaultKeySet() {
564
        return (_self.settings.vault_password !== null);
565
    }
566
567
    _self.isVaultKeySet = isVaultKeySet;
568
569
    function isAutoFillEnabled() {
570
        if (!_self.settings.hasOwnProperty('enableAutoFill')) {
571
            return true;
572
        }
573
        return _self.settings.enableAutoFill;
574
    }
575
576
    _self.isAutoFillEnabled = isAutoFillEnabled;
577
578
    function isAutoSubmitEnabled() {
579
        if (!_self.settings.hasOwnProperty('enableAutoSubmit')) {
580
            return false;
581
        }
582
        return _self.settings.enableAutoSubmit;
583
    }
584
585
    _self.isAutoSubmitEnabled = isAutoSubmitEnabled;
586
587
    var doorhangerData = null;
588
589
    function setDoorhangerData(data) {
590
        doorhangerData = data;
591
    }
592
593
    _self.setDoorhangerData = setDoorhangerData;
594
595
    function getDoorhangerData() {
596
        return doorhangerData;
597
    }
598
599
    _self.getDoorhangerData = getDoorhangerData;
600
601
    function closeSetupTab() {
602
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
603
            if (tabs && tabs[0]) {
604
                API.tabs.remove(tabs[0].id);
605
            }
606
        });
607
    }
608
609
    _self.closeSetupTab = closeSetupTab;
610
611
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
612
613
        if (!msg || !msg.hasOwnProperty('method')) {
614
            return;
615
        }
616
        var result = false;
617
        if (_self[msg.method]) {
618
            result = _self[msg.method](msg.args, sender);
619
        } else {
620
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
621
        }
622
623
        sendResponse(result);
624
    });
625
626
    var defaultColor = '#0082c9';
627
628
    function createIconForTab(tab) {
629
        if (!master_password) {
630
            return;
631
        }
632
        var tabUrl = tab.url;
633
        var logins = getCredentialsByUrl(tabUrl);
634
        if (tab.active) {
635
            window.contextMenu.setContextItems(logins);
636
        }
637
        var credentialAmount = logins.length;
638
        API.browserAction.setBadgeText({
639
            text: credentialAmount.toString(),
640
            tabId: tab.id
641
        });
642
        API.browserAction.setBadgeBackgroundColor({
643
            color: defaultColor,
644
            tabId: tab.id
645
        });
646
647
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
648
        API.browserAction.setTitle({
649
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
650
            tabId: tab.id
651
        });
652
    }
653
654
    function displayLogoutIcons() {
655
        if (_self.settings) {
656
            API.tabs.query({}).then(function (tabs) {
657
                for (var t = 0; t < tabs.length; t++) {
658
                    var tab = tabs[t];
659
                    API.browserAction.setBadgeText({
660
                        text: '🔑',
661
                        tabId: tab.id
662
                    });
663
                    API.browserAction.setBadgeBackgroundColor({
664
                        color: '#ff0000',
665
                        tabId: tab.id
666
                    });
667
                    API.browserAction.setTitle({
668
                        title: API.i18n.getMessage('browser_action_title_locked'),
669
                        tabId: tab.id
670
                    });
671
                }
672
            });
673
        }
674
    }
675
676
    function updateTabsIcon() {
677
        API.tabs.query({}).then(function (tabs) {
678
            for (var t = 0; t < tabs.length; t++) {
679
                var tab = tabs[t];
680
                createIconForTab(tab);
681
            }
682
        });
683
    }
684
685
686
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
687
        if (master_password) {
688
            createIconForTab(tab);
689
        } else {
690
            displayLogoutIcons();
691
        }
692
    });
693
694
    API.tabs.onActivated.addListener(function () {
695
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
696
            if (master_password) {
697
                createIconForTab(tabs[0]);
698
            } else {
699
                displayLogoutIcons();
700
            }
701
        });
702
    });
703
704
    displayLogoutIcons();
705
706
707
    storage.get('master_password').then(function (password) {
708
        if (password) {
709
            master_password = password;
710
            API.api.browserAction.setBadgeBackgroundColor({
711
                color: defaultColor
712
            });
713
        }
714
        getSettings();
715
    }).error(function (error) {
716
        if (error === "Data not found") {
717
            getSettings();
718
        }
719
    });
720
    return _window;
721
}());
722
723