I've recently noticed a strange behaviour in Safari when injecting a content script with scripting.executeScript
.
The expectation here is that an array of InjectionResult
objects will be returned (one for each frame the script was executed in). In all other browsers our extension supports (Chrome, Firefox*, Edge, Brave and Opera) this is indeed the behaviour I've observed.
Safari however, at least on both 15 and 16 that I've been able to test, returns an array containing a null
element. I have confirmed that the scripts execute as expected without error, but even if they did error the expectation would be to have the error
field of an InjectionResult
object populated.
Below are two simple scripts I used to confirm this across all of the mentioned browsers. I made sure to test using both a script expecting arguments and one without, just in case that was somehow the cause. I also tried both a void
return and returning a primitive value from the scripts.
Manifest Version 3
const foo = await browser.scripting.executeScript({
target: { tabId },
func: () => {
console.log('Hello, from an injected script! o/');
},
});
console.log({ foo });
const bar = await browser.scripting.executeScript({
target: { tabId },
args: [1337],
func: (param: number) => {
console.log(`Hello, from an injected script! o/ With '${param}' argument!`);
return param;
},
});
console.log({ bar });
*Manifest Version 2 (for Firefox)
const foo = await browser.tabs.executeScript(tabId, {
code: 'console.log("Hello, from an injected script! o/")',
});
console.log({ foo });
const param = 1337;
const bar = await browser.tabs.executeScript(tabId, {
code: `console.log('Hello, from an injected script! o/ With "${param}" argument!'); ${param};`,
});
console.log({ bar });
The outputs for the console.log
statements above are as follows :-
Chrome | Brave | Edge | Opera
foo: [{documentId: '...', frameId: 0, result: null}]
bar: [{documentId: '...', frameId: 0, result: 1337}]
*Firefox
foo: [undefined]
bar: [1337]
Safari
foo: [null]
bar: [null]
Since Safari's output more closely resembles that of Firefox under MV2, it feels like Safari supports MV3 but is still kind of stuck in this weird limbo between MV2.
Desperate attempt snippet
The observation of the above MV3/MV2 limbo led me to try drop the return
statement, and instead just have param;
as the last evaluated statement. In line with how returns work under MV2 in Firefox as follows:-
func: (param: number) => {
console.log(`Hello, from an injected script! o/ With '${param}' argument!`);
param;
},
This, thank goodness, didn't work.
This behaviour feels like it should almost surely be a bug in WebKit, or am I completely missing something?
It looks like this was resolved, albeit undocumented, in a Safari 16.4.x release (either 16.4.0 or 16.4.1).
So to confirm, this now works across both
ISOLATED
andMAIN
scripts:Resulting in the following now:
Chrome | Brave | Edge | Opera
Safari
The shape of the return is still unaligned with the other browsers, but I'll take that over
[null]
any day.