1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-04-18 19:42:23 +02:00

Compare commits

...

3 Commits

Author SHA1 Message Date
Laurent Cozic 04389e6c87 Keep the index.js validation on mobile. 2026-04-14 16:13:57 +01:00
Laurent Cozic 20b5e02802 Fixed loading 2026-04-14 12:55:06 +01:00
Laurent Cozic 204b653422 update 2026-04-14 11:02:54 +01:00
4 changed files with 34 additions and 4 deletions
@@ -46,12 +46,18 @@ export default class PluginRunner extends BasePluginRunner {
return false;
});
// On native mobile, pass a file path so the WebView can load the
// script directly from the filesystem (avoids transferring the full
// script text across the React Native bridge). On web, file:// URLs
// are blocked by CSP so we pass the script text directly.
const scriptFilePath = plugin.scriptText ? '' : `${plugin.baseDir}/index.js`;
this.webviewRef.current.injectJS(`
pluginBackgroundPage.runPlugin(
${JSON.stringify(shim.injectedJs('pluginBackgroundPage'))},
${JSON.stringify(plugin.scriptText)},
${JSON.stringify(scriptFilePath)},
${JSON.stringify(messageChannelId)},
${JSON.stringify(plugin.id)},
${JSON.stringify(plugin.scriptText)},
);
`);
@@ -186,6 +186,7 @@ const PluginRunnerWebViewComponent: React.FC<Props> = props => {
html={html}
injectedJavaScript={injectedJs}
hasPluginScripts={true}
allowFileAccessFromJs={true}
onMessage={pluginRunner.onWebviewMessage}
onLoadEnd={onLoadEnd}
onLoadStart={onLoadStart}
@@ -26,14 +26,29 @@ export const stopPlugin = async (pluginId: string) => {
delete loadedPlugins[pluginId];
};
export const runPlugin = (
pluginBackgroundScript: string, pluginScript: string, messageChannelId: string, pluginId: string,
export const runPlugin = async (
pluginBackgroundScript: string, scriptFilePath: string, messageChannelId: string, pluginId: string, scriptText = '',
) => {
if (loadedPlugins[pluginId]) {
console.warn(`Plugin already running ${pluginId}`);
return;
}
// When scriptText is provided (web), use it directly. Otherwise load
// the plugin script from the filesystem (native mobile). We use
// XMLHttpRequest because fetch() doesn't support file:// URLs on
// Android WebView.
let pluginScript = scriptText;
if (!pluginScript) {
pluginScript = await new Promise<string>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `file://${scriptFilePath}`, true);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(new Error(`Failed to load plugin script: ${scriptFilePath}`));
xhr.send();
});
}
const bodyHtml = '';
const initialJavaScript = `
"use strict";
@@ -350,8 +350,16 @@ export default class PluginService extends BaseService {
logger.info(`Loading plugin from ${path}`);
const scriptText = await fsDriver.readFile(`${distPath}/index.js`);
const manifestText = await fsDriver.readFile(`${distPath}/manifest.json`);
// On mobile, plugin scripts are loaded directly by the WebView
// from the filesystem, so we don't need to read them here.
const indexPath = `${distPath}/index.js`;
if (shim.mobilePlatform()) {
if (!(await fsDriver.exists(indexPath))) {
throw new Error(`Plugin bundle not found at: ${indexPath}`);
}
}
const scriptText = shim.mobilePlatform() ? '' : await fsDriver.readFile(indexPath);
const pluginId = makePluginId(filename(path));
return this.loadPlugin(distPath, manifestText, scriptText, pluginId);