@@ -6,91 +6,147 @@ const assignManager = {
6
6
MENU_MOVE_ID : "move-to-new-window-container" ,
7
7
OPEN_IN_CONTAINER : "open-bookmark-in-container-tab" ,
8
8
storageArea : {
9
- area : browser . storage . local ,
9
+ store : new utils . NamedStore ( "siteContainerMap" ) ,
10
10
exemptedTabs : { } ,
11
11
12
- getSiteStoreKey ( pageUrl ) {
13
- const url = new window . URL ( pageUrl ) ;
14
- const storagePrefix = "siteContainerMap@@_" ;
15
- if ( url . port === "80" || url . port === "443" ) {
16
- return `${ storagePrefix } ${ url . hostname } ` ;
17
- } else {
18
- return `${ storagePrefix } ${ url . hostname } ${ url . port } ` ;
12
+ setExempted ( siteId , tabId ) {
13
+ if ( ! ( siteId in this . exemptedTabs ) ) {
14
+ this . exemptedTabs [ siteId ] = [ ] ;
19
15
}
16
+ this . exemptedTabs [ siteId ] . push ( tabId ) ;
20
17
} ,
21
18
22
- setExempted ( pageUrl , tabId ) {
23
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
24
- if ( ! ( siteStoreKey in this . exemptedTabs ) ) {
25
- this . exemptedTabs [ siteStoreKey ] = [ ] ;
26
- }
27
- this . exemptedTabs [ siteStoreKey ] . push ( tabId ) ;
19
+ removeExempted ( siteId ) {
20
+ this . exemptedTabs [ siteId ] = [ ] ;
28
21
} ,
29
22
30
- removeExempted ( pageUrl ) {
31
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
32
- this . exemptedTabs [ siteStoreKey ] = [ ] ;
33
- } ,
34
-
35
- isExempted ( pageUrl , tabId ) {
36
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
37
- if ( ! ( siteStoreKey in this . exemptedTabs ) ) {
23
+ isExemptedUrl ( pageUrl , tabId ) {
24
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
25
+ if ( ! ( siteId in this . exemptedTabs ) ) {
38
26
return false ;
39
27
}
40
- return this . exemptedTabs [ siteStoreKey ] . includes ( tabId ) ;
28
+ return this . exemptedTabs [ siteId ] . includes ( tabId ) ;
41
29
} ,
42
-
43
- get ( pageUrl ) {
44
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
45
- return new Promise ( ( resolve , reject ) => {
46
- this . area . get ( [ siteStoreKey ] ) . then ( ( storageResponse ) => {
47
- if ( storageResponse && siteStoreKey in storageResponse ) {
48
- resolve ( storageResponse [ siteStoreKey ] ) ;
49
- }
50
- resolve ( null ) ;
51
- } ) . catch ( ( e ) => {
52
- reject ( e ) ;
30
+
31
+ // Add convenience properties after loading siteSettings from store.
32
+ async _addConvenienceProperties ( siteId , siteSettings ) {
33
+ if ( siteId && siteSettings ) {
34
+ // The travis build incorrectly fails on github when running the below, due to
35
+ // the require-atomic-updates rule. So we have to manually disable the rule.
36
+ const wildcard = await wildcardManager . get ( siteId ) ;
37
+ siteSettings . siteId = siteId ; // eslint-disable-line require-atomic-updates
38
+ siteSettings . hostname = siteId ; // eslint-disable-line require-atomic-updates
39
+ siteSettings . wildcard = wildcard ; // eslint-disable-line require-atomic-updates
40
+ }
41
+ } ,
42
+
43
+ // Add convenience properties after loading multiple siteSettings from store.
44
+ async _addConveniencePropertiesToAll ( siteSettingsById ) {
45
+ const siteIds = Object . keys ( siteSettingsById ) ;
46
+ siteIds . forEach ( ( siteId ) => {
47
+ const siteSettings = siteSettingsById [ siteId ] ;
48
+ siteSettings . siteId = siteId ;
49
+ siteSettings . hostname = siteId ;
50
+ } ) ;
51
+
52
+ if ( siteIds . length > 0 ) {
53
+ const siteIdsToWildcards = await wildcardManager . getAll ( siteIds ) ;
54
+ Object . entries ( siteIdsToWildcards ) . forEach ( ( [ siteId , wildcard ] ) => {
55
+ const siteSettings = siteSettingsById [ siteId ] ;
56
+ siteSettings . wildcard = wildcard ;
53
57
} ) ;
58
+ }
59
+ } ,
60
+
61
+ // Remove convenience properties before saving siteSettings to store.
62
+ _excludingConvenienceProperties ( siteId , siteSettings ) {
63
+ return utils . filterObj ( siteSettings , ( propertyName ) => {
64
+ if ( propertyName === "siteId" ) { return false ; }
65
+ if ( propertyName === "hostname" ) { return false ; }
66
+ if ( propertyName === "wildcard" ) { return false ; }
67
+ return true ;
54
68
} ) ;
55
69
} ,
56
70
57
- set ( pageUrl , data , exemptedTabIds ) {
58
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
71
+ async matchUrl ( pageUrl ) {
72
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
73
+
74
+ // Try exact match
75
+ let siteSettings = await this . get ( siteId ) ;
76
+
77
+ if ( ! siteSettings ) {
78
+ // Try wildcard match
79
+ const wildcard = await wildcardManager . match ( siteId ) ;
80
+ if ( wildcard ) {
81
+ siteSettings = await this . get ( wildcard ) ;
82
+ }
83
+ }
84
+
85
+ return siteSettings ;
86
+ } ,
87
+
88
+ async get ( siteId ) {
89
+ const siteSettings = await this . store . get ( siteId ) ;
90
+ // Convenience properties are not stored - add them
91
+ await this . _addConvenienceProperties ( siteId , siteSettings ) ;
92
+ return siteSettings ;
93
+ } ,
94
+
95
+ async set ( siteId , siteSettings , exemptedTabIds ) {
96
+ // Store exempted tabs
59
97
if ( exemptedTabIds ) {
60
98
exemptedTabIds . forEach ( ( tabId ) => {
61
- this . setExempted ( pageUrl , tabId ) ;
99
+ this . setExempted ( siteId , tabId ) ;
62
100
} ) ;
63
101
}
64
- return this . area . set ( {
65
- [ siteStoreKey ] : data
66
- } ) ;
102
+ // Store/remove wildcard mapping
103
+ if ( siteSettings . wildcard && siteSettings . wildcard !== siteId ) {
104
+ await wildcardManager . set ( siteId , siteSettings . wildcard ) ;
105
+ } else {
106
+ await wildcardManager . remove ( siteId ) ;
107
+ }
108
+
109
+ // Convenience properties are not stored - remove them
110
+ siteSettings = this . _excludingConvenienceProperties ( siteId , siteSettings ) ;
111
+
112
+ // Store assignment
113
+ return this . store . set ( siteId , siteSettings ) ;
67
114
} ,
68
115
69
- remove ( pageUrl ) {
70
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
116
+ async remove ( siteId ) {
71
117
// When we remove an assignment we should clear all the exemptions
72
- this . removeExempted ( pageUrl ) ;
73
- return this . area . remove ( [ siteStoreKey ] ) ;
118
+ this . removeExempted ( siteId ) ;
119
+ // ...and also clear the wildcard mapping
120
+ await wildcardManager . remove ( siteId ) ;
121
+
122
+ return this . store . remove ( siteId ) ;
74
123
} ,
75
124
76
125
async deleteContainer ( userContextId ) {
77
126
const sitesByContainer = await this . getByContainer ( userContextId ) ;
78
- this . area . remove ( Object . keys ( sitesByContainer ) ) ;
127
+ const siteIds = Object . keys ( sitesByContainer ) ;
128
+
129
+ siteIds . forEach ( ( siteId ) => {
130
+ // When we remove an assignment we should clear all the exemptions
131
+ this . removeExempted ( siteId ) ;
132
+ } ) ;
133
+
134
+ // ...and also clear the wildcard mappings
135
+ await wildcardManager . removeAll ( siteIds ) ;
136
+
137
+ return this . store . removeAll ( siteIds ) ;
79
138
} ,
80
139
81
140
async getByContainer ( userContextId ) {
82
- const sites = { } ;
83
- const siteConfigs = await this . area . get ( ) ;
84
- Object . keys ( siteConfigs ) . forEach ( ( key ) => {
141
+ const allSites = await this . store . getAll ( ) ;
142
+ const sites = utils . filterObj ( allSites , ( siteId , site ) => {
85
143
// For some reason this is stored as string... lets check them both as that
86
- if ( String ( siteConfigs [ key ] . userContextId ) === String ( userContextId ) ) {
87
- const site = siteConfigs [ key ] ;
88
- // In hindsight we should have stored this
89
- // TODO file a follow up to clean the storage onLoad
90
- site . hostname = key . replace ( / ^ s i t e C o n t a i n e r M a p @ @ _ / , "" ) ;
91
- sites [ key ] = site ;
92
- }
144
+ return String ( site . userContextId ) === String ( userContextId ) ;
93
145
} ) ;
146
+
147
+ // Convenience properties are not stored - add them
148
+ this . _addConveniencePropertiesToAll ( sites ) ;
149
+
94
150
return sites ;
95
151
}
96
152
} ,
@@ -99,10 +155,10 @@ const assignManager = {
99
155
const pageUrl = m . pageUrl ;
100
156
if ( m . neverAsk === true ) {
101
157
// If we have existing data and for some reason it hasn't been deleted etc lets update it
102
- this . storageArea . get ( pageUrl ) . then ( ( siteSettings ) => {
158
+ this . storageArea . matchUrl ( pageUrl ) . then ( ( siteSettings ) => {
103
159
if ( siteSettings ) {
104
160
siteSettings . neverAsk = true ;
105
- this . storageArea . set ( pageUrl , siteSettings ) ;
161
+ return this . storageArea . set ( siteSettings . siteId , siteSettings ) ;
106
162
}
107
163
} ) . catch ( ( e ) => {
108
164
throw e ;
@@ -113,7 +169,8 @@ const assignManager = {
113
169
// We return here so the confirm page can load the tab when exempted
114
170
async _exemptTab ( m ) {
115
171
const pageUrl = m . pageUrl ;
116
- this . storageArea . setExempted ( pageUrl , m . tabId ) ;
172
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
173
+ this . storageArea . setExempted ( siteId , m . tabId ) ;
117
174
return true ;
118
175
} ,
119
176
@@ -125,7 +182,7 @@ const assignManager = {
125
182
this . removeContextMenu ( ) ;
126
183
const [ tab , siteSettings ] = await Promise . all ( [
127
184
browser . tabs . get ( options . tabId ) ,
128
- this . storageArea . get ( options . url )
185
+ this . storageArea . matchUrl ( options . url )
129
186
] ) ;
130
187
let container ;
131
188
try {
@@ -143,7 +200,7 @@ const assignManager = {
143
200
const userContextId = this . getUserContextIdFromCookieStore ( tab ) ;
144
201
if ( ! siteSettings
145
202
|| userContextId === siteSettings . userContextId
146
- || this . storageArea . isExempted ( options . url , tab . id ) ) {
203
+ || this . storageArea . isExemptedUrl ( options . url , tab . id ) ) {
147
204
return { } ;
148
205
}
149
206
const removeTab = backgroundLogic . NEW_TAB_PAGES . has ( tab . url )
@@ -367,51 +424,74 @@ const assignManager = {
367
424
return true ;
368
425
} ,
369
426
370
- async _setOrRemoveAssignment ( tabId , pageUrl , userContextId , remove ) {
427
+ _determineAssignmentMatchesUrl ( siteSettings , url ) {
428
+ const siteId = backgroundLogic . getSiteIdFromUrl ( url ) ;
429
+ if ( siteSettings . siteId === siteId ) { return true ; }
430
+ if ( siteSettings . wildcard && siteId . endsWith ( siteSettings . wildcard ) ) { return true ; }
431
+ return false ;
432
+ } ,
433
+
434
+ async _setOrRemoveAssignment ( tabId , pageUrl , userContextId , remove , options = { } ) {
371
435
let actionName ;
372
436
373
437
// https://github.com/mozilla/testpilot-containers/issues/626
374
438
// Context menu has stored context IDs as strings, so we need to coerce
375
439
// the value to a string for accurate checking
376
440
userContextId = String ( userContextId ) ;
377
441
442
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
378
443
if ( ! remove ) {
444
+ const siteSettings = {
445
+ siteId,
446
+ hostname : siteId ,
447
+ wildcard : options . wildcard ,
448
+ userContextId,
449
+ neverAsk : ! ! options . neverAsk
450
+ } ;
451
+
379
452
const tabs = await browser . tabs . query ( { } ) ;
380
- const assignmentStoreKey = this . storageArea . getSiteStoreKey ( pageUrl ) ;
381
453
const exemptedTabIds = tabs . filter ( ( tab ) => {
382
- const tabStoreKey = this . storageArea . getSiteStoreKey ( tab . url ) ;
383
454
/* Auto exempt all tabs that exist for this hostname that are not in the same container */
384
- if ( tabStoreKey === assignmentStoreKey &&
455
+ if ( this . _determineAssignmentMatchesUrl ( siteSettings , tab . url ) &&
385
456
this . getUserContextIdFromCookieStore ( tab ) !== userContextId ) {
386
457
return true ;
387
458
}
388
459
return false ;
389
460
} ) . map ( ( tab ) => {
390
461
return tab . id ;
391
462
} ) ;
392
-
393
- await this . storageArea . set ( pageUrl , {
394
- userContextId,
395
- neverAsk : false
396
- } , exemptedTabIds ) ;
463
+
464
+ await this . storageArea . set ( siteId , siteSettings , exemptedTabIds ) ;
397
465
actionName = "added" ;
398
466
} else {
399
- await this . storageArea . remove ( pageUrl ) ;
467
+ await this . storageArea . remove ( siteId ) ;
400
468
actionName = "removed" ;
401
469
}
402
- browser . tabs . sendMessage ( tabId , {
403
- text : `Successfully ${ actionName } site to always open in this container`
404
- } ) ;
470
+ if ( ! options . silent ) {
471
+ browser . tabs . sendMessage ( tabId , {
472
+ text : `Successfully ${ actionName } site to always open in this container`
473
+ } ) ;
474
+ }
405
475
const tab = await browser . tabs . get ( tabId ) ;
406
476
this . calculateContextMenu ( tab ) ;
407
477
} ,
478
+
479
+ async _setOrRemoveWildcard ( tabId , pageUrl , userContextId , wildcard ) {
480
+ // Get existing settings, so we can preserve neverAsk property
481
+ const oldSiteSettings = await this . storageArea . get ( backgroundLogic . getSiteIdFromUrl ( pageUrl ) ) || { } ;
482
+
483
+ // Remove assignment
484
+ await this . _setOrRemoveAssignment ( tabId , pageUrl , userContextId , true , { silent :true } ) ;
485
+ // Add assignment
486
+ await this . _setOrRemoveAssignment ( tabId , pageUrl , userContextId , false , { silent :true , wildcard :wildcard , neverAsk :oldSiteSettings . neverAsk } ) ;
487
+ } ,
408
488
409
489
async _getAssignment ( tab ) {
410
490
const cookieStore = this . getUserContextIdFromCookieStore ( tab ) ;
411
491
// Ensure we have a cookieStore to assign to
412
492
if ( cookieStore
413
493
&& this . isTabPermittedAssign ( tab ) ) {
414
- return await this . storageArea . get ( tab . url ) ;
494
+ return await this . storageArea . matchUrl ( tab . url ) ;
415
495
}
416
496
return false ;
417
497
} ,
0 commit comments