/* * Copyright 2010-2020 Gildas Lormeau * contact : gildas.lormeau gmail.com * * This file is part of SingleFile. * * The code in this file is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * (GNU AGPL) as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * The code in this file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero * General Public License for more details. * * As additional permission under GNU AGPL version 3 section 7, you may * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU * AGPL normally required by section 4, provided you include this license * notice and a URL through which recipients can access the Corresponding * Source. */ /* global browser, document, URL, Blob, MouseEvent, setTimeout, open, navigator, File, setInterval, clearInterval */ import * as yabson from "./../../lib/yabson/yabson.js"; import * as ui from "./../../ui/content/content-ui.js"; import { getSharePageBar, setLabels } from "./../../ui/common/common-content-ui.js"; const MAX_CONTENT_SIZE = 16 * (1024 * 1024); let EMBEDDED_IMAGE_BUTTON_MESSAGE, SHARE_PAGE_BUTTON_MESSAGE, SHARE_SELECTION_BUTTON_MESSAGE, ERROR_TITLE_MESSAGE; try { EMBEDDED_IMAGE_BUTTON_MESSAGE = browser.i18n.getMessage("topPanelEmbeddedImageButton"); SHARE_PAGE_BUTTON_MESSAGE = browser.i18n.getMessage("topPanelSharePageButton"); SHARE_SELECTION_BUTTON_MESSAGE = browser.i18n.getMessage("topPanelShareSelectionButton"); ERROR_TITLE_MESSAGE = browser.i18n.getMessage("topPanelError"); } catch (error) { // ignored } let sharePageBar; setLabels({ EMBEDDED_IMAGE_BUTTON_MESSAGE, SHARE_PAGE_BUTTON_MESSAGE, SHARE_SELECTION_BUTTON_MESSAGE, ERROR_TITLE_MESSAGE }); export { downloadPage, downloadPageForeground }; async function downloadPage(pageData, options) { if (options.includeBOM) { pageData.content = "\ufeff" + pageData.content; } const embeddedImage = options.embeddedImage; const message = { method: "downloads.download", taskId: options.taskId, insertTextBody: options.insertTextBody, confirmFilename: options.confirmFilename, filenameConflictAction: options.filenameConflictAction, filename: pageData.filename, mimeType: pageData.mimeType, saveToClipboard: options.saveToClipboard, saveToGDrive: options.saveToGDrive, saveToDropbox: options.saveToDropbox, saveWithWebDAV: options.saveWithWebDAV, webDAVURL: options.webDAVURL, webDAVUser: options.webDAVUser, webDAVPassword: options.webDAVPassword, saveToGitHub: options.saveToGitHub, githubToken: options.githubToken, githubUser: options.githubUser, githubRepository: options.githubRepository, githubBranch: options.githubBranch, saveWithCompanion: options.saveWithCompanion, forceWebAuthFlow: options.forceWebAuthFlow, filenameReplacementCharacter: options.filenameReplacementCharacter, openEditor: options.openEditor, openSavedPage: options.openSavedPage, compressHTML: options.compressHTML, backgroundSave: options.backgroundSave, bookmarkId: options.bookmarkId, replaceBookmarkURL: options.replaceBookmarkURL, applySystemTheme: options.applySystemTheme, defaultEditorMode: options.defaultEditorMode, includeInfobar: options.includeInfobar, openInfobar: options.openInfobar, warnUnsavedPage: options.warnUnsavedPage, createRootDirectory: options.createRootDirectory, selfExtractingArchive: options.selfExtractingArchive, embeddedImage: embeddedImage ? Array.from(embeddedImage) : null, preventAppendedData: options.preventAppendedData, extractDataFromPage: options.extractDataFromPage, insertCanonicalLink: options.insertCanonicalLink, insertMetaNoIndex: options.insertMetaNoIndex, insertMetaCSP: options.insertMetaCSP, password: options.password, compressContent: options.compressContent, foregroundSave: options.foregroundSave, sharePage: options.sharePage, saveToRestFormApi: options.saveToRestFormApi, saveToRestFormApiUrl: options.saveToRestFormApiUrl, saveToRestFormApiFileFieldName: options.saveToRestFormApiFileFieldName, saveToRestFormApiUrlFieldName: options.saveToRestFormApiUrlFieldName, saveToRestFormApiToken: options.saveToRestFormApiToken, saveToS3: options.saveToS3, S3Domain: options.S3Domain, S3Region: options.S3Region, S3Bucket: options.S3Bucket, S3AccessKey: options.S3AccessKey, S3SecretKey: options.S3SecretKey }; const pingInterval = setInterval(() => { browser.runtime.sendMessage({ method: "ping" }).then(() => { }); }, 15000); if (options.compressContent) { if ((!options.backgroundSave || options.saveToGDrive || options.saveToGitHub || options.saveWithCompanion || options.saveWithWebDAV || options.saveToDropbox || options.saveToRestFormApi || options.saveToS3) && options.confirmFilename && !options.openEditor) { pageData.filename = ui.prompt("Save as", pageData.filename); } if (pageData.filename) { const blob = new Blob([await yabson.serialize(pageData)], { type: pageData.mimeType }); const blobURL = URL.createObjectURL(blob); message.filename = pageData.filename; message.blobURL = blobURL; const result = await browser.runtime.sendMessage(message); URL.revokeObjectURL(blobURL); if (result.error) { message.embeddedImage = embeddedImage; message.blobURL = null; message.pageData = pageData; const serializer = yabson.getSerializer(message); for await (const chunk of serializer) { await browser.runtime.sendMessage({ method: "downloads.download", compressContent: true, data: Array.from(chunk) }); } await browser.runtime.sendMessage({ method: "downloads.download", compressContent: true, mimeType: pageData.mimeType }); } if (options.backgroundSave) { await browser.runtime.sendMessage({ method: "downloads.end", taskId: options.taskId }); } } else { browser.runtime.sendMessage({ method: "downloads.cancel" }); browser.runtime.sendMessage({ method: "ui.processCancelled" }); } } else { if ((options.backgroundSave && !options.sharePage) || options.openEditor || options.saveToGDrive || options.saveToGitHub || options.saveWithCompanion || options.saveWithWebDAV || options.saveToDropbox || options.saveToRestFormApi || options.saveToS3) { let filename = pageData.filename; if ((options.saveToGDrive || options.saveToGitHub || options.saveWithCompanion || options.saveWithWebDAV || options.saveToDropbox || options.saveToRestFormApi || options.saveToS3) && options.confirmFilename && !options.openEditor) { filename = ui.prompt("Save as", pageData.filename); } if (filename) { message.filename = pageData.filename = filename; const blobURL = URL.createObjectURL(new Blob([pageData.content], { type: pageData.mimeType })); message.blobURL = blobURL; const result = await browser.runtime.sendMessage(message); URL.revokeObjectURL(blobURL); if (result.error) { message.blobURL = null; for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE < pageData.content.length; blockIndex++) { message.truncated = pageData.content.length > MAX_CONTENT_SIZE; if (message.truncated) { message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > pageData.content.length; message.content = pageData.content.substring(blockIndex * MAX_CONTENT_SIZE, (blockIndex + 1) * MAX_CONTENT_SIZE); } else { message.content = pageData.content; } await browser.runtime.sendMessage(message); } } } else { browser.runtime.sendMessage({ method: "downloads.cancel" }); browser.runtime.sendMessage({ method: "ui.processCancelled" }); } } else { if (options.saveToClipboard) { saveToClipboard(pageData); } else { await downloadPageForeground(pageData, options); } if (options.openSavedPage) { open(URL.createObjectURL(new Blob([pageData.content], { type: pageData.mimeType }))); } browser.runtime.sendMessage({ method: "ui.processEnd" }); } await browser.runtime.sendMessage({ method: "downloads.end", taskId: options.taskId, hash: pageData.hash, woleetKey: options.woleetKey }); } clearInterval(pingInterval); } async function downloadPageForeground(pageData, options) { if (Array.isArray(pageData.content)) { pageData.content = new Uint8Array(pageData.content); } if (options.sharePage && navigator.share) { await sharePage(pageData, options); } else { let filename = pageData.filename; if (options.confirmFilename) { filename = ui.prompt("Save as", pageData.filename); if (filename) { pageData.filename = filename; } else { browser.runtime.sendMessage({ method: "downloads.cancel" }); browser.runtime.sendMessage({ method: "ui.processCancelled" }); } } if (filename) { const link = document.createElement("a"); link.download = pageData.filename; link.href = URL.createObjectURL(new Blob([pageData.content], { type: pageData.mimeType })); link.dispatchEvent(new MouseEvent("click")); return new Promise(resolve => setTimeout(() => { URL.revokeObjectURL(link.href); resolve(); }, 1000)); } } } async function sharePage(pageData, options) { sharePageBar = getSharePageBar(); const cancelled = await sharePageBar.display(options.selected); if (!cancelled) { const data = { files: [new File([pageData.content], pageData.filename, { type: pageData.mimeType })] }; try { await navigator.share(data); sharePageBar.hide(); } catch (error) { sharePageBar.hide(); if (error.name === "AbortError") { await sharePage(pageData, options); } else { throw error; } } } } function saveToClipboard(pageData) { const command = "copy"; document.addEventListener(command, listener); document.execCommand(command); document.removeEventListener(command, listener); function listener(event) { event.clipboardData.setData(pageData.mimeType, pageData.content); event.clipboardData.setData("text/plain", pageData.content); event.preventDefault(); } }