autosave.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. * Copyright 2010-2019 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 singlefile, SingleFileBrowser, URL, Blob */
  24. singlefile.autosave = (() => {
  25. return {
  26. onMessage,
  27. onMessageExternal,
  28. onTabUpdated,
  29. isEnabled,
  30. refreshTabs
  31. };
  32. async function onMessage(message, sender) {
  33. if (message.method.endsWith(".init")) {
  34. const [options, autoSaveEnabled] = await Promise.all([singlefile.config.getOptions(sender.tab.url, true), isEnabled(sender.tab)]);
  35. return { options, autoSaveEnabled };
  36. }
  37. if (message.method.endsWith(".save")) {
  38. return saveContent(message, sender.tab);
  39. }
  40. }
  41. async function onMessageExternal(message, currentTab) {
  42. if (message.method == "enableAutoSave") {
  43. const tabsData = await singlefile.tabsData.get(currentTab.id);
  44. tabsData[currentTab.id].autoSave = message.enabled;
  45. await singlefile.tabsData.set(tabsData);
  46. singlefile.ui.refreshTab(currentTab);
  47. }
  48. if (message.method == "isAutoSaveEnabled") {
  49. return await isEnabled(currentTab);
  50. }
  51. }
  52. async function onTabUpdated(tabId, changeInfo, tab) {
  53. const [options, autoSaveEnabled] = await Promise.all([singlefile.config.getOptions(tab.url, true), isEnabled(tab)]);
  54. if (options && ((options.autoSaveLoad || options.autoSaveLoadOrUnload) && autoSaveEnabled)) {
  55. if (changeInfo.status == "complete") {
  56. singlefile.core.saveTab(tab, { autoSave: true });
  57. }
  58. }
  59. }
  60. async function isEnabled(tab) {
  61. if (tab) {
  62. const [tabsData, rule] = await Promise.all([singlefile.tabsData.get(), singlefile.config.getRule(tab.url)]);
  63. return singlefile.util.isAllowedURL(tab.url) &&
  64. Boolean(tabsData.autoSaveAll ||
  65. (tabsData.autoSaveUnpinned && !tab.pinned) ||
  66. (tabsData[tab.id] && tabsData[tab.id].autoSave)) &&
  67. (!rule || rule.autoSaveProfile != singlefile.config.DISABLED_PROFILE_NAME);
  68. }
  69. }
  70. async function refreshTabs() {
  71. const tabs = (await singlefile.tabs.get({})).filter(tab => singlefile.util.isAllowedURL(tab.url));
  72. return Promise.all(tabs.map(async tab => {
  73. const [options, autoSaveEnabled] = await Promise.all([singlefile.config.getOptions(tab.url, true), isEnabled(tab)]);
  74. singlefile.tabs.sendMessage(tab.id, { method: "content.init", autoSaveEnabled, options }).catch(() => { /* ignored */ });
  75. }));
  76. }
  77. async function saveContent(message, tab) {
  78. const options = await singlefile.config.getOptions(tab.url, true);
  79. const tabId = tab.id;
  80. options.content = message.content;
  81. options.url = message.url;
  82. options.framesData = message.framesData;
  83. options.canvasData = message.canvasData;
  84. options.fontsData = message.fontsData;
  85. options.stylesheetContents = message.stylesheetContents;
  86. options.imageData = message.imageData;
  87. options.postersData = message.postersData;
  88. options.usedFonts = message.usedFonts;
  89. options.shadowRootContents = message.shadowRootContents;
  90. options.referrer = message.referrer;
  91. options.insertSingleFileComment = true;
  92. options.insertFaviconLink = true;
  93. options.backgroundTab = true;
  94. options.autoSave = true;
  95. options.incognito = tab.incognito;
  96. options.tabId = tabId;
  97. options.tabIndex = tab.index;
  98. let index = 0, maxIndex = 0;
  99. options.onprogress = async event => {
  100. if (event.type == event.RESOURCES_INITIALIZED) {
  101. maxIndex = event.detail.max;
  102. singlefile.ui.onProgress(tabId, index, maxIndex, { autoSave: true });
  103. }
  104. if (event.type == event.RESOURCE_LOADED) {
  105. index++;
  106. singlefile.ui.onProgress(tabId, index, maxIndex, { autoSave: true });
  107. } else if (event.type == event.PAGE_ENDED) {
  108. singlefile.ui.onEnd(tabId, { autoSave: true });
  109. }
  110. };
  111. const processor = new (SingleFileBrowser.getClass())(options);
  112. await processor.run();
  113. const page = await processor.getPageData();
  114. page.url = URL.createObjectURL(new Blob([page.content], { type: "text/html" }));
  115. return singlefile.download.downloadPage(page, options);
  116. }
  117. })();