Skip to content

Commit e1500ee

Browse files
committed
Add ability to defer set-constant execution
A new optional parameter has been added to `set-constant` scriptlet: `runAt`, default to `0`. ..##+js(set, document.body.oncontextmenu, null, 2) When the `runAt` parameter is present, uBO will take it into account to possibly defer execution of `set-constant`: - `runAt` not present: execute immediately - `runAt` = 1: execute immediately - `runAt` = 2: execute when document state is "interactive" - `runAt` = 3: execute when document state is `"complete"
1 parent 8083e06 commit e1500ee

File tree

1 file changed

+160
-137
lines changed

1 file changed

+160
-137
lines changed

assets/resources/scriptlets.js

Lines changed: 160 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -982,162 +982,185 @@ builtinScriptlets.push({
982982
fn: setConstant,
983983
});
984984
function setConstant(
985-
chain = '',
986-
cValue = ''
985+
arg1 = '',
986+
arg2 = '',
987+
arg3 = 0
987988
) {
989+
const details = typeof arg1 !== 'object'
990+
? { prop: arg1, value: arg2, runAt: parseInt(arg3, 10) || 0 }
991+
: arg1;
992+
const { prop: chain = '', value: cValue = '' } = details;
988993
if ( typeof chain !== 'string' ) { return; }
989994
if ( chain === '' ) { return; }
990-
const trappedProp = (( ) => {
991-
const pos = chain.lastIndexOf('.');
992-
if ( pos === -1 ) { return chain; }
993-
return chain.slice(pos+1);
994-
})();
995-
if ( trappedProp === '' ) { return; }
996-
const thisScript = document.currentScript;
997-
const objectDefineProperty = Object.defineProperty.bind(Object);
998-
const cloakFunc = fn => {
999-
objectDefineProperty(fn, 'name', { value: trappedProp });
1000-
const proxy = new Proxy(fn, {
1001-
defineProperty(target, prop) {
1002-
if ( prop !== 'toString' ) {
1003-
return Reflect.deleteProperty(...arguments);
1004-
}
1005-
return true;
1006-
},
1007-
deleteProperty(target, prop) {
1008-
if ( prop !== 'toString' ) {
1009-
return Reflect.deleteProperty(...arguments);
1010-
}
1011-
return true;
1012-
},
1013-
get(target, prop) {
1014-
if ( prop === 'toString' ) {
1015-
return function() {
1016-
return `function ${trappedProp}() { [native code] }`;
1017-
}.bind(null);
1018-
}
1019-
return Reflect.get(...arguments);
1020-
},
1021-
});
1022-
return proxy;
1023-
};
1024-
if ( cValue === 'undefined' ) {
1025-
cValue = undefined;
1026-
} else if ( cValue === 'false' ) {
1027-
cValue = false;
1028-
} else if ( cValue === 'true' ) {
1029-
cValue = true;
1030-
} else if ( cValue === 'null' ) {
1031-
cValue = null;
1032-
} else if ( cValue === "''" ) {
1033-
cValue = '';
1034-
} else if ( cValue === '[]' ) {
1035-
cValue = [];
1036-
} else if ( cValue === '{}' ) {
1037-
cValue = {};
1038-
} else if ( cValue === 'noopFunc' ) {
1039-
cValue = cloakFunc(function(){});
1040-
} else if ( cValue === 'trueFunc' ) {
1041-
cValue = cloakFunc(function(){ return true; });
1042-
} else if ( cValue === 'falseFunc' ) {
1043-
cValue = cloakFunc(function(){ return false; });
1044-
} else if ( /^\d+$/.test(cValue) ) {
1045-
cValue = parseFloat(cValue);
1046-
if ( isNaN(cValue) ) { return; }
1047-
if ( Math.abs(cValue) > 0x7FFF ) { return; }
1048-
} else {
1049-
return;
1050-
}
1051-
let aborted = false;
1052-
const mustAbort = function(v) {
1053-
if ( aborted ) { return true; }
1054-
aborted =
1055-
(v !== undefined && v !== null) &&
1056-
(cValue !== undefined && cValue !== null) &&
1057-
(typeof v !== typeof cValue);
1058-
return aborted;
1059-
};
1060-
// https://github.com/uBlockOrigin/uBlock-issues/issues/156
1061-
// Support multiple trappers for the same property.
1062-
const trapProp = function(owner, prop, configurable, handler) {
1063-
if ( handler.init(owner[prop]) === false ) { return; }
1064-
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
1065-
let prevGetter, prevSetter;
1066-
if ( odesc instanceof Object ) {
1067-
owner[prop] = cValue;
1068-
if ( odesc.get instanceof Function ) {
1069-
prevGetter = odesc.get;
1070-
}
1071-
if ( odesc.set instanceof Function ) {
1072-
prevSetter = odesc.set;
1073-
}
1074-
}
1075-
try {
1076-
objectDefineProperty(owner, prop, {
1077-
configurable,
1078-
get() {
1079-
if ( prevGetter !== undefined ) {
1080-
prevGetter();
995+
function setConstant(chain, cValue) {
996+
const trappedProp = (( ) => {
997+
const pos = chain.lastIndexOf('.');
998+
if ( pos === -1 ) { return chain; }
999+
return chain.slice(pos+1);
1000+
})();
1001+
if ( trappedProp === '' ) { return; }
1002+
const thisScript = document.currentScript;
1003+
const objectDefineProperty = Object.defineProperty.bind(Object);
1004+
const cloakFunc = fn => {
1005+
objectDefineProperty(fn, 'name', { value: trappedProp });
1006+
const proxy = new Proxy(fn, {
1007+
defineProperty(target, prop) {
1008+
if ( prop !== 'toString' ) {
1009+
return Reflect.deleteProperty(...arguments);
10811010
}
1082-
return handler.getter(); // cValue
1011+
return true;
10831012
},
1084-
set(a) {
1085-
if ( prevSetter !== undefined ) {
1086-
prevSetter(a);
1013+
deleteProperty(target, prop) {
1014+
if ( prop !== 'toString' ) {
1015+
return Reflect.deleteProperty(...arguments);
10871016
}
1088-
handler.setter(a);
1089-
}
1017+
return true;
1018+
},
1019+
get(target, prop) {
1020+
if ( prop === 'toString' ) {
1021+
return function() {
1022+
return `function ${trappedProp}() { [native code] }`;
1023+
}.bind(null);
1024+
}
1025+
return Reflect.get(...arguments);
1026+
},
10901027
});
1091-
} catch(ex) {
1028+
return proxy;
1029+
};
1030+
if ( cValue === 'undefined' ) {
1031+
cValue = undefined;
1032+
} else if ( cValue === 'false' ) {
1033+
cValue = false;
1034+
} else if ( cValue === 'true' ) {
1035+
cValue = true;
1036+
} else if ( cValue === 'null' ) {
1037+
cValue = null;
1038+
} else if ( cValue === "''" ) {
1039+
cValue = '';
1040+
} else if ( cValue === '[]' ) {
1041+
cValue = [];
1042+
} else if ( cValue === '{}' ) {
1043+
cValue = {};
1044+
} else if ( cValue === 'noopFunc' ) {
1045+
cValue = cloakFunc(function(){});
1046+
} else if ( cValue === 'trueFunc' ) {
1047+
cValue = cloakFunc(function(){ return true; });
1048+
} else if ( cValue === 'falseFunc' ) {
1049+
cValue = cloakFunc(function(){ return false; });
1050+
} else if ( /^\d+$/.test(cValue) ) {
1051+
cValue = parseFloat(cValue);
1052+
if ( isNaN(cValue) ) { return; }
1053+
if ( Math.abs(cValue) > 0x7FFF ) { return; }
1054+
} else {
1055+
return;
10921056
}
1093-
};
1094-
const trapChain = function(owner, chain) {
1095-
const pos = chain.indexOf('.');
1096-
if ( pos === -1 ) {
1097-
trapProp(owner, chain, false, {
1057+
let aborted = false;
1058+
const mustAbort = function(v) {
1059+
if ( aborted ) { return true; }
1060+
aborted =
1061+
(v !== undefined && v !== null) &&
1062+
(cValue !== undefined && cValue !== null) &&
1063+
(typeof v !== typeof cValue);
1064+
return aborted;
1065+
};
1066+
// https://github.com/uBlockOrigin/uBlock-issues/issues/156
1067+
// Support multiple trappers for the same property.
1068+
const trapProp = function(owner, prop, configurable, handler) {
1069+
if ( handler.init(configurable ? owner[prop] : cValue) === false ) { return; }
1070+
const odesc = Object.getOwnPropertyDescriptor(owner, prop);
1071+
let prevGetter, prevSetter;
1072+
if ( odesc instanceof Object ) {
1073+
owner[prop] = cValue;
1074+
if ( odesc.get instanceof Function ) {
1075+
prevGetter = odesc.get;
1076+
}
1077+
if ( odesc.set instanceof Function ) {
1078+
prevSetter = odesc.set;
1079+
}
1080+
}
1081+
try {
1082+
objectDefineProperty(owner, prop, {
1083+
configurable,
1084+
get() {
1085+
if ( prevGetter !== undefined ) {
1086+
prevGetter();
1087+
}
1088+
return handler.getter(); // cValue
1089+
},
1090+
set(a) {
1091+
if ( prevSetter !== undefined ) {
1092+
prevSetter(a);
1093+
}
1094+
handler.setter(a);
1095+
}
1096+
});
1097+
} catch(ex) {
1098+
}
1099+
};
1100+
const trapChain = function(owner, chain) {
1101+
const pos = chain.indexOf('.');
1102+
if ( pos === -1 ) {
1103+
trapProp(owner, chain, false, {
1104+
v: undefined,
1105+
init: function(v) {
1106+
if ( mustAbort(v) ) { return false; }
1107+
this.v = v;
1108+
return true;
1109+
},
1110+
getter: function() {
1111+
return document.currentScript === thisScript
1112+
? this.v
1113+
: cValue;
1114+
},
1115+
setter: function(a) {
1116+
if ( mustAbort(a) === false ) { return; }
1117+
cValue = a;
1118+
}
1119+
});
1120+
return;
1121+
}
1122+
const prop = chain.slice(0, pos);
1123+
const v = owner[prop];
1124+
chain = chain.slice(pos + 1);
1125+
if ( v instanceof Object || typeof v === 'object' && v !== null ) {
1126+
trapChain(v, chain);
1127+
return;
1128+
}
1129+
trapProp(owner, prop, true, {
10981130
v: undefined,
10991131
init: function(v) {
1100-
if ( mustAbort(v) ) { return false; }
11011132
this.v = v;
11021133
return true;
11031134
},
11041135
getter: function() {
1105-
return document.currentScript === thisScript
1106-
? this.v
1107-
: cValue;
1136+
return this.v;
11081137
},
11091138
setter: function(a) {
1110-
if ( mustAbort(a) === false ) { return; }
1111-
cValue = a;
1139+
this.v = a;
1140+
if ( a instanceof Object ) {
1141+
trapChain(a, chain);
1142+
}
11121143
}
11131144
});
1114-
return;
1115-
}
1116-
const prop = chain.slice(0, pos);
1117-
const v = owner[prop];
1118-
chain = chain.slice(pos + 1);
1119-
if ( v instanceof Object || typeof v === 'object' && v !== null ) {
1120-
trapChain(v, chain);
1121-
return;
1122-
}
1123-
trapProp(owner, prop, true, {
1124-
v: undefined,
1125-
init: function(v) {
1126-
this.v = v;
1127-
return true;
1128-
},
1129-
getter: function() {
1130-
return this.v;
1131-
},
1132-
setter: function(a) {
1133-
this.v = a;
1134-
if ( a instanceof Object ) {
1135-
trapChain(a, chain);
1136-
}
1137-
}
1138-
});
1145+
};
1146+
trapChain(window, chain);
1147+
}
1148+
const runAt = details.runAt;
1149+
if ( runAt === 0 ) {
1150+
setConstant(chain, cValue); return;
1151+
}
1152+
const docReadyState = ( ) => {
1153+
return ({ loading: 1, interactive: 2, complete: 3, })[document.readyState] || 0;
1154+
};
1155+
if ( docReadyState() >= runAt ) {
1156+
setConstant(chain, cValue); return;
1157+
}
1158+
const onReadyStateChange = ( ) => {
1159+
if ( docReadyState() < runAt ) { return; }
1160+
setConstant(chain, cValue);
1161+
document.removeEventListener('readystatechange', onReadyStateChange);
11391162
};
1140-
trapChain(window, chain);
1163+
document.addEventListener('readystatechange', onReadyStateChange);
11411164
}
11421165

11431166
/******************************************************************************/

0 commit comments

Comments
 (0)