Skip to content

Commit fca80bc

Browse files
committed
web: Fix extension webpack import not working in Safari 16 (part of ruffle-rs#19348)
1 parent 950f3fe commit fca80bc

File tree

5 files changed

+101
-4
lines changed

5 files changed

+101
-4
lines changed

web/packages/core/src/current-script.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ try {
1010
"src" in document.currentScript &&
1111
document.currentScript.src !== ""
1212
) {
13-
let src = document.currentScript.src;
13+
let src = getScriptOriginalSrc(document.currentScript);
1414

1515
// CDNs allow omitting the filename. If it's omitted, append a slash to
1616
// prevent the last component from being dropped.
@@ -24,3 +24,34 @@ try {
2424
} catch (_e) {
2525
console.warn("Unable to get currentScript URL");
2626
}
27+
28+
/**
29+
*
30+
* Obtain the origin src content according to the running environment of the \<script\> node
31+
*
32+
* @param script \<script\> node instance
33+
*
34+
* @returns \<script\> node origin src
35+
*/
36+
function getScriptOriginalSrc(script: HTMLScriptElement): string {
37+
const scriptUrl = script.src;
38+
const scriptUrlPolyfill = script.getAttribute("ruffle-src-polyfill");
39+
if (!scriptUrlPolyfill) {
40+
return scriptUrl;
41+
}
42+
// Reset webkit mask url should be safe
43+
if ("webkit-masked-url://hidden/" === scriptUrl) {
44+
try {
45+
const currentPolyfillURL = new URL(".", scriptUrlPolyfill);
46+
const isExtensionUrl =
47+
currentPolyfillURL.protocol.includes("extension");
48+
// Only apply to extension
49+
if (isExtensionUrl) {
50+
return scriptUrlPolyfill;
51+
}
52+
} catch (_) {
53+
// Fallback to itself src
54+
}
55+
}
56+
return scriptUrl;
57+
}

web/packages/extension/global.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
declare global {
2+
/**
3+
* https://webpack.js.org/guides/public-path/#on-the-fly
4+
*/
5+
let __webpack_public_path__: string;
6+
}
7+
8+
export {};

web/packages/extension/src/content.ts

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ function injectScriptURL(url: string): Promise<void> {
8080
script.src = url;
8181
// safari 16+ script.src will be masked to "webkit-masked-url://hidden/"
8282
script.setAttribute("ruffle-id", String(ID));
83+
if (url !== script.src) {
84+
script.setAttribute("ruffle-src-polyfill", url);
85+
}
8386
(document.head || document.documentElement).append(script);
8487
return promise;
8588
}

web/packages/extension/src/ruffle.ts

+57-2
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ function handleMessage(message: Message) {
2929

3030
let ID: string | null = null;
3131
if (document.currentScript !== undefined && document.currentScript !== null) {
32-
if ("src" in document.currentScript && document.currentScript.src !== "") {
32+
polyfillCurrentScript();
33+
if (determineKnownTypeHasNoEmptyStringProp(document.currentScript, "src")) {
3334
try {
3435
ID = new URL(document.currentScript.src).searchParams.get("id");
3536
} catch (_) {
3637
// ID remains null.
3738
}
3839
}
3940
if (ID === null) {
40-
// if `script.src` is masked, get id from attrs
41+
// Fallback to get id from attrs
4142
const ruffleId = document.currentScript.getAttribute("ruffle-id");
4243
if (ruffleId) {
4344
ID = ruffleId;
@@ -57,6 +58,60 @@ function openInNewTab(swf: URL): void {
5758
window.postMessage(message, "*");
5859
}
5960

61+
function polyfillCurrentScript(): void {
62+
const script = document.currentScript!;
63+
try {
64+
if (determineKnownTypeHasNoEmptyStringProp(script, "src")) {
65+
determineScriptIsMaskedSrcAndMakePolyfill(script);
66+
}
67+
} catch (_) {
68+
// Continue to run
69+
}
70+
}
71+
72+
function determineScriptIsMaskedSrcAndMakePolyfill(
73+
script: HTMLScriptElement,
74+
): void {
75+
const scriptUrlPolyfill = script.getAttribute("ruffle-src-polyfill");
76+
if (!scriptUrlPolyfill) {
77+
return;
78+
}
79+
const scriptUrl = script.src;
80+
const applyResetWebpackPublicPath = (overridePublicPath: string): void => {
81+
__webpack_public_path__ = overridePublicPath;
82+
// TODO: If there are other scripts that need to be dynamically created and executed, the current polyfill logic should also be applied.
83+
};
84+
// Reset webpack public path should be safe when it is using mask url
85+
if ("webkit-masked-url://hidden/" === scriptUrl) {
86+
const webpackCurrentPublicPath = __webpack_public_path__;
87+
const scriptAutoPublicPath =
88+
getWebpackPublicPathFromScriptSrc(scriptUrl);
89+
if (webpackCurrentPublicPath === scriptAutoPublicPath) {
90+
applyResetWebpackPublicPath(
91+
getWebpackPublicPathFromScriptSrc(scriptUrlPolyfill),
92+
);
93+
}
94+
}
95+
// TODO: Process other situations when mask url not webkit-masked-url://hidden/
96+
}
97+
98+
// Exclude types that do not have this prop
99+
function determineKnownTypeHasNoEmptyStringProp<
100+
T extends object,
101+
TKey extends PropertyKey,
102+
>(obj: T, prop: TKey): obj is T extends Record<TKey, string> ? T : never {
103+
return prop in obj && (obj as Record<PropertyKey, unknown>)[prop] !== "";
104+
}
105+
106+
// Copied from Webpack: https://github.com/webpack/webpack/blob/f1bdec5cc70236083e45b665831d5d79d6485db7/lib/runtime/AutoPublicPathRuntimeModule.js#L75
107+
function getWebpackPublicPathFromScriptSrc(scriptUrl: string): string {
108+
return scriptUrl
109+
.replace(/^blob:/, "")
110+
.replace(/#.*$/, "")
111+
.replace(/\?.*$/, "")
112+
.replace(/\/[^/]+$/, "/");
113+
}
114+
60115
if (ID) {
61116
window.addEventListener("message", (event) => {
62117
// We only accept messages from ourselves.

web/packages/extension/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"target": "es2021",
77
"rootDir": "src",
88
},
9-
"include": ["src/**/*"],
9+
"include": ["src/**/*", "global.d.ts"],
1010
"references": [{ "path": "../core" }],
1111
}

0 commit comments

Comments
 (0)