From 4477073d6d47e0e00fc02bcd5df6bb1af64778c6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 10 Nov 2020 17:23:23 +0100 Subject: [PATCH] add platform method to offer saving a buffer handle --- assets/download-sandbox.html | 23 ++++++++++++++++++++ index.html | 1 + scripts/build.mjs | 6 ++++++ src/platform/web/Platform.js | 9 ++++++++ src/platform/web/dom/download.js | 37 ++++++++++++++++++++++++++++++++ src/platform/web/ui/css/main.css | 4 ++++ 6 files changed, 80 insertions(+) create mode 100644 assets/download-sandbox.html create mode 100644 src/platform/web/dom/download.js diff --git a/assets/download-sandbox.html b/assets/download-sandbox.html new file mode 100644 index 00000000..ecb4886e --- /dev/null +++ b/assets/download-sandbox.html @@ -0,0 +1,23 @@ + + + + + + + Download! + + + diff --git a/index.html b/index.html index af5f513b..3f077e04 100644 --- a/index.html +++ b/index.html @@ -23,6 +23,7 @@ import {Platform} from "./src/platform/web/Platform.js"; main(new Platform(document.body, { worker: "src/worker.js", + downloadSandbox: "assets/download-sandbox.html", olm: { wasm: "lib/olm/olm.wasm", legacyBundle: "lib/olm/olm_legacy.js", diff --git a/scripts/build.mjs b/scripts/build.mjs index 718802d1..dbbeabcd 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -78,6 +78,10 @@ async function build({modernOnly}) { ])); await assets.write(`worker.js`, await buildJsLegacy("src/platform/web/worker/main.js", ['src/platform/web/worker/polyfill.js'])); } + // copy over non-theme assets + const downloadSandbox = "download-sandbox.html"; + let downloadSandboxHtml = await fs.readFile(path.join(projectDir, `assets/${downloadSandbox}`)); + await assets.write(downloadSandbox, downloadSandboxHtml); // creates the directories where the theme css bundles are placed in, // and writes to assets, so the build bundles can translate them, so do it first await copyThemeAssets(themes, assets); @@ -143,6 +147,7 @@ async function buildHtml(doc, version, globalHash, modernOnly, assets) { }); const pathsJSON = JSON.stringify({ worker: assets.has("worker.js") ? assets.resolve(`worker.js`) : null, + downloadSandbox: assets.resolve("download-sandbox.html"), serviceWorker: "sw.js", olm: { wasm: assets.resolve("olm.wasm"), @@ -234,6 +239,7 @@ function isPreCached(asset) { asset.endsWith(".png") || asset.endsWith(".css") || asset.endsWith(".wasm") || + asset.endsWith(".html") || // most environments don't need the worker asset.endsWith(".js") && !NON_PRECACHED_JS.includes(asset); } diff --git a/src/platform/web/Platform.js b/src/platform/web/Platform.js index 87b055cd..0f5222bd 100644 --- a/src/platform/web/Platform.js +++ b/src/platform/web/Platform.js @@ -28,6 +28,7 @@ import {Crypto} from "./dom/Crypto.js"; import {estimateStorageUsage} from "./dom/StorageEstimate.js"; import {WorkerPool} from "./dom/WorkerPool.js"; import {BufferHandle} from "./dom/BufferHandle.js"; +import {downloadInIframe} from "./dom/download.js"; function addScript(src) { return new Promise(function (resolve, reject) { @@ -133,4 +134,12 @@ export class Platform { createBufferHandle(buffer, mimetype) { return new BufferHandle(buffer, mimetype); } + + offerSaveBufferHandle(bufferHandle, filename) { + if (navigator.msSaveBlob) { + navigator.msSaveBlob(bufferHandle.blob, filename); + } else { + downloadInIframe(this._container, this._paths.downloadSandbox, bufferHandle.blob, filename); + } + } } diff --git a/src/platform/web/dom/download.js b/src/platform/web/dom/download.js new file mode 100644 index 00000000..4e8aaece --- /dev/null +++ b/src/platform/web/dom/download.js @@ -0,0 +1,37 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export async function downloadInIframe(container, iframeSrc, blob, filename) { + let iframe = container.querySelector("iframe.downloadSandbox"); + if (!iframe) { + iframe = document.createElement("iframe"); + iframe.setAttribute("sandbox", "allow-scripts allow-downloads allow-downloads-without-user-activation"); + iframe.setAttribute("src", iframeSrc); + iframe.className = "downloadSandbox"; + container.appendChild(iframe); + let detach; + await new Promise((resolve, reject) => { + detach = () => { + iframe.removeEventListener("load", resolve); + iframe.removeEventListener("error", reject); + } + iframe.addEventListener("load", resolve); + iframe.addEventListener("error", reject); + }); + detach(); + } + iframe.contentWindow.postMessage({type: "download", blob: blob, filename: filename}, "*"); +} diff --git a/src/platform/web/ui/css/main.css b/src/platform/web/ui/css/main.css index aa22839e..913141b6 100644 --- a/src/platform/web/ui/css/main.css +++ b/src/platform/web/ui/css/main.css @@ -49,3 +49,7 @@ body.hydrogen { input::-ms-clear { display: none; } + +.hydrogen > iframe.downloadSandbox { + display: none; +}