@@ -219,6 +219,12 @@ class GMAPIRegistry {
219219 window . GM . addStyle = addStyle ;
220220 }
221221
222+ if ( enabledApis . gmAddElement ) {
223+ const addElement = ( parent , tag , attributes = { } ) => this . addElementToDocument ( parent , tag , attributes ) ;
224+ window . GM_addElement = addElement ;
225+ window . GM . addElement = addElement ;
226+ }
227+
222228 // Always provide Trusted Types helpers for user scripts
223229 this . setupTrustedTypesHelpers ( ) ;
224230 }
@@ -430,6 +436,32 @@ class GMAPIRegistry {
430436 return style ;
431437 }
432438
439+ addElementToDocument ( parent , tag , attributes = { } ) {
440+ if ( ! parent || typeof tag !== "string" ) {
441+ console . warn ( "GM_addElement: parent must be a valid DOM node and tag must be a string" ) ;
442+ return null ;
443+ }
444+
445+ try {
446+ const element = document . createElement ( tag ) ;
447+
448+ // Set attributes if provided
449+ if ( attributes && typeof attributes === "object" ) {
450+ Object . entries ( attributes ) . forEach ( ( [ key , value ] ) => {
451+ if ( typeof key === "string" && value != null ) {
452+ element . setAttribute ( key , String ( value ) ) ;
453+ }
454+ } ) ;
455+ }
456+
457+ parent . appendChild ( element ) ;
458+ return element ;
459+ } catch ( error ) {
460+ console . error ( "GM_addElement: Failed to create or append element:" , error ) ;
461+ return null ;
462+ }
463+ }
464+
433465 registerMenuCommand ( caption , onClick , accessKey ) {
434466 if ( typeof caption !== "string" || typeof onClick !== "function" ) {
435467 console . warn (
@@ -595,6 +627,32 @@ function createMainWorldExecutor(
595627 valueManager . initializeCache ( initialValues ) ;
596628 apiRegistry . registerAll ( enabledApis ) ;
597629
630+ // Ensure native functions are bound to the correct receiver to prevent Illegal invocation
631+ try {
632+ window . setTimeout = window . setTimeout . bind ( window ) ;
633+ window . clearTimeout = window . clearTimeout . bind ( window ) ;
634+ window . setInterval = window . setInterval . bind ( window ) ;
635+ window . clearInterval = window . clearInterval . bind ( window ) ;
636+ if ( typeof window . postMessage === 'function' ) {
637+ window . postMessage = window . postMessage . bind ( window ) ;
638+ }
639+ } catch ( e ) {
640+ console . warn ( 'CodeTweak: Failed to bind timers/postMessage:' , e ) ;
641+ }
642+
643+ // Debug: log enabled APIs for this execution
644+ try {
645+ const enabled = Object . entries ( enabledApis )
646+ . filter ( ( [ , v ] ) => ! ! v )
647+ . map ( ( [ k ] ) => k )
648+ . sort ( ) ;
649+ console . log ( `CodeTweak: Enabled GM APIs for script ${ scriptId } (MAIN):` , enabled ) ;
650+ // Also dump full object for completeness
651+ console . debug ( 'CodeTweak: enabledApis (MAIN) raw:' , enabledApis ) ;
652+ } catch ( e ) {
653+ console . warn ( 'CodeTweak: Failed to log enabled APIs (MAIN):' , e ) ;
654+ }
655+
598656 // Expose GM_info (read-only)
599657 try {
600658 Object . defineProperty ( window , "GM_info" , {
@@ -681,6 +739,32 @@ function createIsolatedWorldExecutor(
681739 valueManager . initializeCache ( initialValues ) ;
682740 apiRegistry . registerAll ( enabledApis ) ;
683741
742+ // Ensure native functions are bound to the correct receiver to prevent Illegal invocation
743+ try {
744+ window . setTimeout = window . setTimeout . bind ( window ) ;
745+ window . clearTimeout = window . clearTimeout . bind ( window ) ;
746+ window . setInterval = window . setInterval . bind ( window ) ;
747+ window . clearInterval = window . clearInterval . bind ( window ) ;
748+ if ( typeof window . postMessage === 'function' ) {
749+ window . postMessage = window . postMessage . bind ( window ) ;
750+ }
751+ } catch ( e ) {
752+ console . warn ( 'CodeTweak: Failed to bind timers/postMessage (isolated):' , e ) ;
753+ }
754+
755+ // Debug: log enabled APIs for this execution (ISOLATED)
756+ try {
757+ const enabled = Object . entries ( enabledApis )
758+ . filter ( ( [ , v ] ) => ! ! v )
759+ . map ( ( [ k ] ) => k )
760+ . sort ( ) ;
761+ console . log ( `CodeTweak: Enabled GM APIs for script ${ scriptId } (ISOLATED):` , enabled ) ;
762+ // Also dump full object for completeness
763+ console . debug ( 'CodeTweak: enabledApis (ISOLATED) raw:' , enabledApis ) ;
764+ } catch ( e ) {
765+ console . warn ( 'CodeTweak: Failed to log enabled APIs (ISOLATED):' , e ) ;
766+ }
767+
684768 // Expose GM_info (read-only)
685769 try {
686770 Object . defineProperty ( window , "GM_info" , {
@@ -858,10 +942,23 @@ class ScriptInjector {
858942 gmGetResourceURL : Boolean ( script . gmGetResourceURL ) ,
859943 gmSetClipboard : Boolean ( script . gmSetClipboard ) ,
860944 gmAddStyle : Boolean ( script . gmAddStyle ) ,
945+ gmAddElement : Boolean ( script . gmAddElement ) ,
861946 gmRegisterMenuCommand : Boolean ( script . gmRegisterMenuCommand ) ,
862947 gmXmlhttpRequest : Boolean ( script . gmXmlhttpRequest ) ,
863948 } ;
864949
950+ // Debug: log enabled APIs at config time (background/service context)
951+ try {
952+ const enabledList = Object . entries ( enabledApis )
953+ . filter ( ( [ , v ] ) => ! ! v )
954+ . map ( ( [ k ] ) => k )
955+ . sort ( ) ;
956+ console . log ( 'CodeTweak: prepareScriptConfig enabled APIs for' , scriptId , enabledList ) ;
957+ console . debug ( 'CodeTweak: prepareScriptConfig enabledApis raw:' , enabledApis ) ;
958+ } catch ( e ) {
959+ console . warn ( 'CodeTweak: Failed to log enabled APIs in prepareScriptConfig:' , e ) ;
960+ }
961+
865962 const resourceManager = ResourceManager . fromScript ( script ) ;
866963
867964 return {
0 commit comments