From ed74f5893be22e9d47940a00401dc1fcc0c903a8 Mon Sep 17 00:00:00 2001 From: GarboMuffin Date: Sat, 26 Aug 2023 17:57:51 -0500 Subject: [PATCH] iframe: various fixes (#955) - update default page - add resize behavior option - fix position after custom stage size changes --- extensions/iframe.js | 67 ++++++++++++++++++++++++++++++++++++++------ website/hello.html | 2 +- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/extensions/iframe.js b/extensions/iframe.js index 6b6f044d2b..687570356d 100644 --- a/extensions/iframe.js +++ b/extensions/iframe.js @@ -7,6 +7,7 @@ /** @type {HTMLIFrameElement|null} */ let iframe = null; + let overlay = null; const featurePolicy = { accelerometer: "'none'", @@ -39,32 +40,48 @@ let width = -1; // negative means default let height = -1; // negative means default let interactive = true; + let resizeBehavior = "scale"; const updateFrameAttributes = () => { if (!iframe) { return; } + iframe.style.pointerEvents = interactive ? "auto" : "none"; + const { stageWidth, stageHeight } = Scratch.vm.runtime; const effectiveWidth = width >= 0 ? width : stageWidth; const effectiveHeight = height >= 0 ? height : stageHeight; - iframe.style.width = `${effectiveWidth}px`; - iframe.style.height = `${effectiveHeight}px`; - let transform = ""; - transform += `translate(${stageWidth / 2 - effectiveWidth / 2 + x}px,${ - stageHeight / 2 - effectiveHeight / 2 - y - }px)`; - iframe.style.transform = transform; + if (resizeBehavior === "scale") { + iframe.style.width = `${effectiveWidth}px`; + iframe.style.height = `${effectiveHeight}px`; - iframe.style.pointerEvents = interactive ? "auto" : "none"; + iframe.style.transform = `translate(${-effectiveWidth / 2 + x}px, ${ + -effectiveHeight / 2 - y + }px)`; + iframe.style.top = "0"; + iframe.style.left = "0"; + } else { + // As the stage is resized in fullscreen mode, only % can be relied upon + iframe.style.width = `${(effectiveWidth / stageWidth) * 100}%`; + iframe.style.height = `${(effectiveHeight / stageHeight) * 100}%`; + + iframe.style.transform = ""; + iframe.style.top = `${(0.5 - effectiveHeight / 2 / stageHeight) * 100}%`; + iframe.style.left = `${(0.5 - effectiveWidth / 2 / stageWidth) * 100}%`; + } }; + const getOverlayMode = () => + resizeBehavior === "scale" ? "scale-centered" : "manual"; + const createFrame = (src) => { iframe = document.createElement("iframe"); iframe.style.width = "100%"; iframe.style.height = "100%"; iframe.style.border = "none"; + iframe.style.position = "absolute"; iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); iframe.setAttribute( "allow", @@ -75,11 +92,13 @@ iframe.setAttribute("allowtransparency", "true"); iframe.setAttribute("allowtransparency", "true"); iframe.setAttribute("src", src); - Scratch.renderer.addOverlay(iframe, "scale"); + overlay = Scratch.renderer.addOverlay(iframe, getOverlayMode()); updateFrameAttributes(); }; + Scratch.vm.on("STAGE_SIZE_CHANGED", updateFrameAttributes); + class IframeExtension { getInfo() { return { @@ -191,6 +210,17 @@ }, }, }, + { + opcode: "setResize", + blockType: Scratch.BlockType.COMMAND, + text: "set iframe resize behavior to [RESIZE]", + arguments: { + RESIZE: { + type: Scratch.ArgumentType.STRING, + menu: "resizeMenu", + }, + }, + }, ], menus: { getMenu: { @@ -203,12 +233,17 @@ "width", "height", "interactive", + "resize behavior", ], }, interactiveMenu: { acceptReporters: true, items: ["true", "false"], }, + resizeMenu: { + acceptReporters: true, + items: ["scale", "viewport"], + }, }, }; } @@ -246,6 +281,7 @@ if (iframe) { Scratch.renderer.removeOverlay(iframe); iframe = null; + overlay = null; } } @@ -266,6 +302,8 @@ return height >= 0 ? height : Scratch.vm.runtime.stageHeight; } else if (MENU === "interactive") { return interactive; + } else if (MENU === "resize behavior") { + return resizeBehavior; } else { return ""; } @@ -295,6 +333,17 @@ interactive = Scratch.Cast.toBoolean(INTERACTIVE); updateFrameAttributes(); } + + setResize({ RESIZE }) { + if (RESIZE === "scale" || RESIZE === "viewport") { + resizeBehavior = RESIZE; + if (overlay) { + overlay.mode = getOverlayMode(); + Scratch.renderer._updateOverlays(); + updateFrameAttributes(); + } + } + } } Scratch.extensions.register(new IframeExtension()); diff --git a/website/hello.html b/website/hello.html index 2c0a89cf45..5bd51d8c8f 100644 --- a/website/hello.html +++ b/website/hello.html @@ -35,7 +35,7 @@

It works!

Transparency is supported, so you can display the project behind the frame as long as the page doesn't explicitly set a background color.

You can also use JavaScript and most other Web APIs, like this:

-

Due to browser security measures blocks like "mouse x" and "mouse down?" will not work while the cursor is over the frame unless you disable interactivity.

+

Due to browser security measures, blocks like "mouse x" and "mouse down?" will not work while the cursor is over the frame unless you use the disable interactivity block.

Many websites also block other sites from embedding them for security reasons.