download.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Copyright 2010-2020 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * The code in this file is free software: you can redistribute it and/or
  8. * modify it under the terms of the GNU Affero General Public License
  9. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * The code in this file is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  15. * General Public License for more details.
  16. *
  17. * As additional permission under GNU AGPL version 3 section 7, you may
  18. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  19. * AGPL normally required by section 4, provided you include this license
  20. * notice and a URL through which recipients can access the Corresponding
  21. * Source.
  22. */
  23. /* global browser, document, URL, Blob, MouseEvent, setTimeout, open */
  24. import * as yabson from "./../../lib/yabson/yabson.js";
  25. const MAX_CONTENT_SIZE = 32 * (1024 * 1024);
  26. export {
  27. downloadPage
  28. };
  29. async function downloadPage(pageData, options) {
  30. if (options.includeBOM) {
  31. pageData.content = "\ufeff" + pageData.content;
  32. }
  33. const message = {
  34. method: "downloads.download",
  35. taskId: options.taskId,
  36. insertTextBody: options.insertTextBody,
  37. confirmFilename: options.confirmFilename,
  38. filenameConflictAction: options.filenameConflictAction,
  39. filename: pageData.filename,
  40. saveToClipboard: options.saveToClipboard,
  41. saveToGDrive: options.saveToGDrive,
  42. saveWithWebDAV: options.saveWithWebDAV,
  43. webDAVURL: options.webDAVURL,
  44. webDAVUser: options.webDAVUser,
  45. webDAVPassword: options.webDAVPassword,
  46. saveToGitHub: options.saveToGitHub,
  47. githubToken: options.githubToken,
  48. githubUser: options.githubUser,
  49. githubRepository: options.githubRepository,
  50. githubBranch: options.githubBranch,
  51. saveWithCompanion: options.saveWithCompanion,
  52. forceWebAuthFlow: options.forceWebAuthFlow,
  53. filenameReplacementCharacter: options.filenameReplacementCharacter,
  54. openEditor: options.openEditor,
  55. openSavedPage: options.openSavedPage,
  56. compressHTML: options.compressHTML,
  57. backgroundSave: options.backgroundSave,
  58. bookmarkId: options.bookmarkId,
  59. replaceBookmarkURL: options.replaceBookmarkURL,
  60. applySystemTheme: options.applySystemTheme,
  61. defaultEditorMode: options.defaultEditorMode,
  62. includeInfobar: options.includeInfobar,
  63. warnUnsavedPage: options.warnUnsavedPage,
  64. createRootDirectory: options.createRootDirectory,
  65. selfExtractingArchive: options.selfExtractingArchive,
  66. extractDataFromPage: options.extractDataFromPage,
  67. insertCanonicalLink: options.insertCanonicalLink,
  68. insertMetaNoIndex: options.insertMetaNoIndex,
  69. password: options.password,
  70. compressContent: options.compressContent,
  71. foregroundSave: options.foregroundSave
  72. };
  73. if (options.compressContent) {
  74. const blobURL = URL.createObjectURL(new Blob([await yabson.serialize(pageData)], { type: "application/octet-stream" }));
  75. message.blobURL = blobURL;
  76. const result = await browser.runtime.sendMessage(message);
  77. URL.revokeObjectURL(blobURL);
  78. if (result.error) {
  79. message.blobURL = null;
  80. message.pageData = pageData;
  81. const serializer = yabson.getSerializer(message);
  82. for await (const data of serializer) {
  83. await browser.runtime.sendMessage({
  84. method: "downloads.download",
  85. compressContent: true,
  86. data: Array.from(data)
  87. });
  88. }
  89. await browser.runtime.sendMessage({
  90. method: "downloads.download",
  91. compressContent: true
  92. });
  93. }
  94. if (options.backgroundSave) {
  95. await browser.runtime.sendMessage({ method: "downloads.end", taskId: options.taskId });
  96. }
  97. } else {
  98. if (options.backgroundSave || options.openEditor || options.saveToGDrive || options.saveToGitHub || options.saveWithCompanion || options.saveWithWebDAV) {
  99. const blobURL = URL.createObjectURL(new Blob([pageData.content], { type: "text/html" }));
  100. message.blobURL = blobURL;
  101. const result = await browser.runtime.sendMessage(message);
  102. URL.revokeObjectURL(blobURL);
  103. if (result.error) {
  104. message.blobURL = null;
  105. for (let blockIndex = 0; blockIndex * MAX_CONTENT_SIZE < pageData.content.length; blockIndex++) {
  106. message.truncated = pageData.content.length > MAX_CONTENT_SIZE;
  107. if (message.truncated) {
  108. message.finished = (blockIndex + 1) * MAX_CONTENT_SIZE > pageData.content.length;
  109. message.content = pageData.content.substring(blockIndex * MAX_CONTENT_SIZE, (blockIndex + 1) * MAX_CONTENT_SIZE);
  110. } else {
  111. message.content = pageData.content;
  112. }
  113. await browser.runtime.sendMessage(message);
  114. }
  115. }
  116. } else {
  117. if (options.saveToClipboard) {
  118. saveToClipboard(pageData);
  119. } else {
  120. await downloadPageForeground(pageData);
  121. }
  122. if (options.openSavedPage) {
  123. open(URL.createObjectURL(new Blob([pageData.content], { type: "text/html" })));
  124. }
  125. browser.runtime.sendMessage({ method: "ui.processEnd" });
  126. }
  127. await browser.runtime.sendMessage({ method: "downloads.end", taskId: options.taskId, hash: pageData.hash, woleetKey: options.woleetKey });
  128. }
  129. }
  130. async function downloadPageForeground(pageData) {
  131. if (pageData.filename && pageData.filename.length) {
  132. const link = document.createElement("a");
  133. link.download = pageData.filename;
  134. link.href = URL.createObjectURL(new Blob([pageData.content], { type: "text/html" }));
  135. link.dispatchEvent(new MouseEvent("click"));
  136. setTimeout(() => URL.revokeObjectURL(link.href), 1000);
  137. }
  138. return new Promise(resolve => setTimeout(resolve, 1));
  139. }
  140. function saveToClipboard(page) {
  141. const command = "copy";
  142. document.addEventListener(command, listener);
  143. document.execCommand(command);
  144. document.removeEventListener(command, listener);
  145. function listener(event) {
  146. event.clipboardData.setData("text/html", page.content);
  147. event.clipboardData.setData("text/plain", page.content);
  148. event.preventDefault();
  149. }
  150. }