2
2
"use strict" ;
3
3
4
4
require ( "chrome-extension-async" ) ;
5
+ var TldJS = require ( "tldjs" ) ;
5
6
var sha1 = require ( "sha1" ) ;
6
7
7
8
// native application id
@@ -17,8 +18,12 @@ var defaultSettings = {
17
18
18
19
var authListeners = { } ;
19
20
21
+ chrome . browserAction . setBadgeBackgroundColor ( {
22
+ color : "#666"
23
+ } ) ;
24
+
20
25
// watch for tab updates
21
- chrome . tabs . onUpdated . addListener ( function ( tabId , info ) {
26
+ chrome . tabs . onUpdated . addListener ( ( tabId , info ) => {
22
27
// ignore non-complete status
23
28
if ( info . status !== "complete" ) {
24
29
return ;
@@ -29,6 +34,9 @@ chrome.tabs.onUpdated.addListener(function(tabId, info) {
29
34
chrome . webRequest . onAuthRequired . removeListener ( authListeners [ tabId ] ) ;
30
35
delete authListeners [ tabId ] ;
31
36
}
37
+
38
+ // show number of matching passwords in a badge
39
+ updateMatchingPasswordsCount ( tabId ) ;
32
40
} ) ;
33
41
34
42
// handle incoming messages
@@ -43,6 +51,79 @@ chrome.runtime.onInstalled.addListener(onExtensionInstalled);
43
51
44
52
//----------------------------------- Function definitions ----------------------------------//
45
53
54
+ /**
55
+ * Get the deepest available domain component of a path
56
+ *
57
+ * @since 3.0.0
58
+ *
59
+ * @param string path Path to parse
60
+ * @return string|null Extracted domain
61
+ */
62
+ function pathToDomain ( path ) {
63
+ var parts = path . split ( / \/ / ) . reverse ( ) ;
64
+ for ( var key in parts ) {
65
+ if ( parts [ key ] . indexOf ( "@" ) >= 0 ) {
66
+ continue ;
67
+ }
68
+ var t = TldJS . parse ( parts [ key ] ) ;
69
+ if ( t . isValid && t . domain !== null ) {
70
+ return t . hostname ;
71
+ }
72
+ }
73
+
74
+ return null ;
75
+ }
76
+
77
+ /**
78
+ * Set badge text with the number of matching password entries
79
+ *
80
+ * @since 3.0.0
81
+ *
82
+ * @param int tabId Tab id
83
+ */
84
+ async function updateMatchingPasswordsCount ( tabId ) {
85
+ try {
86
+ const settings = await getFullSettings ( ) ;
87
+ var response = await hostAction ( settings , "list" ) ;
88
+ if ( response . status != "ok" ) {
89
+ throw new Error ( JSON . stringify ( response ) ) ;
90
+ }
91
+
92
+ // Get tab info
93
+ let currentDomain = undefined ;
94
+ try {
95
+ const tab = await chrome . tabs . get ( tabId ) ;
96
+ currentDomain = new URL ( tab . url ) . hostname ;
97
+ } catch ( e ) {
98
+ throw new Error ( `Unable to determine domain of the tab with id ${ tabId } ` ) ;
99
+ }
100
+
101
+ let matchedPasswordsCount = 0 ;
102
+ for ( var storeId in response . data . files ) {
103
+ for ( var key in response . data . files [ storeId ] ) {
104
+ const login = response . data . files [ storeId ] [ key ] . replace ( / \. g p g $ / i, "" ) ;
105
+ const domain = pathToDomain ( storeId + "/" + login ) ;
106
+ const inCurrentDomain =
107
+ currentDomain === domain || currentDomain . endsWith ( "." + domain ) ;
108
+ const recent = settings . recent [ sha1 ( currentDomain + sha1 ( storeId + sha1 ( login ) ) ) ] ;
109
+ if ( recent || inCurrentDomain ) {
110
+ matchedPasswordsCount ++ ;
111
+ }
112
+ }
113
+ }
114
+
115
+ if ( matchedPasswordsCount ) {
116
+ // Set badge for the current tab
117
+ chrome . browserAction . setBadgeText ( {
118
+ text : "" + matchedPasswordsCount ,
119
+ tabId : tabId
120
+ } ) ;
121
+ }
122
+ } catch ( e ) {
123
+ console . log ( e ) ;
124
+ }
125
+ }
126
+
46
127
/**
47
128
* Copy text to clipboard
48
129
*
@@ -89,6 +170,11 @@ function saveRecent(settings, login, remove = false) {
89
170
90
171
// save to local storage
91
172
localStorage . setItem ( "recent" , JSON . stringify ( settings . recent ) ) ;
173
+
174
+ // a new entry was added to the popup matching list, need to refresh the count
175
+ if ( ! login . inCurrentDomain && login . recent . count === 1 ) {
176
+ updateMatchingPasswordsCount ( settings . tab . id ) ;
177
+ }
92
178
}
93
179
94
180
/**
@@ -226,6 +312,83 @@ function getLocalSettings() {
226
312
return settings ;
227
313
}
228
314
315
+ /**
316
+ * Get full settings from the extension and host application
317
+ *
318
+ * @since 3.0.0
319
+ *
320
+ * @return object Full settings object
321
+ */
322
+ async function getFullSettings ( ) {
323
+ var settings = getLocalSettings ( ) ;
324
+ var configureSettings = Object . assign ( deepCopy ( settings ) , {
325
+ defaultStore : { }
326
+ } ) ;
327
+ var response = await hostAction ( configureSettings , "configure" ) ;
328
+ if ( response . status != "ok" ) {
329
+ throw new Error ( JSON . stringify ( response ) ) ; // TODO handle host error
330
+ }
331
+ settings . version = response . version ;
332
+ if ( Object . keys ( settings . stores ) . length > 0 ) {
333
+ // there are user-configured stores present
334
+ for ( var storeId in settings . stores ) {
335
+ if ( response . data . storeSettings . hasOwnProperty ( storeId ) ) {
336
+ var fileSettings = JSON . parse ( response . data . storeSettings [ storeId ] ) ;
337
+ if ( typeof settings . stores [ storeId ] . settings !== "object" ) {
338
+ settings . stores [ storeId ] . settings = { } ;
339
+ }
340
+ var storeSettings = settings . stores [ storeId ] . settings ;
341
+ for ( var settingKey in fileSettings ) {
342
+ if ( ! storeSettings . hasOwnProperty ( settingKey ) ) {
343
+ storeSettings [ settingKey ] = fileSettings [ settingKey ] ;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ } else {
349
+ // no user-configured stores, so use the default store
350
+ settings . stores . default = {
351
+ id : "default" ,
352
+ name : "pass" ,
353
+ path : response . data . defaultStore . path
354
+ } ;
355
+ var fileSettings = JSON . parse ( response . data . defaultStore . settings ) ;
356
+ if ( typeof settings . stores . default . settings !== "object" ) {
357
+ settings . stores . default . settings = { } ;
358
+ }
359
+ var storeSettings = settings . stores . default . settings ;
360
+ for ( var settingKey in fileSettings ) {
361
+ if ( ! storeSettings . hasOwnProperty ( settingKey ) ) {
362
+ storeSettings [ settingKey ] = fileSettings [ settingKey ] ;
363
+ }
364
+ }
365
+ }
366
+
367
+ // Fill recent data
368
+ for ( var storeId in settings . stores ) {
369
+ var when = localStorage . getItem ( "recent:" + storeId ) ;
370
+ if ( when ) {
371
+ settings . stores [ storeId ] . when = JSON . parse ( when ) ;
372
+ } else {
373
+ settings . stores [ storeId ] . when = 0 ;
374
+ }
375
+ }
376
+ settings . recent = localStorage . getItem ( "recent" ) ;
377
+ if ( settings . recent ) {
378
+ settings . recent = JSON . parse ( settings . recent ) ;
379
+ } else {
380
+ settings . recent = { } ;
381
+ }
382
+
383
+ // Fill current tab info
384
+ try {
385
+ settings . tab = ( await chrome . tabs . query ( { active : true , currentWindow : true } ) ) [ 0 ] ;
386
+ settings . host = new URL ( settings . tab . url ) . hostname ;
387
+ } catch ( e ) { }
388
+
389
+ return settings ;
390
+ }
391
+
229
392
/**
230
393
* Get most relevant setting value
231
394
*
@@ -602,73 +765,8 @@ async function receiveMessage(message, sender, sendResponse) {
602
765
return ;
603
766
}
604
767
605
- var settings = getLocalSettings ( ) ;
606
768
try {
607
- var configureSettings = Object . assign ( deepCopy ( settings ) , {
608
- defaultStore : { }
609
- } ) ;
610
- var response = await hostAction ( configureSettings , "configure" ) ;
611
- if ( response . status != "ok" ) {
612
- throw new Error ( JSON . stringify ( response ) ) ; // TODO handle host error
613
- }
614
- settings . version = response . version ;
615
- if ( Object . keys ( settings . stores ) . length > 0 ) {
616
- // there are user-configured stores present
617
- for ( var storeId in settings . stores ) {
618
- if ( response . data . storeSettings . hasOwnProperty ( storeId ) ) {
619
- var fileSettings = JSON . parse ( response . data . storeSettings [ storeId ] ) ;
620
- if ( typeof settings . stores [ storeId ] . settings !== "object" ) {
621
- settings . stores [ storeId ] . settings = { } ;
622
- }
623
- var storeSettings = settings . stores [ storeId ] . settings ;
624
- for ( var settingKey in fileSettings ) {
625
- if ( ! storeSettings . hasOwnProperty ( settingKey ) ) {
626
- storeSettings [ settingKey ] = fileSettings [ settingKey ] ;
627
- }
628
- }
629
- }
630
- }
631
- } else {
632
- // no user-configured stores, so use the default store
633
- settings . stores . default = {
634
- id : "default" ,
635
- name : "pass" ,
636
- path : response . data . defaultStore . path
637
- } ;
638
- var fileSettings = JSON . parse ( response . data . defaultStore . settings ) ;
639
- if ( typeof settings . stores . default . settings !== "object" ) {
640
- settings . stores . default . settings = { } ;
641
- }
642
- var storeSettings = settings . stores . default . settings ;
643
- for ( var settingKey in fileSettings ) {
644
- if ( ! storeSettings . hasOwnProperty ( settingKey ) ) {
645
- storeSettings [ settingKey ] = fileSettings [ settingKey ] ;
646
- }
647
- }
648
- }
649
-
650
- // Fill recent data
651
- for ( var storeId in settings . stores ) {
652
- var when = localStorage . getItem ( "recent:" + storeId ) ;
653
- if ( when ) {
654
- settings . stores [ storeId ] . when = JSON . parse ( when ) ;
655
- } else {
656
- settings . stores [ storeId ] . when = 0 ;
657
- }
658
- }
659
- settings . recent = localStorage . getItem ( "recent" ) ;
660
- if ( settings . recent ) {
661
- settings . recent = JSON . parse ( settings . recent ) ;
662
- } else {
663
- settings . recent = { } ;
664
- }
665
-
666
- // Fill current tab info
667
- try {
668
- settings . tab = ( await chrome . tabs . query ( { active : true , currentWindow : true } ) ) [ 0 ] ;
669
- settings . host = new URL ( settings . tab . url ) . hostname ;
670
- } catch ( e ) { }
671
-
769
+ const settings = await getFullSettings ( ) ;
672
770
handleMessage ( settings , message , sendResponse ) ;
673
771
} catch ( e ) {
674
772
// handle error
0 commit comments