Mind discarded status of tabs when internally handling them

Related discussion:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1652925#c19

Content scripts should not be injected in discarded tabs, and
a discarded tab should treated as if it does not exist.
This commit is contained in:
Raymond Hill 2020-07-19 17:41:13 -04:00
parent 3b46b2532d
commit aed850978e
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 68 additions and 71 deletions

View File

@ -52,38 +52,45 @@ vAPI.app.onShutdown = function() {
// the extension was launched. It can be used to inject content scripts
// in already opened web pages, to remove whatever nuisance could make it to
// the web pages before uBlock was ready.
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=1652925#c19
// Mind discarded tabs.
const initializeTabs = async function() {
const manifest = browser.runtime.getManifest();
if ( manifest instanceof Object === false ) { return; }
const tabs = await vAPI.tabs.query({ url: '<all_urls>' });
const toCheck = [];
const checker = {
file: 'js/scriptlets/should-inject-contentscript.js'
};
for ( const tab of tabs ) {
µb.tabContextManager.commit(tab.id, tab.url);
µb.bindTabToPageStats(tab.id);
// https://github.com/chrisaljoudi/uBlock/issues/129
// Find out whether content scripts need to be injected
// programmatically. This may be necessary for web pages which
// were loaded before uBO launched.
toCheck.push(
/^https?:\/\//.test(tab.url)
? vAPI.tabs.executeScript(tab.id, checker)
: false
);
const tabIds = [];
{
const checker = { file: 'js/scriptlets/should-inject-contentscript.js' };
const tabs = await vAPI.tabs.query({ url: '<all_urls>' });
for ( const tab of tabs ) {
if ( tab.discarded === true ) { continue; }
const { id, url } = tab;
µb.tabContextManager.commit(id, url);
µb.bindTabToPageStats(id);
// https://github.com/chrisaljoudi/uBlock/issues/129
// Find out whether content scripts need to be injected
// programmatically. This may be necessary for web pages which
// were loaded before uBO launched.
toCheck.push(
/^https?:\/\//.test(url)
? vAPI.tabs.executeScript(id, checker)
.then(result => result, ( ) => false)
: false
);
tabIds.push(id);
}
}
const results = await Promise.all(toCheck);
for ( let i = 0; i < results.length; i++ ) {
const result = results[i];
if ( result.length === 0 || result[0] !== true ) { continue; }
// Inject dclarative content scripts programmatically.
const tabId = tabs[i].id;
// Inject declarative content scripts programmatically.
for ( const contentScript of manifest.content_scripts ) {
for ( const file of contentScript.js ) {
vAPI.tabs.executeScript(tabId, {
vAPI.tabs.executeScript(tabIds[i], {
file: file,
allFrames: contentScript.all_frames,
runAt: contentScript.run_at

View File

@ -577,26 +577,22 @@ housekeep itself.
tabContexts.delete(this.tabId);
};
TabContext.prototype.onTab = function(tab) {
if ( tab ) {
this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod);
} else {
this.destroy();
}
};
TabContext.prototype.onGC = async function() {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; }
// https://github.com/gorhill/uBlock/issues/1713
// For unknown reasons, Firefox's setTimeout() will sometimes
// causes the callback function to be called immediately, bypassing
// the main event loop. For now this should prevent uBO from crashing
// as a result of the bad setTimeout() behavior.
// For unknown reasons, Firefox's setTimeout() will sometimes
// causes the callback function to be called immediately, bypassing
// the main event loop. For now this should prevent uBO from
// crashing as a result of the bad setTimeout() behavior.
if ( this.onGCBarrier ) { return; }
this.onGCBarrier = true;
this.gcTimer = null;
const tab = await vAPI.tabs.get(this.tabId);
this.onTab(tab);
if ( tab instanceof Object === false || tab.discarded === true ) {
this.destroy();
} else {
this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod);
}
this.onGCBarrier = false;
};
@ -1074,23 +1070,19 @@ vAPI.tabs = new vAPI.Tabs();
/******************************************************************************/
µBlock.updateTitle = (( ) => {
const tabIdToTimer = new Map();
const tabIdToCount = new Map();
const delay = 499;
const tryAgain = function(entry) {
if ( entry.count === 1 ) { return false; }
entry.count -= 1;
tabIdToTimer.set(
entry.tabId,
vAPI.setTimeout(( ) => { updateTitle(entry); }, delay)
);
return true;
};
const onTabReady = function(entry, tab) {
if ( !tab ) { return; }
const updateTitle = async function(tabId) {
let count = tabIdToCount.get(tabId);
if ( count === undefined ) { return; }
tabIdToCount.delete(tabId);
const tab = await vAPI.tabs.get(tabId);
if ( tab instanceof Object === false || tab.discarded === true ) {
return;
}
const µb = µBlock;
const pageStore = µb.pageStoreFromTabId(entry.tabId);
const pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore === null ) { return; }
// Firefox needs this: if you detach a tab, the new tab won't have
// its rawURL set. Concretely, this causes the logger to report an
@ -1098,35 +1090,32 @@ vAPI.tabs = new vAPI.Tabs();
// TODO: Investigate for a fix vAPI-side.
pageStore.rawURL = tab.url;
µb.pageStoresToken = Date.now();
if ( !tab.title && tryAgain(entry) ) { return; }
// https://github.com/gorhill/uMatrix/issues/225
// Sometimes title changes while page is loading.
const settled = tab.title && tab.title === pageStore.title;
// Sometimes title changes while page is loading.
const settled =
typeof tab.title === 'string' &&
tab.title !== '' &&
tab.title === pageStore.title;
pageStore.title = tab.title || tab.url || '';
if ( !settled ) {
tryAgain(entry);
}
if ( settled ) { return; }
if ( tabIdToCount.has(tabId) ) { return; }
count -= 1;
if ( count === 0 ) { return; }
tabIdToCount.set(tabId, count);
updateTitleAsync(tabId);
};
const updateTitle = async function(entry) {
tabIdToTimer.delete(entry.tabId);
const tab = await vAPI.tabs.get(entry.tabId);
onTabReady(entry, tab);
const updateTitleAsync = function(tabId) {
vAPI.setTimeout(( ) => { updateTitle(tabId); }, delay);
};
return function(tabId) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; }
const timer = tabIdToTimer.get(tabId);
if ( timer !== undefined ) {
clearTimeout(timer);
const count = tabIdToCount.get(tabId);
tabIdToCount.set(tabId, 5);
if ( count === undefined ) {
updateTitleAsync(tabId);
}
tabIdToTimer.set(
tabId,
vAPI.setTimeout(
updateTitle.bind(null, { tabId: tabId, count: 5 }),
delay
)
);
};
})();
@ -1140,13 +1129,14 @@ vAPI.tabs = new vAPI.Tabs();
let pageStoreJanitorSampleAt = 0;
let pageStoreJanitorSampleSize = 10;
const checkTab = async tabId => {
const tab = await vAPI.tabs.get(tabId);
if ( tab instanceof Object && tab.discarded !== true ) { return; }
µBlock.unbindTabFromPageStats(tabId);
};
const pageStoreJanitor = function() {
const tabIds = Array.from(µBlock.pageStores.keys()).sort();
const checkTab = async tabId => {
const tab = await vAPI.tabs.get(tabId);
if ( tab ) { return; }
µBlock.unbindTabFromPageStats(tabId);
};
if ( pageStoreJanitorSampleAt >= tabIds.length ) {
pageStoreJanitorSampleAt = 0;
}