mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 00:25:46 -06:00
Sandbox: Patch history.replaceState to make it work inside the sandbox (#76255)
* Patch history.replaceState api instead of doing a live distortion * Add better patching mechanism * Remove console log
This commit is contained in:
parent
63c7a0e8ca
commit
7107ba0104
@ -528,24 +528,5 @@ async function distortPostMessage(distortions: DistortionMap) {
|
||||
*/
|
||||
export function distortLiveApis(originalValue: ProxyTarget): ProxyTarget | undefined {
|
||||
distortMonacoEditor(generalDistortionMap);
|
||||
|
||||
// This distorts the `history.replace` function in react-router-dom.
|
||||
// constructed for each browser history and is only accessible within the react context.
|
||||
// Note that this distortion does not affect `String.prototype.replace` calls.
|
||||
// because they don't go through distortions
|
||||
if (
|
||||
originalValue instanceof Function &&
|
||||
originalValue.name === 'replace' &&
|
||||
originalValue.prototype.constructor.length === 2
|
||||
) {
|
||||
return function replace(this: unknown, ...args: unknown[]) {
|
||||
// validate history.replace signature further
|
||||
if (args && args[0] && typeof args[0] === 'string' && args[1] && !(args[1] instanceof Function)) {
|
||||
const newArgs = cloneDeep(args);
|
||||
return Reflect.apply(originalValue, this, newArgs);
|
||||
}
|
||||
return Reflect.apply(originalValue, this, args);
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { isNearMembraneProxy, ProxyTarget } from '@locker/near-membrane-shared';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import Prism from 'prismjs';
|
||||
|
||||
import { DataSourceApi } from '@grafana/data';
|
||||
@ -160,3 +161,50 @@ export function getSandboxMockBody(): Element {
|
||||
}
|
||||
return sandboxBody;
|
||||
}
|
||||
|
||||
let nativeAPIsPatched = false;
|
||||
|
||||
export function patchWebAPIs() {
|
||||
if (!nativeAPIsPatched) {
|
||||
nativeAPIsPatched = true;
|
||||
patchHistoryReplaceState();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* window.history.replaceState is a native API that won't work with proxies
|
||||
* so we need to patch it to unwrap any possible proxies you pass to it.
|
||||
*
|
||||
* Why can't we directly distord window.history.replaceState calls inside plugins?
|
||||
*
|
||||
* We can. Except that plugins don't call window.history.replaceState directly they
|
||||
* instead use the history object from react-router.
|
||||
*
|
||||
* react-router is a runtime dependency and it is executed in the blue realm
|
||||
* and calls window.history.replaceState directly where the sandbox is not involved at all
|
||||
*
|
||||
* It is most likely this "original" function is not really the native function because
|
||||
* `useLocation` from `react-use` patches this function before the sandbox kicks in.
|
||||
*
|
||||
* Regarding the performance impact of this cloneDeep. The structures passed to history.replaceState
|
||||
* are minimalistic and its impact will be neglegible.
|
||||
*/
|
||||
function patchHistoryReplaceState() {
|
||||
const original = window.history.replaceState;
|
||||
Object.defineProperty(window.history, 'replaceState', {
|
||||
value: function (...args: Parameters<typeof window.history.replaceState>) {
|
||||
let newArgs = args;
|
||||
try {
|
||||
newArgs = cloneDeep(args);
|
||||
} catch (e) {
|
||||
logWarning('Error cloning args in window.history.replaceState', {
|
||||
error: String(e),
|
||||
});
|
||||
}
|
||||
return Reflect.apply(original, this, newArgs);
|
||||
},
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
});
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
isLiveTarget,
|
||||
markDomElementStyleAsALiveTarget,
|
||||
patchObjectAsLiveTarget,
|
||||
patchWebAPIs,
|
||||
} from './document_sandbox';
|
||||
import { sandboxPluginDependencies } from './plugin_dependencies';
|
||||
import { sandboxPluginComponents } from './sandbox_components';
|
||||
@ -30,6 +31,7 @@ const pluginImportCache = new Map<string, Promise<System.Module>>();
|
||||
const pluginLogCache: Record<string, boolean> = {};
|
||||
|
||||
export async function importPluginModuleInSandbox({ pluginId }: { pluginId: string }): Promise<System.Module> {
|
||||
patchWebAPIs();
|
||||
try {
|
||||
const pluginMeta = await getPluginSettings(pluginId);
|
||||
if (!pluginImportCache.has(pluginId)) {
|
||||
|
Loading…
Reference in New Issue
Block a user