From e658255c36d18fab152e7755778e2d99474a29c7 Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 15 Mar 2017 15:41:39 +0300 Subject: [PATCH] Reinject content scripts on install/update/enabling the extension The injection code also runs outside of onInstalled event so we check first if a content script belonging to our execution context "generation" is already injected. This can happen on browser startup: the background page is loaded in several seconds after the normal web page tabs are loaded with our content script(s) already injected. The check itself is simply a "ping" message to each tab that should return true if the content script is alive and kicking. --- apply.js | 3 +++ background.js | 40 ++++++++++++++++++++++++++++++++++++++++ edit.js | 5 +---- messaging.js | 9 +++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/apply.js b/apply.js index eae0a738..f174f71f 100644 --- a/apply.js +++ b/apply.js @@ -52,6 +52,9 @@ function applyOnMessage(request, sender, sendResponse) { case "styleDisableAll": disableAll(request.disableAll); break; + case "ping": + sendResponse(true); + break; } } diff --git a/background.js b/background.js index 14e57d53..0d1e7654 100644 --- a/background.js +++ b/background.js @@ -1,3 +1,5 @@ +/* globals wildcardAsRegExp, KEEP_CHANNEL_OPEN */ + var frameIdMessageable; runTryCatch(function() { chrome.tabs.sendMessage(0, {}, {frameId: 0}, function() { @@ -202,3 +204,41 @@ chrome.storage.local.get('version', prefs => { } } }); + +// after the extension was enabled or just installed +injectContentScripts({reason: 'update'}); +// after an actual update +chrome.runtime.onInstalled.addListener(injectContentScripts); + +function injectContentScripts({reason, previousVersion, id, checkFirst} = {}) { + // reason: install, update, chrome_update, shared_module_update + // the "install" case is ignored because it was already handled by explicit invocation of this function + if (!/update/.test(reason)) { + return; + } + const contentScripts = chrome.app.getDetails().content_scripts; + for (let cs of contentScripts) { + cs.matches = cs.matches.map(m => m == '' ? m : wildcardAsRegExp(m)); + } + chrome.tabs.query({url: '*://*/*'}, tabs => { + for (let tab of tabs) { + for (let cs of contentScripts) { + for (let m of cs.matches) { + if (m == '' || tab.url.match(m)) { + chrome.tabs.sendMessage(tab.id, {method: 'ping'}, pong => { + if (!pong) { + chrome.tabs.executeScript(tab.id, { + file: cs.js[0], + runAt: cs.run_at, + allFrames: cs.all_frames, + }, result => chrome.runtime.lastError); // ignore lastError just in case + } + }); + // inject the content script just once + break; + } + } + } + } + }); +} diff --git a/edit.js b/edit.js index 4b6e9d27..4b439e07 100644 --- a/edit.js +++ b/edit.js @@ -1,3 +1,4 @@ +/* globals stringAsRegExp */ "use strict"; var styleId = null; @@ -1643,10 +1644,6 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { } }); -function stringAsRegExp(s, flags) { - return new RegExp(s.replace(/[{}()\[\]\/\\.+?^$:=*!|]/g, "\\$&"), flags); -} - function getComputedHeight(el) { var compStyle = getComputedStyle(el); return el.getBoundingClientRect().height + diff --git a/messaging.js b/messaging.js index 5cd1e99e..6367b413 100644 --- a/messaging.js +++ b/messaging.js @@ -123,3 +123,12 @@ function getTabRealURL(tab, callback) { }); } } + +function stringAsRegExp(s, flags) { + return new RegExp(s.replace(/[{}()\[\]\/\\.+?^$:=*!|]/g, "\\$&"), flags); +} + +// expands * as .*? +function wildcardAsRegExp(s, flags) { + return new RegExp(s.replace(/[{}()\[\]\/\\.+?^$:=!|]/g, "\\$&").replace(/\*/g, '.*?'), flags); +}