@@ -6,115 +6,155 @@ 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 ,
10
- exemptedTabs : { } ,
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 } ` ;
19
- }
9
+ store : new utils . NamedStore ( "siteContainerMap" ) ,
10
+ exemptedTabsStore : {
11
+ data : { } ,
12
+ get ( siteId ) { return this . data [ siteId ] ; } ,
13
+ set ( siteId , tabIds ) { this . data [ siteId ] = tabIds ; } ,
14
+ remove ( siteId ) { delete this . data [ siteId ] ; }
20
15
} ,
21
16
22
- setExempted ( pageUrl , tabId ) {
23
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
24
- if ( ! ( siteStoreKey in this . exemptedTabs ) ) {
25
- this . exemptedTabs [ siteStoreKey ] = [ ] ;
17
+ async matchUrl ( pageUrl ) {
18
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
19
+
20
+ // Try exact match
21
+ let siteSettings = await this . get ( siteId ) ;
22
+
23
+ if ( ! siteSettings ) {
24
+ // Try wildcard match
25
+ const wildcard = await wildcardManager . match ( siteId ) ;
26
+ if ( wildcard ) {
27
+ siteSettings = await this . get ( wildcard ) ;
28
+ }
26
29
}
27
- this . exemptedTabs [ siteStoreKey ] . push ( tabId ) ;
28
- } ,
29
-
30
- removeExempted ( pageUrl ) {
31
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
32
- this . exemptedTabs [ siteStoreKey ] = [ ] ;
30
+
31
+ return siteSettings ;
33
32
} ,
34
-
35
- isExempted ( pageUrl , tabId ) {
36
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
37
- if ( ! ( siteStoreKey in this . exemptedTabs ) ) {
38
- return false ;
39
- }
40
- return this . exemptedTabs [ siteStoreKey ] . includes ( tabId ) ;
33
+
34
+ create ( siteId , userContextId , options = { } ) {
35
+ const siteSettings = { userContextId, neverAsk :! ! options . neverAsk } ;
36
+ this . _setTransientProperties ( siteId , siteSettings , options . wildcard ) ;
37
+ return siteSettings ;
41
38
} ,
42
39
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 ) ;
53
- } ) ;
54
- } ) ;
40
+ async get ( siteId ) {
41
+ const siteSettings = await this . store . get ( siteId ) ;
42
+ await this . _loadTransientProperties ( siteId , siteSettings ) ;
43
+ return siteSettings ;
55
44
} ,
56
45
57
- set ( pageUrl , data , exemptedTabIds ) {
58
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
59
- if ( exemptedTabIds ) {
60
- exemptedTabIds . forEach ( ( tabId ) => {
61
- this . setExempted ( pageUrl , tabId ) ;
62
- } ) ;
46
+ async set ( siteSettings ) {
47
+ const siteId = siteSettings . siteId ;
48
+ const exemptedTabs = siteSettings . exemptedTabs ;
49
+ const wildcard = siteSettings . wildcard ;
50
+
51
+ // Store/remove exempted tabs
52
+ this . exemptedTabsStore . set ( siteId , exemptedTabs ) ;
53
+
54
+ // Store/remove wildcard mapping
55
+ if ( wildcard && wildcard !== siteId ) {
56
+ await wildcardManager . set ( siteId , wildcard ) ;
57
+ } else {
58
+ await wildcardManager . remove ( siteId ) ;
63
59
}
64
- return this . area . set ( {
65
- [ siteStoreKey ] : data
66
- } ) ;
60
+
61
+ // Remove transient properties
62
+ const cleanSiteSettings = Object . assign ( { } , siteSettings ) ;
63
+ this . _unsetTransientProperties ( cleanSiteSettings ) ;
64
+
65
+ // Store assignment
66
+ return this . store . set ( siteId , cleanSiteSettings ) ;
67
67
} ,
68
68
69
- remove ( pageUrl ) {
70
- const siteStoreKey = this . getSiteStoreKey ( pageUrl ) ;
69
+ async remove ( siteId ) {
71
70
// When we remove an assignment we should clear all the exemptions
72
- this . removeExempted ( pageUrl ) ;
73
- return this . area . remove ( [ siteStoreKey ] ) ;
71
+ this . exemptedTabsStore . remove ( siteId ) ;
72
+ // ...and also clear the wildcard mapping
73
+ await wildcardManager . remove ( siteId ) ;
74
+
75
+ return this . store . remove ( siteId ) ;
74
76
} ,
75
77
76
78
async deleteContainer ( userContextId ) {
77
- const sitesByContainer = await this . getByContainer ( userContextId ) ;
78
- this . area . remove ( Object . keys ( sitesByContainer ) ) ;
79
+ const siteSettingsById = await this . getByContainer ( userContextId ) ;
80
+ const siteIds = Object . keys ( siteSettingsById ) ;
81
+
82
+ siteIds . forEach ( ( siteId ) => {
83
+ // When we remove an assignment we should clear all the exemptions
84
+ this . exemptedTabsStore . remove ( siteId ) ;
85
+ } ) ;
86
+
87
+ // ...and also clear the wildcard mappings
88
+ await wildcardManager . removeAll ( siteIds ) ;
89
+
90
+ return this . store . removeAll ( siteIds ) ;
79
91
} ,
80
92
81
93
async getByContainer ( userContextId ) {
82
- const sites = { } ;
83
- const siteConfigs = await this . area . get ( ) ;
84
- Object . keys ( siteConfigs ) . forEach ( ( key ) => {
94
+ const siteSettingsById = await this . store . getSome ( ( siteId , siteSettings ) => {
85
95
// 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
- }
96
+ return String ( siteSettings . userContextId ) === String ( userContextId ) ;
93
97
} ) ;
94
- return sites ;
98
+ await this . _loadTransientPropertiesForAll ( siteSettingsById ) ;
99
+ return siteSettingsById ;
100
+ } ,
101
+
102
+ async _loadTransientProperties ( siteId , siteSettings ) {
103
+ if ( siteId && siteSettings ) {
104
+ const wildcard = await wildcardManager . get ( siteId ) ;
105
+ const exemptedTabs = this . exemptedTabsStore . get ( siteId ) ;
106
+ this . _setTransientProperties ( siteId , siteSettings , wildcard , exemptedTabs ) ;
107
+ }
108
+ } ,
109
+
110
+ async _loadTransientPropertiesForAll ( siteSettingsById ) {
111
+ const siteIds = Object . keys ( siteSettingsById ) ;
112
+ if ( siteIds . length > 0 ) {
113
+ const siteIdsToWildcards = await wildcardManager . getAll ( siteIds ) ;
114
+ siteIds . forEach ( ( siteId ) => {
115
+ const siteSettings = siteSettingsById [ siteId ] ;
116
+ const wildcard = siteIdsToWildcards [ siteId ] ;
117
+ const exemptedTabs = this . exemptedTabsStore . get ( siteId ) ;
118
+ this . _setTransientProperties ( siteId , siteSettings , wildcard , exemptedTabs ) ;
119
+ } ) ;
120
+ }
121
+ } ,
122
+
123
+ _setTransientProperties ( siteId , siteSettings , wildcard , exemptedTabs = [ ] ) {
124
+ siteSettings . siteId = siteId ;
125
+ siteSettings . hostname = siteId ;
126
+ siteSettings . wildcard = wildcard ;
127
+ siteSettings . exemptedTabs = exemptedTabs ;
128
+ } ,
129
+
130
+ _unsetTransientProperties ( siteSettings ) {
131
+ delete siteSettings . siteId ;
132
+ delete siteSettings . hostname ;
133
+ delete siteSettings . wildcard ;
134
+ delete siteSettings . exemptedTabs ;
95
135
}
96
136
} ,
97
137
98
- _neverAsk ( m ) {
138
+ async _neverAsk ( m ) {
99
139
const pageUrl = m . pageUrl ;
100
- if ( m . neverAsk === true ) {
101
- // 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 ) => {
103
- if ( siteSettings ) {
104
- siteSettings . neverAsk = true ;
105
- this . storageArea . set ( pageUrl , siteSettings ) ;
106
- }
107
- } ) . catch ( ( e ) => {
108
- throw e ;
109
- } ) ;
140
+ const neverAsk = m . neverAsk ;
141
+ if ( neverAsk === true ) {
142
+ const siteSettings = await this . storageArea . matchUrl ( pageUrl ) ;
143
+ if ( siteSettings && ! siteSettings . neverAsk ) {
144
+ siteSettings . neverAsk = true ;
145
+ await this . storageArea . set ( siteSettings ) ;
146
+ }
110
147
}
111
148
} ,
112
149
113
- // We return here so the confirm page can load the tab when exempted
114
150
async _exemptTab ( m ) {
115
151
const pageUrl = m . pageUrl ;
116
- this . storageArea . setExempted ( pageUrl , m . tabId ) ;
117
- return true ;
152
+ const tabId = m . tabId ;
153
+ const siteSettings = await this . storageArea . matchUrl ( pageUrl ) ;
154
+ if ( siteSettings && siteSettings . exemptedTabs . indexOf ( tabId ) === - 1 ) {
155
+ siteSettings . exemptedTabs . push ( tabId ) ;
156
+ await this . storageArea . set ( siteSettings ) ;
157
+ }
118
158
} ,
119
159
120
160
// Before a request is handled by the browser we decide if we should route through a different container
@@ -125,7 +165,7 @@ const assignManager = {
125
165
this . removeContextMenu ( ) ;
126
166
const [ tab , siteSettings ] = await Promise . all ( [
127
167
browser . tabs . get ( options . tabId ) ,
128
- this . storageArea . get ( options . url )
168
+ this . storageArea . matchUrl ( options . url )
129
169
] ) ;
130
170
let container ;
131
171
try {
@@ -142,8 +182,8 @@ const assignManager = {
142
182
}
143
183
const userContextId = this . getUserContextIdFromCookieStore ( tab ) ;
144
184
if ( ! siteSettings
145
- || userContextId === siteSettings . userContextId
146
- || this . storageArea . isExempted ( options . url , tab . id ) ) {
185
+ || siteSettings . userContextId === userContextId
186
+ || siteSettings . exemptedTabs . includes ( tab . id ) ) {
147
187
return { } ;
148
188
}
149
189
const removeTab = backgroundLogic . NEW_TAB_PAGES . has ( tab . url )
@@ -367,51 +407,68 @@ const assignManager = {
367
407
return true ;
368
408
} ,
369
409
370
- async _setOrRemoveAssignment ( tabId , pageUrl , userContextId , remove ) {
410
+ _determineAssignmentMatchesUrl ( siteSettings , url ) {
411
+ const siteId = backgroundLogic . getSiteIdFromUrl ( url ) ;
412
+ if ( siteSettings . siteId === siteId ) { return true ; }
413
+ if ( siteSettings . wildcard && siteId . endsWith ( siteSettings . wildcard ) ) { return true ; }
414
+ return false ;
415
+ } ,
416
+
417
+ async _setOrRemoveAssignment ( tabId , pageUrl , userContextId , remove , options = { } ) {
371
418
let actionName ;
372
419
373
420
// https://github.com/mozilla/testpilot-containers/issues/626
374
421
// Context menu has stored context IDs as strings, so we need to coerce
375
422
// the value to a string for accurate checking
376
423
userContextId = String ( userContextId ) ;
377
424
425
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
378
426
if ( ! remove ) {
427
+ const siteSettings = this . storageArea . create ( siteId , userContextId , options ) ;
428
+
429
+ // Auto exempt all tabs that exist for this hostname that are not in the same container
379
430
const tabs = await browser . tabs . query ( { } ) ;
380
- const assignmentStoreKey = this . storageArea . getSiteStoreKey ( pageUrl ) ;
381
- const exemptedTabIds = tabs . filter ( ( tab ) => {
382
- const tabStoreKey = this . storageArea . getSiteStoreKey ( tab . url ) ;
383
- /* Auto exempt all tabs that exist for this hostname that are not in the same container */
384
- if ( tabStoreKey === assignmentStoreKey &&
385
- this . getUserContextIdFromCookieStore ( tab ) !== userContextId ) {
386
- return true ;
387
- }
388
- return false ;
431
+ siteSettings . exemptedTabs = tabs . filter ( ( tab ) => {
432
+ if ( ! this . _determineAssignmentMatchesUrl ( siteSettings , tab . url ) ) { return false ; }
433
+ if ( this . getUserContextIdFromCookieStore ( tab ) === userContextId ) { return false ; }
434
+ return true ;
389
435
} ) . map ( ( tab ) => {
390
436
return tab . id ;
391
437
} ) ;
392
-
393
- await this . storageArea . set ( pageUrl , {
394
- userContextId,
395
- neverAsk : false
396
- } , exemptedTabIds ) ;
438
+
439
+ await this . storageArea . set ( siteSettings ) ;
397
440
actionName = "added" ;
398
441
} else {
399
- await this . storageArea . remove ( pageUrl ) ;
442
+ await this . storageArea . remove ( siteId ) ;
400
443
actionName = "removed" ;
401
444
}
402
- browser . tabs . sendMessage ( tabId , {
403
- text : `Successfully ${ actionName } site to always open in this container`
404
- } ) ;
445
+ if ( ! options . silent ) {
446
+ browser . tabs . sendMessage ( tabId , {
447
+ text : `Successfully ${ actionName } site to always open in this container`
448
+ } ) ;
449
+ }
405
450
const tab = await browser . tabs . get ( tabId ) ;
406
451
this . calculateContextMenu ( tab ) ;
407
452
} ,
453
+
454
+ async _setOrRemoveWildcard ( tabId , pageUrl , userContextId , wildcard ) {
455
+ // Get existing settings, so we can preserve neverAsk property
456
+ const siteId = backgroundLogic . getSiteIdFromUrl ( pageUrl ) ;
457
+ const siteSettings = await this . storageArea . get ( siteId ) ;
458
+ const neverAsk = siteSettings && siteSettings . neverAsk ;
459
+
460
+ // Remove assignment
461
+ await this . _setOrRemoveAssignment ( tabId , pageUrl , userContextId , true , { silent :true } ) ;
462
+ // Add assignment
463
+ await this . _setOrRemoveAssignment ( tabId , pageUrl , userContextId , false , { silent :true , wildcard :wildcard , neverAsk :neverAsk } ) ;
464
+ } ,
408
465
409
466
async _getAssignment ( tab ) {
410
467
const cookieStore = this . getUserContextIdFromCookieStore ( tab ) ;
411
468
// Ensure we have a cookieStore to assign to
412
469
if ( cookieStore
413
470
&& this . isTabPermittedAssign ( tab ) ) {
414
- return await this . storageArea . get ( tab . url ) ;
471
+ return await this . storageArea . matchUrl ( tab . url ) ;
415
472
}
416
473
return false ;
417
474
} ,
0 commit comments