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