Hi users and developers,
It’s glad that I’ve figured out the root cause of this issue, and has a (maybe not perfect) solution to that.
I’d like to share it with you in case you’re bothering by this weird issue.
Phenomenon
executeScript scripts will be executed twice(or more) each time (I’ve seen 4 times on a speedy ubuntu server);
Root cause: in source code level, this is caused by duplicated injection of “inject.js” into the main page document.
Details of issue:
Injection is done in the function “insertScript” with dynamically created node and insertion into main page document.
And to indicate the injection is finished & ready, there’s a flag in the document which will be set through postMessage to the injected “inject.js”, as following code shows:
(code snippet from “content_script.js”)
function untilInjected() {
…
const injected = document.body.getAttribute(‘data-injected’);
// by XJP: bugfix v2(maybe final version^_^) for duplicate injection
if (injected) {
return Promise.resolve(api);
utils_1.insertScript(web_extension_1.default.runtime.getURL(‘inject.js’));
return ts_utils_1.retry(() => {
log_1.default('sending INJECT_READY');
return cs_postmessage_1.postMessage(window, window, { cmd: '**INJECT_READY**' }, '*', 500);
}
...
(code snippet from “inject.js”)
(0, _cs_postmessage.onMessage)(window, function (_ref) {
var cmd = _ref.cmd,
args = _ref.args;
switch (cmd) {
case ‘INJECT_READY’:
{
document.body.setAttribute(‘data-injected’, ‘done’);
return true;
}
…
This is an elegant design.
But the result is not ideal.
Because of **async execution fact** of javascript.
In a word, this “postMessage” and set-flag action will not block the execution of the whole script.
Thus after the injection action started and before the flag is set(usually this “gap” is several milliseconds or even 1 second, depending on the speed of the computer), the injection will be triggered multiple times, because of multiple times window/frame initiation and updates.
Bug Fix
Introduce a new transient state of “injecting” showing that the injection is started but not succeeded, thus prevent duplicate injection, and will not affect the normal processes waiting for real injection ready.
(all bugfix codes are in “content_script.js”)
var insertScript = exports.insertScript = function insert Script(file) {
// by XJP: set “doing” flag at the beginning could stop any second request for injection.
document.body.setAttribute(‘data-injected’, ‘injecting’);
var s = document.constructor.prototype.createElement.call(document, ‘script’);
s.setAttribute(‘type’, ‘text/javascript’);
s.setAttribute(‘src’, file);
document.documentElement.appendChild(s);
s.parentNode.removeChild(s);
};
function untilInjected() {
…
const injected = document.body.getAttribute(‘data-injected’);
// by XJP: bugfix v2(maybe final version^_^) for duplicate injection
if (injected && injected == “done”) {
return Promise.resolve(api);
}else if (injected != “injecting”){ // by XJP: otherwise if injection has started, DO NOT inject AGAIN.
utils_1.insertScript(web_extension_1.default.runtime.getURL(‘inject.js’));
}
return ts_utils_1.retry(() => {
…
Two functions modified: set “injecting” flag in insertScript(), check this flag in untilInjected().