@@ -22,10 +22,24 @@ const parentOptions = {
2222export async function init ( ) {
2323 browser . menus . create ( parentOptions ) ;
2424
25- browser . menus . onShown . addListener ( ( info , tab ) => {
25+ browser . menus . onShown . addListener ( async ( info , tab ) => {
2626 if ( info . contexts . includes ( "tab" ) ) {
2727 shown = true ;
28- createMenuForTab ( tab ) ;
28+
29+ // get selected/highlighted tabs
30+ let selection = await browser . tabs . query ( {
31+ currentWindow : true ,
32+ highlighted : true
33+ } ) ;
34+
35+ console . assert ( selection . length >= 1 ) ;
36+
37+ if ( ! selection . find ( t => t . id === tab . id ) ) {
38+ console . error ( `[TA] Unexpected tab selection` , selection ) ;
39+ return ;
40+ }
41+
42+ createMenuForTabs ( selection ) ;
2943 }
3044 } ) ;
3145
@@ -39,44 +53,67 @@ export async function init() {
3953 } ) ;
4054}
4155
42- async function createMenuForTab ( tab :Tab ) {
43- let currentSession :ActiveSession = ActiveSessionManager . getSessionFromTab ( tab ) ;
44- let currentSessionId :SessionId = currentSession ? currentSession . bookmarkId : null ;
56+ async function createMenuForTabs ( tabs :Tab [ ] ) {
57+ // collect active sessions of selected tabs
58+ let currentSessions = new Set < ActiveSession > ( ) ;
59+ let currentSessionIds = new Set < SessionId > ( ) ;
60+ tabs . forEach ( tab => {
61+ let activeSession = ActiveSessionManager . getSessionFromTab ( tab ) ;
62+ if ( activeSession ) {
63+ currentSessions . add ( activeSession ) ;
64+ currentSessionIds . add ( activeSession . bookmarkId ) ;
65+ }
66+ } ) ;
67+
68+ // get list of sessions (active + non-active)
4569 let sessions :Bookmark [ ] = await SessionManager . getSessionBookmarks ( ) ;
4670 let activeSessions :Set < SessionId > = new Set (
4771 ActiveSessionManager . getActiveSessions ( ) . map ( data => data . bookmarkId )
4872 ) ;
73+
74+ addToSessionMenu ( sessions , currentSessionIds , activeSessions , tabs ) ;
4975
50- addToSessionMenu ( sessions , currentSessionId , activeSessions , tab ) ;
51-
52- if ( currentSession ) {
76+ if ( currentSessions . size === 1 ) {
5377 dynamicMenus . push ( browser . menus . create ( {
5478 parentId : "parent" ,
5579 id : "set-aside" ,
5680 title : browser . i18n . getMessage ( "tab_contextmenu_set_aside" ) ,
57- onclick : info => {
58- currentSession . setTabAside ( tab . id ) ;
81+ onclick : async ( info ) => {
82+ let currentSession :ActiveSession = currentSessions . values ( ) . next ( ) . value ;
83+
84+ // set aside all selected/highlighted tabs
85+ for ( let tab of tabs ) {
86+ await currentSession . setTabAside ( tab . id ) ;
87+ }
5988 }
6089 } ) ) ;
61- } else {
62- addAndSetAsideMenu ( sessions , activeSessions , tab ) ;
90+ } else if ( currentSessions . size === 0 ) {
91+ addAndSetAsideMenu ( sessions , activeSessions , tabs ) ;
6392 }
6493
65- browser . menus . refresh ( ) ;
94+ if ( shown ) {
95+ // rebuilding a shown menu is an expensive operation, only invoke this method when necessary
96+ browser . menus . refresh ( ) ;
97+ }
6698}
6799
68100async function addToSessionMenu (
69101 sessions :Bookmark [ ] ,
70- currentSessionId : SessionId | null ,
102+ currentSessionIds : Set < SessionId > ,
71103 activeSessions :Set < SessionId > ,
72- tab :Tab
104+ tabs :Tab [ ]
73105) {
106+ console . assert ( tabs . length > 0 ) ;
107+
74108 if ( sessions . length > 0 ) {
75109 dynamicMenus . push (
76110 browser . menus . create ( {
77111 parentId : "parent" ,
78112 id : "add" ,
79- title : browser . i18n . getMessage ( "tab_contextmenu_add" ) ,
113+ title : browser . i18n . getMessage ( tabs . length > 1 ?
114+ "tab_contextmenu_add_multiple" :
115+ "tab_contextmenu_add"
116+ ) ,
80117 icons : {
81118 "16" : "img/browserMenu/add.svg" ,
82119 "32" : "img/browserMenu/add.svg"
@@ -91,39 +128,44 @@ async function addToSessionMenu(
91128 "16" : "img/browserMenu/active.svg" ,
92129 "32" : "img/browserMenu/active.svg"
93130 } : undefined ,
94- enabled : session . id !== currentSessionId ,
131+ enabled : ! currentSessionIds . has ( session . id ) ,
95132 onclick : async ( info ) => {
96- const data = TabData . createFromTab ( tab ) ;
97133 let added = false ;
98134
99- // move tab to active session
135+ // move tabs to active session
100136 if ( activeSessions . has ( session . id ) ) {
101137 let as = ActiveSessionManager . getActiveSession ( session . id ) ;
102138 console . assert ( as ) ;
103139
104140 // only if the target session has its own window
105141 if ( as . getWindowId ( ) !== null ) {
106- // move or copy tab to new session
107- if ( currentSessionId === null ) {
108- await browser . tabs . move ( tab . id , {
109- windowId : as . getWindowId ( ) ,
110- index : tab . pinned ? 0 : - 1
111- } ) ;
142+ // move or copy tabs to new session
143+ if ( currentSessionIds . size === 0 ) {
144+ for ( let tab of tabs ) {
145+ await browser . tabs . move ( tab . id , {
146+ windowId : as . getWindowId ( ) ,
147+ index : tab . pinned ? 0 : - 1
148+ } ) ;
149+ }
112150 } else {
113- // duplicate tab
114- let details = data . getTabCreateProperties ( true ) ;
115- details . windowId = as . getWindowId ( ) ;
116- await createTab ( details ) ;
151+ // duplicate tabs
152+ for ( let tab of tabs ) {
153+ let details = TabData . createFromTab ( tab ) . getTabCreateProperties ( true ) ;
154+ details . windowId = as . getWindowId ( ) ;
155+ await createTab ( details ) ;
156+ }
117157 }
118158 added = true ;
119159 }
120160 }
121161
122162 // otherwise just create the bookmark
123163 if ( ! added ) {
124- await browser . bookmarks . create (
125- data . getBookmarkCreateDetails ( session . id )
126- ) ;
164+ for ( let tab of tabs ) {
165+ await browser . bookmarks . create (
166+ TabData . createFromTab ( tab ) . getBookmarkCreateDetails ( session . id )
167+ ) ;
168+ }
127169 }
128170
129171 // update sidebar
@@ -136,7 +178,7 @@ async function addToSessionMenu(
136178async function addAndSetAsideMenu (
137179 sessions :Bookmark [ ] ,
138180 activeSessions :Set < SessionId > ,
139- tab :Tab
181+ tabs :Tab [ ]
140182) {
141183 if ( sessions . length > 0 ) {
142184 dynamicMenus . push (
@@ -155,13 +197,15 @@ async function addAndSetAsideMenu(
155197 parentId : "add-n-close" ,
156198 title : "&" + session . title . replace ( / & / ig, "&&" ) . trim ( ) ,
157199 onclick : async ( info ) => {
158- const data = TabData . createFromTab ( tab ) ;
159- await browser . bookmarks . create (
160- data . getBookmarkCreateDetails ( session . id )
161- ) ;
162-
163- browser . tabs . remove ( tab . id ) ;
200+ for ( let tab of tabs ) {
201+ const data = TabData . createFromTab ( tab ) ;
202+ await browser . bookmarks . create (
203+ data . getBookmarkCreateDetails ( session . id )
204+ ) ;
164205
206+ browser . tabs . remove ( tab . id ) ;
207+ }
208+
165209 // update sidebar
166210 SessionContentUpdate . send ( session . id ) ;
167211 }
0 commit comments