@ -1,6 +1,6 @@
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
uMatrix - a Chromium browser extension to black / white list requests .
uMatrix - a browser extension to black / white list requests .
Copyright ( C ) 2014 - 2018 Raymond Hill
Copyright ( C ) 2014 - 2018 Raymond Hill
This program is free software : you can redistribute it and / or modify
This program is free software : you can redistribute it and / or modify
@ -140,7 +140,7 @@ housekeep itself.
* /
* /
µm . tabContextManager = ( function ( ) {
µm . tabContextManager = ( function ( ) {
var tabContexts = Object . create ( null ) ;
var tabContexts = new Map ( ) ;
// https://github.com/chrisaljoudi/uBlock/issues/1001
// https://github.com/chrisaljoudi/uBlock/issues/1001
// This is to be used as last-resort fallback in case a tab is found to not
// This is to be used as last-resort fallback in case a tab is found to not
@ -170,18 +170,16 @@ housekeep itself.
this . commitTimer = null ;
this . commitTimer = null ;
this . gcTimer = null ;
this . gcTimer = null ;
tabContexts [tabId ] = this ;
tabContexts .set ( tabId , this ) ;
} ;
} ;
TabContext . prototype . destroy = function ( ) {
TabContext . prototype . destroy = function ( ) {
if ( vAPI . isBehindTheSceneTabId ( this . tabId ) ) {
if ( vAPI . isBehindTheSceneTabId ( this . tabId ) ) { return ; }
return ;
}
if ( this . gcTimer !== null ) {
if ( this . gcTimer !== null ) {
clearTimeout ( this . gcTimer ) ;
clearTimeout ( this . gcTimer ) ;
this . gcTimer = null ;
this . gcTimer = null ;
}
}
delete tabContexts [ this . tabId ] ;
tabContexts . delete ( this . tabId ) ;
} ;
} ;
TabContext . prototype . onTab = function ( tab ) {
TabContext . prototype . onTab = function ( tab ) {
@ -285,7 +283,7 @@ housekeep itself.
// These are to be used for the API of the tab context manager.
// These are to be used for the API of the tab context manager.
var push = function ( tabId , url , context ) {
var push = function ( tabId , url , context ) {
var entry = tabContexts [ tabId ] ;
let entry = tabContexts . get ( tabId ) ;
if ( entry === undefined ) {
if ( entry === undefined ) {
entry = new TabContext ( tabId ) ;
entry = new TabContext ( tabId ) ;
entry . autodestroy ( ) ;
entry . autodestroy ( ) ;
@ -303,7 +301,7 @@ housekeep itself.
if ( url !== undefined ) {
if ( url !== undefined ) {
entry = push ( tabId , url ) ;
entry = push ( tabId , url ) ;
} else {
} else {
entry = tabContexts [tabId ] ;
entry = tabContexts .get ( tabId ) ;
}
}
if ( entry !== undefined ) {
if ( entry !== undefined ) {
return entry ;
return entry ;
@ -329,11 +327,11 @@ housekeep itself.
// about to fall through the cracks.
// about to fall through the cracks.
// Example: Chromium + case #12 at
// Example: Chromium + case #12 at
// http://raymondhill.net/ublock/popup.html
// http://raymondhill.net/ublock/popup.html
return tabContexts [vAPI . noTabId ] ;
return tabContexts .get ( vAPI . noTabId ) ;
} ;
} ;
var lookup = function ( tabId ) {
var lookup = function ( tabId ) {
return tabContexts [tabId ] || null ;
return tabContexts .get ( tabId ) || null ;
} ;
} ;
// Behind-the-scene tab context
// Behind-the-scene tab context
@ -372,7 +370,7 @@ housekeep itself.
vAPI . tabs . onClosed = function ( tabId ) {
vAPI . tabs . onClosed = function ( tabId ) {
µm . unbindTabFromPageStats ( tabId ) ;
µm . unbindTabFromPageStats ( tabId ) ;
var entry = tabContexts [ tabId ] ;
let entry = tabContexts . get ( tabId ) ;
if ( entry instanceof TabContext ) {
if ( entry instanceof TabContext ) {
entry . destroy ( ) ;
entry . destroy ( ) ;
}
}
@ -397,7 +395,7 @@ vAPI.tabs.registerListeners();
// Do not create a page store for URLs which are of no interests
// Do not create a page store for URLs which are of no interests
// Example: dev console
// Example: dev console
var tabContext = this . tabContextManager . lookup ( tabId ) ;
let tabContext = this . tabContextManager . lookup ( tabId ) ;
if ( tabContext === null ) {
if ( tabContext === null ) {
throw new Error ( 'Unmanaged tab id: ' + tabId ) ;
throw new Error ( 'Unmanaged tab id: ' + tabId ) ;
}
}
@ -406,14 +404,14 @@ vAPI.tabs.registerListeners();
// virtual tab.
// virtual tab.
// https://github.com/gorhill/httpswitchboard/issues/67
// https://github.com/gorhill/httpswitchboard/issues/67
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) {
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) {
return this . pageStores [tabId ] ;
return this . pageStores .get ( tabId ) ;
}
}
var normalURL = tabContext . normalURL ;
let normalURL = tabContext . normalURL ;
var pageStore = this . pageStores [ tabId ] || null ;
let pageStore = this . pageStores . get ( tabId ) ;
// The previous page URL, if any, associated with the tab
// The previous page URL, if any, associated with the tab
if ( pageStore !== null ) {
if ( pageStore !== undefined ) {
// No change, do not rebind
// No change, do not rebind
if ( pageStore . pageUrl === normalURL ) {
if ( pageStore . pageUrl === normalURL ) {
return pageStore ;
return pageStore ;
@ -425,7 +423,10 @@ vAPI.tabs.registerListeners();
// Example: Google Maps, Github
// Example: Google Maps, Github
// https://github.com/gorhill/uMatrix/issues/72
// https://github.com/gorhill/uMatrix/issues/72
// Need to double-check that the new scope is same as old scope
// Need to double-check that the new scope is same as old scope
if ( context === 'updateURL' && pageStore . pageHostname === tabContext . rootHostname ) {
if (
context === 'updateURL' &&
pageStore . pageHostname === tabContext . rootHostname
) {
pageStore . rawURL = tabContext . rawURL ;
pageStore . rawURL = tabContext . rawURL ;
pageStore . normalURL = normalURL ;
pageStore . normalURL = normalURL ;
this . updateTitle ( tabId ) ;
this . updateTitle ( tabId ) ;
@ -442,29 +443,22 @@ vAPI.tabs.registerListeners();
if ( pageStore === null ) {
if ( pageStore === null ) {
pageStore = this . pageStoreFactory ( tabContext ) ;
pageStore = this . pageStoreFactory ( tabContext ) ;
}
}
this . pageStores [tabId ] = pageStore ;
this . pageStores .set ( tabId , pageStore ) ;
this . updateTitle ( tabId ) ;
this . updateTitle ( tabId ) ;
this . pageStoresToken = Date . now ( ) ;
this . pageStoresToken = Date . now ( ) ;
// console.debug('tab.js > bindTabToPageStats(): dispatching traffic in tab id %d to page store "%s"', tabId, pageUrl);
return pageStore ;
return pageStore ;
} ;
} ;
/******************************************************************************/
/******************************************************************************/
µm . unbindTabFromPageStats = function ( tabId ) {
µm . unbindTabFromPageStats = function ( tabId ) {
// Never unbind behind-the-scene page store.
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) { return ; }
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) {
return ;
}
var pageStore = this . pageStores [ tabId ] || null ;
let pageStore = this . pageStores . get ( tabId ) ;
if ( pageStore === null ) {
if ( pageStore === undefined ) { return ; }
return ;
}
delete this . pageStores [ tabId ] ;
this . pageStores . delete ( tabId ) ;
this . pageStoresToken = Date . now ( ) ;
this . pageStoresToken = Date . now ( ) ;
if ( pageStore . incinerationTimer ) {
if ( pageStore . incinerationTimer ) {
@ -472,13 +466,13 @@ vAPI.tabs.registerListeners();
pageStore . incinerationTimer = null ;
pageStore . incinerationTimer = null ;
}
}
if ( this . pageStoreCemetery . hasOwnProperty ( tabId ) === false ) {
let pageStoreCrypt = this . pageStoreCemetery . get ( tabId ) ;
this . pageStoreCemetery [ tabId ] = { } ;
if ( pageStoreCrypt === undefined ) {
this . pageStoreCemetery . set ( tabId , ( pageStoreCrypt = new Map ( ) ) ) ;
}
}
var pageStoreCrypt = this . pageStoreCemetery [ tabId ] ;
var pageURL = pageStore . pageUrl ;
let pageURL = pageStore . pageUrl ;
pageStoreCrypt [pageURL ] = pageStore ;
pageStoreCrypt .set ( pageURL , pageStore ) ;
pageStore . incinerationTimer = vAPI . setTimeout (
pageStore . incinerationTimer = vAPI . setTimeout (
this . incineratePageStore . bind ( this , tabId , pageURL ) ,
this . incineratePageStore . bind ( this , tabId , pageURL ) ,
@ -489,25 +483,21 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
/******************************************************************************/
µm . resurrectPageStore = function ( tabId , pageURL ) {
µm . resurrectPageStore = function ( tabId , pageURL ) {
if ( this . pageStoreCemetery . hasOwnProperty ( tabId ) === false ) {
let pageStoreCrypt = this . pageStoreCemetery . get ( tabId ) ;
return null ;
if ( pageStoreCrypt === undefined ) { return null ; }
}
var pageStoreCrypt = this . pageStoreCemetery [ tabId ] ;
if ( pageStoreCrypt . hasOwnProperty ( pageURL ) === false ) {
let pageStore = pageStoreCrypt . get ( pageURL ) ;
return null ;
if ( pageStore === undefined ) { return null ; }
}
var pageStore = pageStoreCrypt [ pageURL ] ;
if ( pageStore . incinerationTimer !== null ) {
if ( pageStore . incinerationTimer !== null ) {
clearTimeout ( pageStore . incinerationTimer ) ;
clearTimeout ( pageStore . incinerationTimer ) ;
pageStore . incinerationTimer = null ;
pageStore . incinerationTimer = null ;
}
}
delete pageStoreCrypt [ pageURL ] ;
pageStoreCrypt . delete ( pageURL ) ;
if ( Object . keys ( pageStoreCrypt ) . length === 0 ) {
if ( pageStoreCrypt . size === 0 ) {
delete this . pageStoreCemetery [ tabId ] ;
this. pageStoreCemetery . delete ( tabId ) ;
}
}
return pageStore ;
return pageStore ;
@ -516,24 +506,20 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
/******************************************************************************/
µm . incineratePageStore = function ( tabId , pageURL ) {
µm . incineratePageStore = function ( tabId , pageURL ) {
if ( this . pageStoreCemetery . hasOwnProperty ( tabId ) === false ) {
let pageStoreCrypt = this . pageStoreCemetery . get ( tabId ) ;
return ;
if ( pageStoreCrypt === undefined ) { return ; }
}
var pageStoreCrypt = this . pageStoreCemetery [ tabId ] ;
if ( pageStoreCrypt . hasOwnProperty ( pageURL ) === false ) {
let pageStore = pageStoreCrypt . get ( pageURL ) ;
return ;
if ( pageStore === undefined ) { return ; }
}
var pageStore = pageStoreCrypt [ pageURL ] ;
if ( pageStore . incinerationTimer !== null ) {
if ( pageStore . incinerationTimer !== null ) {
clearTimeout ( pageStore . incinerationTimer ) ;
clearTimeout ( pageStore . incinerationTimer ) ;
pageStore . incinerationTimer = null ;
pageStore . incinerationTimer = null ;
}
}
delete pageStoreCrypt [ pageURL ] ;
pageStoreCrypt . delete ( pageURL ) ;
if ( Object . keys ( pageStoreCrypt ) . length === 0 ) {
if ( pageStoreCrypt . size === 0 ) {
delete this . pageStoreCemetery [ tabId ] ;
this. pageStoreCemetery . delete ( tabId ) ;
}
}
pageStore . dispose ( ) ;
pageStore . dispose ( ) ;
@ -542,12 +528,12 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
/******************************************************************************/
µm . pageStoreFromTabId = function ( tabId ) {
µm . pageStoreFromTabId = function ( tabId ) {
return this . pageStores [tabId ] || null ;
return this . pageStores .get ( tabId ) || null ;
} ;
} ;
// Never return null
// Never return null
µm . mustPageStoreFromTabId = function ( tabId ) {
µm . mustPageStoreFromTabId = function ( tabId ) {
return this . pageStores [tabId ] || this . pageStores [ vAPI . noTabId ] ;
return this . pageStores .get ( tabId ) || this . pageStores . get ( vAPI . noTabId ) ;
} ;
} ;
/******************************************************************************/
/******************************************************************************/
@ -608,25 +594,26 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
/******************************************************************************/
µm . updateTitle = ( function ( ) {
µm . updateTitle = ( function ( ) {
var tabIdToTimer = Object . create ( null ) ;
let tabIdToTimer = new Map ( ) ;
var tabIdToTryCount = Object . create ( null ) ;
let tabIdToTryCount = new Map ( ) ;
var delay = 499 ;
let delay = 499 ;
var tryNoMore = function ( tabId ) {
let tryNoMore = function ( tabId ) {
delete tabIdToTryCount [ tabId ] ;
tabIdToTryCount . delete ( tabId ) ;
} ;
} ;
var tryAgain = function ( tabId ) {
let tryAgain = function ( tabId ) {
var count = tabIdToTryCount [ tabId ] ;
let count = tabIdToTryCount . get ( tabId ) ;
if ( count === undefined ) {
if ( count === undefined ) { return false ; }
return false ;
}
if ( count === 1 ) {
if ( count === 1 ) {
delete tabIdToTryCount [ tabId ] ;
tabIdToTryCount . delete ( tabId ) ;
return false ;
return false ;
}
}
tabIdToTryCount [ tabId ] = count - 1 ;
tabIdToTryCount . set ( tabId , count - 1 ) ;
tabIdToTimer [ tabId ] = vAPI . setTimeout ( updateTitle . bind ( µm , tabId ) , delay ) ;
tabIdToTimer . set (
tabId ,
vAPI . setTimeout ( updateTitle . bind ( µm , tabId ) , delay )
) ;
return true ;
return true ;
} ;
} ;
@ -652,19 +639,21 @@ vAPI.tabs.registerListeners();
} ;
} ;
var updateTitle = function ( tabId ) {
var updateTitle = function ( tabId ) {
delete tabIdToTimer [ tabId ] ;
tabIdToTimer . delete ( tabId ) ;
vAPI . tabs . get ( tabId , onTabReady . bind ( this , tabId ) ) ;
vAPI . tabs . get ( tabId , onTabReady . bind ( this , tabId ) ) ;
} ;
} ;
return function ( tabId ) {
return function ( tabId ) {
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) {
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) { return ; }
return ;
let timer = tabIdToTimer . get ( tabId ) ;
}
if ( timer !== undefined ) {
if ( tabIdToTimer [ tabId ] ) {
clearTimeout ( timer ) ;
clearTimeout ( tabIdToTimer [ tabId ] ) ;
}
}
tabIdToTimer [ tabId ] = vAPI . setTimeout ( updateTitle . bind ( this , tabId ) , delay ) ;
tabIdToTimer . set (
tabIdToTryCount [ tabId ] = 5 ;
tabId ,
vAPI . setTimeout ( updateTitle . bind ( this , tabId ) , delay )
) ;
tabIdToTryCount . set ( tabId , 5 ) ;
} ;
} ;
} ) ( ) ;
} ) ( ) ;
@ -680,7 +669,7 @@ vAPI.tabs.registerListeners();
var cleanup = function ( ) {
var cleanup = function ( ) {
var vapiTabs = vAPI . tabs ;
var vapiTabs = vAPI . tabs ;
var tabIds = Object. keys ( µm . pageStores ) . sort ( ) ;
var tabIds = Array. from ( µm . pageStores . keys ( ) ) . sort ( ) ;
var checkTab = function ( tabId ) {
var checkTab = function ( tabId ) {
vapiTabs . get ( tabId , function ( tab ) {
vapiTabs . get ( tabId , function ( tab ) {
if ( ! tab ) {
if ( ! tab ) {
@ -695,9 +684,7 @@ vAPI.tabs.registerListeners();
var n = Math . min ( cleanupSampleAt + cleanupSampleSize , tabIds . length ) ;
var n = Math . min ( cleanupSampleAt + cleanupSampleSize , tabIds . length ) ;
for ( var i = cleanupSampleAt ; i < n ; i ++ ) {
for ( var i = cleanupSampleAt ; i < n ; i ++ ) {
tabId = tabIds [ i ] ;
tabId = tabIds [ i ] ;
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) {
if ( vAPI . isBehindTheSceneTabId ( tabId ) ) { continue ; }
continue ;
}
checkTab ( tabId ) ;
checkTab ( tabId ) ;
}
}
cleanupSampleAt = n ;
cleanupSampleAt = n ;