Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions media/annotationPanel/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,17 @@ <h3 id="bboxTitle">Bounding Box</h3>
(() => {
const nativeAcquire = window.acquireVsCodeApi;
let sharedApi;
window.acquireVsCodeApi = () => sharedApi || (sharedApi = nativeAcquire());
window.acquireVsCodeApi = () => {
if (!sharedApi) {
const nativeApi = nativeAcquire();
sharedApi = {
postMessage: (...args) => nativeApi.postMessage(...args),
getState: (...args) => nativeApi.getState(...args),
setState: (...args) => nativeApi.setState(...args),
};
}
return sharedApi;
};
})();
</script>
<script nonce="__CSP_NONCE__">
Expand Down Expand Up @@ -145,4 +155,4 @@ <h3 id="bboxTitle">Bounding Box</h3>
<script src="__CONFLICT_SCRIPT_URI__"></script>
<script src="__APP_SCRIPT_URI__"></script>
</body>
</html>
</html>
34 changes: 34 additions & 0 deletions scripts/test_annotation_mode_cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ const root = path.join(__dirname, '..');
const indexPath = path.join(root, 'media', 'annotationPanel', 'index.html');
const indexHtml = fs.readFileSync(indexPath, 'utf8');
const inlineScripts = [...indexHtml.matchAll(/<script nonce="__CSP_NONCE__">([\s\S]*?)<\/script>/g)];
const apiShimScript = inlineScripts.find(match => match[1].includes('const nativeAcquire = window.acquireVsCodeApi'))?.[1];
const cycleScript = inlineScripts.find(match => match[1].includes('MODE_BUTTON_IDS'))?.[1];
assert(apiShimScript, 'annotation panel should keep the shared VS Code API shim embedded in the HTML resource');
assert(cycleScript, 'annotation mode cycle script should remain embedded in the annotation panel resource');

const dom = new JSDOM(`<!doctype html><body>
Expand Down Expand Up @@ -100,4 +102,36 @@ assert(!Object.prototype.hasOwnProperty.call(defaults, 'modeTemplate'));
assert(!Object.prototype.hasOwnProperty.call(defaults, 'modeRect'));
assert(!Object.prototype.hasOwnProperty.call(defaults, 'modePoint'));

const syncSent = [];
const syncDom = new JSDOM(`<!doctype html><body>
<canvas id="canvas"></canvas>
<div id="bboxModal"></div>
</body>`, { runScripts: 'outside-only' });
const syncWindow = syncDom.window;
const nativeApi = Object.freeze({
postMessage: message => syncSent.push(message),
getState: () => ({}),
setState: () => {},
});
syncWindow.acquireVsCodeApi = () => nativeApi;
syncWindow.eval(apiShimScript);
const sharedApi = syncWindow.acquireVsCodeApi();
assert.notStrictEqual(sharedApi, nativeApi, 'shared API shim must not expose the frozen native VS Code API object');
assert.strictEqual(Object.isFrozen(nativeApi), true, 'test must model the real frozen VS Code Webview API');

const externalSyncSource = fs.readFileSync(path.join(root, 'media', 'annotationPanel', 'externalSync.js'), 'utf8');
syncWindow.eval(externalSyncSource);
syncWindow.dispatchEvent(new syncWindow.MessageEvent('message', {
data: { type: 'load', annotationMode: 'rect', imagePath: 'x/frozen.png', annotations: [] },
}));
syncWindow.acquireVsCodeApi().postMessage({
type: 'save',
mode: 'rect',
annotations: [{ category: 'screen.guard', x: 1, y: 2, w: 3, h: 4 }],
});
const frozenSave = syncSent.find(message => message?.type === 'save');
assert(frozenSave, 'save should reach the native VS Code API through the mutable facade');
assert.strictEqual(frozenSave.imagePath, 'x/frozen.png', 'external sync should bind saves to the loaded image path');
assert.strictEqual(frozenSave.editorVersion, 1, 'external sync should version saves before they leave the webview');

console.log('annotation mode cycle shortcut tests passed');
Loading