2017-11-12 22:00:40 +01:00
// Copyright (c) 2017 kroppy. All rights reserved.
// Use of this source code is governed by a Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license
// that can be found at https://creativecommons.org/licenses/by-nc-nd/4.0/
2017-11-20 18:14:07 +01:00
if ( browserId != "F" ) {
2018-03-13 14:39:34 +01:00
// ConvertLegacyStorage();
2017-11-12 22:00:40 +01:00
ChromeLoadTabs ( 0 ) ;
ChromeMessageListeners ( ) ;
}
function ChromeLoadTabs ( retry ) {
chrome . windows . getAll ( { windowTypes : [ 'normal' ] , populate : true } , function ( w ) {
2018-01-01 19:50:56 +01:00
chrome . storage . local . get ( null , function ( storage ) {
2018-03-13 14:39:34 +01:00
// LOAD PREFERENCES
opt = Object . assign ( { } , DefaultPreferences ) ;
if ( storage [ "preferences" ] ) {
for ( var parameter in storage [ "preferences" ] ) {
if ( opt [ parameter ] != undefined ) {
opt [ parameter ] = storage [ "preferences" ] [ parameter ] ;
}
}
}
// LOAD THEME
if ( storage [ "current_theme" ] && storage [ "themes" ] && storage [ "themes" ] [ storage [ "current_theme" ] ] ) {
theme = storage [ "themes" ] [ storage [ "current_theme" ] ] ;
} else {
theme = Object . assign ( { } , DefaultTheme ) ;
}
// load tabs and windows from storage
2018-01-01 19:50:56 +01:00
var refTabs = { } ;
var tabs _matched = 0 ;
var w _count = storage . w _count ? storage . w _count : 0 ;
var t _count = storage . t _count ? storage . t _count : 0 ;
var LoadedWindows = storage . windows ? storage . windows : [ ] ;
var LoadedTabs = storage . tabs ? storage . tabs : [ ] ;
// if loaded tabs mismatch by 50%, then try to load back
if ( LoadedTabs . length < t _count * 0.5 || retry > 0 ) {
LoadedTabs = storage [ "tabs_BAK" + retry ] ? storage [ "tabs_BAK" + retry ] : [ ] ;
}
// if loaded windows mismatch, then try to load back
if ( LoadedWindows . length < w _count || retry > 0 ) {
LoadedWindows = storage [ "windows_BAK" + retry ] ? storage [ "windows_BAK" + retry ] : [ ] ;
}
// CACHED COUNTS
var WinCount = w . length ;
var LoadedWinCount = LoadedWindows . length ;
var LoadedTabsCount = LoadedTabs . length ;
for ( var wIndex = 0 ; wIndex < WinCount ; wIndex ++ ) {
if ( w [ wIndex ] . tabs [ 0 ] . url != "chrome://videopopout/" ) { // this is for opera for their extra video popup, which is weirdly queried as a "normal" window
let winId = w [ wIndex ] . id ;
let url1 = w [ wIndex ] . tabs [ 0 ] . url ;
let url2 = w [ wIndex ] . tabs [ w [ wIndex ] . tabs . length - 1 ] . url ;
2018-03-13 14:39:34 +01:00
windows [ winId ] = { group _bar : opt . groups _toolbar _default , search _filter : "url" , active _shelf : "" , active _group : "tab_list" , groups : { tab _list : { id : "tab_list" , index : 0 , active _tab : 0 , prev _active _tab : 0 , name : caption _ungrouped _group , font : "" } } , folders : { } } ;
2018-01-01 19:50:56 +01:00
for ( var LwIndex = 0 ; LwIndex < LoadedWinCount ; LwIndex ++ ) {
if ( LoadedWindows [ LwIndex ] . url1 == url1 || LoadedWindows [ LwIndex ] . url2 == url2 ) {
if ( LoadedWindows [ LwIndex ] . group _bar ) { windows [ winId ] . group _bar = LoadedWindows [ LwIndex ] . group _bar ; }
if ( LoadedWindows [ LwIndex ] . search _filter ) { windows [ winId ] . search _filter = LoadedWindows [ LwIndex ] . search _filter ; }
if ( LoadedWindows [ LwIndex ] . active _shelf ) { windows [ winId ] . active _shelf = LoadedWindows [ LwIndex ] . active _shelf ; }
if ( LoadedWindows [ LwIndex ] . active _group ) { windows [ winId ] . active _group = LoadedWindows [ LwIndex ] . active _group ; }
if ( Object . keys ( LoadedWindows [ LwIndex ] . groups ) . length > 0 ) { windows [ winId ] . groups = Object . assign ( { } , LoadedWindows [ LwIndex ] . groups ) ; }
if ( Object . keys ( LoadedWindows [ LwIndex ] . folders ) . length > 0 ) { windows [ winId ] . folders = Object . assign ( { } , LoadedWindows [ LwIndex ] . folders ) ; }
LoadedWindows [ LwIndex ] . url1 = "" ;
LoadedWindows [ LwIndex ] . url2 = "" ;
break ;
}
2017-11-12 22:00:40 +01:00
}
}
}
for ( var wIndex = 0 ; wIndex < WinCount ; wIndex ++ ) {
var TabsCount = w [ wIndex ] . tabs . length ;
for ( var tabIndex = 0 ; tabIndex < TabsCount ; tabIndex ++ ) {
2018-01-01 19:50:56 +01:00
ChromeHashURL ( w [ wIndex ] . tabs [ tabIndex ] ) ;
}
}
if ( opt . skip _load == false && LoadedTabs . length > 0 ) { // compare saved tabs from storage to current session tabs, but can be skipped if set in options
for ( var wIndex = 0 ; wIndex < WinCount ; wIndex ++ ) { // match loaded tabs
var TabsCount = w [ wIndex ] . tabs . length ;
for ( var tabIndex = 0 ; tabIndex < TabsCount ; tabIndex ++ ) {
for ( var LtabIndex = 0 ; LtabIndex < LoadedTabsCount ; LtabIndex ++ ) {
let tabId = w [ wIndex ] . tabs [ tabIndex ] . id ;
if ( LoadedTabs [ LtabIndex ] . hash == tabs [ tabId ] . hash && refTabs [ LoadedTabs [ LtabIndex ] . id ] == undefined ) {
refTabs [ LoadedTabs [ LtabIndex ] . id ] = tabId ;
if ( LoadedTabs [ LtabIndex ] . parent ) { tabs [ tabId ] . parent = LoadedTabs [ LtabIndex ] . parent ; }
if ( LoadedTabs [ LtabIndex ] . index ) { tabs [ tabId ] . index = LoadedTabs [ LtabIndex ] . index ; }
if ( LoadedTabs [ LtabIndex ] . expand ) { tabs [ tabId ] . expand = LoadedTabs [ LtabIndex ] . expand ; }
LoadedTabs [ LtabIndex ] . hash = undefined ;
tabs _matched ++ ;
break ;
}
2017-11-12 22:00:40 +01:00
}
}
}
2018-01-01 19:50:56 +01:00
// replace parents tabIds for new ones, for that purpose refTabs was made before
for ( var tabId in tabs ) {
if ( refTabs [ tabs [ tabId ] . parent ] != undefined ) {
tabs [ tabId ] . parent = refTabs [ tabs [ tabId ] . parent ] ;
}
2017-11-12 22:00:40 +01:00
}
}
2018-01-01 19:50:56 +01:00
// replace active tab ids for each group using refTabs
for ( var windowId in windows ) {
for ( var group in windows [ windowId ] . groups ) {
if ( refTabs [ windows [ windowId ] . groups [ group ] . active _tab ] ) {
windows [ windowId ] . groups [ group ] . active _tab = refTabs [ windows [ windowId ] . groups [ group ] . active _tab ] ;
}
2018-03-13 14:39:34 +01:00
if ( refTabs [ windows [ windowId ] . groups [ group ] . prev _active _tab ] ) {
windows [ windowId ] . groups [ group ] . prev _active _tab = refTabs [ windows [ windowId ] . groups [ group ] . prev _active _tab ] ;
}
2017-11-12 22:00:40 +01:00
}
}
2018-01-01 19:50:56 +01:00
// will try to find tabs for 3 times
if ( opt . skip _load == true || retry > 2 || ( tabs _matched > t _count * 0.5 ) ) {
schedule _save = 1 ;
running = true ;
ChromeAutoSaveData ( 0 , 1000 ) ;
ChromeAutoSaveData ( 1 , 300000 ) ;
ChromeAutoSaveData ( 2 , 600000 ) ;
ChromeAutoSaveData ( 3 , 1800000 ) ;
ChromeListeners ( ) ;
} else {
setTimeout ( function ( ) {
ChromeLoadTabs ( retry + 1 ) ;
} , 2000 ) ;
}
} ) ;
2017-11-12 22:00:40 +01:00
} ) ;
}
// You maybe are asking yourself why I save tabs in array? It's because, instead of, keeping 2 index numbers (one for browser tabs on top and one for my index in tree), it's easier to just arrange them in order and save it in localstorage.
// Another reason is that Object does not preserve order in chrome, I've been told that in Firefox it is. But I can't trust that.
2018-01-01 19:50:56 +01:00
async function ChromeAutoSaveData ( BAK , LoopTimer ) {
2017-11-20 18:14:07 +01:00
setInterval ( function ( ) {
2018-01-01 19:50:56 +01:00
if ( schedule _save > 1 || BAK > 0 ) {
schedule _save = 1 ;
}
2017-11-20 18:14:07 +01:00
if ( running && schedule _save > 0 && Object . keys ( tabs ) . length > 1 ) {
2017-11-12 22:00:40 +01:00
chrome . windows . getAll ( { windowTypes : [ 'normal' ] , populate : true } , function ( w ) {
var WinCount = w . length ;
var t _count = 0 ;
var counter = 0 ;
var Windows = [ ] ;
var Tabs = [ ] ;
for ( var wIndex = 0 ; wIndex < WinCount ; wIndex ++ ) {
t _count += w [ wIndex ] . tabs . length ;
}
for ( var wIndex = 0 ; wIndex < WinCount ; wIndex ++ ) {
let winId = w [ wIndex ] . id ;
2017-11-20 18:14:07 +01:00
if ( windows [ winId ] != undefined && windows [ winId ] . group _bar != undefined && windows [ winId ] . search _filter != undefined && windows [ winId ] . active _shelf != undefined && windows [ winId ] . active _group != undefined && windows [ winId ] . groups != undefined && windows [ winId ] . folders != undefined ) {
Windows . push ( { url1 : w [ wIndex ] . tabs [ 0 ] . url , url2 : w [ wIndex ] . tabs [ w [ wIndex ] . tabs . length - 1 ] . url , group _bar : windows [ winId ] . group _bar , search _filter : windows [ winId ] . search _filter , active _shelf : windows [ winId ] . active _shelf , active _group : windows [ winId ] . active _group , groups : windows [ winId ] . groups , folders : windows [ winId ] . folders } ) ;
2017-11-12 22:00:40 +01:00
}
let TabsCount = w [ wIndex ] . tabs . length ;
for ( var tabIndex = 0 ; tabIndex < TabsCount ; tabIndex ++ ) {
let tabId = w [ wIndex ] . tabs [ tabIndex ] . id ;
if ( tabs [ tabId ] != undefined && tabs [ tabId ] . hash != undefined && tabs [ tabId ] . parent != undefined && tabs [ tabId ] . index != undefined && tabs [ tabId ] . expand != undefined ) {
Tabs . push ( { id : tabId , hash : tabs [ tabId ] . hash , parent : tabs [ tabId ] . parent , index : tabs [ tabId ] . index , expand : tabs [ tabId ] . expand } ) ;
counter ++ ;
}
}
if ( counter == t _count ) {
2018-01-01 19:50:56 +01:00
chrome . storage . local . set ( { t _count : t _count } ) ;
chrome . storage . local . set ( { w _count : WinCount } ) ;
if ( BAK == 0 ) {
chrome . storage . local . set ( { windows : Windows } ) ;
chrome . storage . local . set ( { tabs : Tabs } ) ;
}
if ( BAK == 1 ) {
chrome . storage . local . set ( { windows _BAK1 : Windows } ) ;
chrome . storage . local . set ( { tabs _BAK1 : Tabs } ) ;
chrome . runtime . sendMessage ( { command : "backup_available" , bak : 1 } ) ;
}
if ( BAK == 2 ) {
chrome . storage . local . set ( { windows _BAK2 : Windows } ) ;
chrome . storage . local . set ( { tabs _BAK2 : Tabs } ) ;
chrome . runtime . sendMessage ( { command : "backup_available" , bak : 2 } ) ;
}
if ( BAK == 3 ) {
chrome . storage . local . set ( { windows _BAK3 : Windows } ) ;
chrome . storage . local . set ( { tabs _BAK3 : Tabs } ) ;
chrome . runtime . sendMessage ( { command : "backup_available" , bak : 3 } ) ;
}
2017-11-12 22:00:40 +01:00
}
}
schedule _save -- ;
} ) ;
}
} , LoopTimer ) ;
}
2018-01-01 19:50:56 +01:00
function ChromeHashURL ( tab ) {
2017-11-12 22:00:40 +01:00
if ( tabs [ tab . id ] == undefined ) {
2018-03-13 14:39:34 +01:00
tabs [ tab . id ] = { hash : 0 , parent : tab . pinned ? "pin_list" : ( windows [ tab . windowId ] ? windows [ tab . windowId ] . active _group : "tab_list" ) , index : tab . index , expand : "n" } ;
2017-11-12 22:00:40 +01:00
}
var hash = 0 ;
2018-01-01 19:50:56 +01:00
for ( var charIndex = 0 ; charIndex < tab . url . length ; charIndex ++ ) {
2017-11-12 22:00:40 +01:00
hash += tab . url . charCodeAt ( charIndex ) ;
}
tabs [ tab . id ] . hash = hash ;
}
function ReplaceParents ( oldTabId , newTabId ) {
for ( var tabId in tabs ) {
if ( tabs [ tabId ] . parent == oldTabId ) {
tabs [ tabId ] . parent = newTabId ;
}
}
}
2018-01-01 19:50:56 +01:00
function ChromeListeners ( ) { // start all listeners
2017-11-12 22:00:40 +01:00
chrome . tabs . onCreated . addListener ( function ( tab ) {
ChromeHashURL ( tab ) ;
chrome . runtime . sendMessage ( { command : "tab_created" , windowId : tab . windowId , tab : tab , tabId : tab . id } ) ;
schedule _save ++ ;
} ) ;
chrome . tabs . onRemoved . addListener ( function ( tabId , removeInfo ) {
2017-11-20 18:14:07 +01:00
setTimeout ( function ( ) { chrome . runtime . sendMessage ( { command : "tab_removed" , windowId : removeInfo . windowId , tabId : tabId } ) ; } , 5 ) ;
2017-11-12 22:00:40 +01:00
delete tabs [ tabId ] ;
schedule _save ++ ;
} ) ;
chrome . tabs . onAttached . addListener ( function ( tabId , attachInfo ) {
chrome . tabs . get ( tabId , function ( tab ) {
chrome . runtime . sendMessage ( { command : "tab_attached" , windowId : attachInfo . newWindowId , tab : tab , tabId : tabId , ParentId : tabs [ tabId ] . parent } ) ;
} ) ;
schedule _save ++ ;
} ) ;
chrome . tabs . onDetached . addListener ( function ( tabId , detachInfo ) {
chrome . runtime . sendMessage ( { command : "tab_detached" , windowId : detachInfo . oldWindowId , tabId : tabId } ) ;
schedule _save ++ ;
} ) ;
chrome . tabs . onUpdated . addListener ( function ( tabId , changeInfo , tab ) {
if ( tabs [ tabId ] == undefined || changeInfo . url != undefined ) {
ChromeHashURL ( tab ) ;
}
if ( changeInfo . pinned != undefined ) {
schedule _save ++ ;
}
if ( changeInfo . pinned == true ) {
tabs [ tabId ] . parent = "pin_list" ;
}
if ( changeInfo . title != undefined && ! tab . active ) {
chrome . runtime . sendMessage ( { command : "tab_attention" , windowId : tab . windowId , tabId : tabId } ) ;
}
chrome . runtime . sendMessage ( { command : "tab_updated" , windowId : tab . windowId , tab : tab , tabId : tabId , changeInfo : changeInfo } ) ;
} ) ;
chrome . tabs . onMoved . addListener ( function ( tabId , moveInfo ) {
schedule _save ++ ;
} ) ;
chrome . tabs . onReplaced . addListener ( function ( addedTabId , removedTabId ) {
chrome . tabs . get ( addedTabId , function ( tab ) {
if ( addedTabId == removedTabId ) {
chrome . runtime . sendMessage ( { command : "tab_updated" , windowId : tab . windowId , tab : tab , tabId : tab . id , changeInfo : { status : tab . status , url : tab . url , title : tab . title , audible : tab . audible , mutedInfo : tab . mutedInfo } } ) ;
} else {
ReplaceParents ( tabId , tab . id ) ;
if ( tabs [ removedTabId ] ) {
tabs [ addedTabId ] = tabs [ removedTabId ] ;
} else {
ChromeHashURL ( tab ) ;
}
chrome . runtime . sendMessage ( { command : "tab_removed" , windowId : tab . windowId , tabId : removedTabId } ) ;
chrome . runtime . sendMessage ( { command : "tab_attached" , windowId : tab . windowId , tab : tab , tabId : addedTabId } ) ;
delete tabs [ removedTabId ] ;
}
schedule _save ++ ;
} ) ;
} ) ;
chrome . tabs . onActivated . addListener ( function ( activeInfo ) {
chrome . runtime . sendMessage ( { command : "tab_activated" , windowId : activeInfo . windowId , tabId : activeInfo . tabId } ) ;
} ) ;
chrome . windows . onCreated . addListener ( function ( window ) {
2018-03-13 14:39:34 +01:00
windows [ window . id ] = { group _bar : opt . groups _toolbar _default , search _filter : "url" , active _shelf : "" , active _group : "tab_list" , groups : { tab _list : { id : "tab_list" , index : 0 , active _tab : 0 , prev _active _tab : 0 , name : caption _ungrouped _group , font : "" } } , folders : { } } ;
2017-11-12 22:00:40 +01:00
schedule _save ++ ;
} ) ;
chrome . windows . onRemoved . addListener ( function ( windowId ) {
delete windows [ windowId ] ;
schedule _save ++ ;
} ) ;
chrome . runtime . onSuspend . addListener ( function ( ) {
2017-11-20 18:14:07 +01:00
running = false ;
2017-11-12 22:00:40 +01:00
} ) ;
}
function ChromeMessageListeners ( ) {
chrome . runtime . onMessage . addListener ( function ( message , sender , sendResponse ) {
2018-03-13 14:39:34 +01:00
if ( opt . debug ) console . log ( "message to background:" ) ;
if ( opt . debug ) console . log ( message ) ;
if ( message . command == "reload" ) {
window . location . reload ( ) ;
return ;
}
if ( message . command == "get_preferences" ) {
sendResponse ( opt ) ;
return ;
}
if ( message . command == "save_preferences" ) {
opt = Object . assign ( { } , message . opt ) ;
chrome . storage . local . set ( { preferences : message . opt } ) ;
return ;
}
if ( message . command == "get_windows" ) {
sendResponse ( windows ) ;
return ;
}
if ( message . command == "get_folders" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . folders ) ;
}
return ;
}
if ( message . command == "save_folders" ) {
if ( windows [ message . windowId ] ) {
2018-01-01 19:50:56 +01:00
windows [ message . windowId ] . folders = Object . assign ( { } , message . folders ) ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "get_groups" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . groups ) ;
}
return ;
}
if ( message . command == "save_groups" ) {
if ( windows [ message . windowId ] ) {
2017-11-12 22:00:40 +01:00
windows [ message . windowId ] . groups = Object . assign ( { } , message . groups ) ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "set_active_group" ) {
if ( windows [ message . windowId ] ) {
2017-11-12 22:00:40 +01:00
windows [ message . windowId ] . active _group = message . active _group ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "get_active_group" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . active _group ) ;
}
return ;
}
if ( message . command == "set_search_filter" ) {
if ( windows [ message . windowId ] ) {
2017-11-20 18:14:07 +01:00
windows [ message . windowId ] . search _filter = message . search _filter ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "get_search_filter" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . search _filter ) ;
}
return ;
}
if ( message . command == "set_active_shelf" ) {
if ( windows [ message . windowId ] ) {
2017-11-12 22:00:40 +01:00
windows [ message . windowId ] . active _shelf = message . active _shelf ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "get_active_shelf" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . active _shelf ) ;
}
return ;
}
if ( message . command == "set_group_bar" ) {
if ( windows [ message . windowId ] ) {
2017-11-12 22:00:40 +01:00
windows [ message . windowId ] . group _bar = message . group _bar ;
schedule _save ++ ;
2018-03-13 14:39:34 +01:00
}
return ;
}
if ( message . command == "get_group_bar" ) {
if ( windows [ message . windowId ] ) {
sendResponse ( windows [ message . windowId ] . group _bar ) ;
}
return ;
}
if ( message . command == "get_browser_tabs" ) {
sendResponse ( tabs ) ;
return ;
}
if ( message . command == "is_bg_ready" ) {
sendResponse ( running ) ;
return ;
}
if ( message . command == "update_tab" ) {
if ( tabs [ message . tabId ] ) {
if ( message . tab . index ) {
tabs [ message . tabId ] . index = message . tab . index ;
}
if ( message . tab . expand ) {
tabs [ message . tabId ] . expand = message . tab . expand ;
}
if ( message . tab . parent ) {
tabs [ message . tabId ] . parent = message . tab . parent ;
}
schedule _save ++ ;
}
return ;
}
if ( message . command == "update_all_tabs" ) {
for ( let i = 0 ; i < message . pins . length ; i ++ ) {
if ( tabs [ message . pins [ i ] . id ] ) {
tabs [ message . pins [ i ] . id ] . parent = "pin_list" ;
tabs [ message . pins [ i ] . id ] . expand = "" ;
tabs [ message . pins [ i ] . id ] . index = message . pins [ i ] . index ;
2017-11-12 22:00:40 +01:00
}
2018-03-13 14:39:34 +01:00
}
for ( let j = 0 ; j < message . tabs . length ; j ++ ) {
if ( tabs [ message . tabs [ j ] . id ] ) {
tabs [ message . tabs [ j ] . id ] . parent = message . tabs [ j ] . parent ;
tabs [ message . tabs [ j ] . id ] . expand = message . tabs [ j ] . expand ;
tabs [ message . tabs [ j ] . id ] . index = message . tabs [ j ] . index ;
2017-11-12 22:00:40 +01:00
}
2018-03-13 14:39:34 +01:00
}
schedule _save ++ ;
return ;
}
if ( message . command == "get_theme" ) {
sendResponse ( theme ) ;
return ;
}
if ( message . command == "reload_theme" ) {
GetCurrentTheme ( ) ;
return ;
2017-11-12 22:00:40 +01:00
}
} ) ;
2018-01-01 19:50:56 +01:00
}