single-file-bootstrap.js 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.singlefileBootstrap = {}));
  5. })(this, (function (exports) { 'use strict';
  6. /*
  7. * Copyright 2010-2022 Gildas Lormeau
  8. * contact : gildas.lormeau <at> gmail.com
  9. *
  10. * This file is part of SingleFile.
  11. *
  12. * The code in this file is free software: you can redistribute it and/or
  13. * modify it under the terms of the GNU Affero General Public License
  14. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  15. * of the License, or (at your option) any later version.
  16. *
  17. * The code in this file is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  20. * General Public License for more details.
  21. *
  22. * As additional permission under GNU AGPL version 3 section 7, you may
  23. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  24. * AGPL normally required by section 4, provided you include this license
  25. * notice and a URL through which recipients can access the Corresponding
  26. * Source.
  27. */
  28. /* global globalThis, window */
  29. const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
  30. const LOAD_DEFERRED_IMAGES_END_EVENT = "single-file-load-deferred-images-end";
  31. const LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT = "single-file-load-deferred-images-keep-zoom-level-start";
  32. const LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT = "single-file-load-deferred-images-keep-zoom-level-end";
  33. const BLOCK_COOKIES_START_EVENT = "single-file-block-cookies-start";
  34. const BLOCK_COOKIES_END_EVENT = "single-file-block-cookies-end";
  35. const DISPATCH_SCROLL_START_EVENT = "single-file-dispatch-scroll-event-start";
  36. const DISPATCH_SCROLL_END_EVENT = "single-file-dispatch-scroll-event-end";
  37. const BLOCK_STORAGE_START_EVENT = "single-file-block-storage-start";
  38. const BLOCK_STORAGE_END_EVENT = "single-file-block-storage-end";
  39. const LOAD_IMAGE_EVENT = "single-file-load-image";
  40. const IMAGE_LOADED_EVENT = "single-file-image-loaded";
  41. const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
  42. const DELETE_FONT_EVENT = "single-file-delete-font";
  43. const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
  44. const FONT_FACE_PROPERTY_NAME = "_singleFile_fontFaces";
  45. const CustomEvent$1 = globalThis.CustomEvent;
  46. const document$2 = globalThis.document;
  47. const Document = globalThis.Document;
  48. const JSON$2 = globalThis.JSON;
  49. const MutationObserver$3 = globalThis.MutationObserver;
  50. let fontFaces;
  51. if (window[FONT_FACE_PROPERTY_NAME]) {
  52. fontFaces = window[FONT_FACE_PROPERTY_NAME];
  53. } else {
  54. fontFaces = window[FONT_FACE_PROPERTY_NAME] = new Map();
  55. }
  56. init$1();
  57. new MutationObserver$3(init$1).observe(document$2, { childList: true });
  58. function init$1() {
  59. if (document$2 instanceof Document) {
  60. document$2.addEventListener(NEW_FONT_FACE_EVENT, event => {
  61. const detail = event.detail;
  62. const key = Object.assign({}, detail);
  63. delete key.src;
  64. fontFaces.set(JSON$2.stringify(key), detail);
  65. });
  66. document$2.addEventListener(DELETE_FONT_EVENT, event => {
  67. const detail = event.detail;
  68. const key = Object.assign({}, detail);
  69. delete key.src;
  70. fontFaces.delete(JSON$2.stringify(key));
  71. });
  72. document$2.addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
  73. }
  74. }
  75. function getFontsData$1() {
  76. return Array.from(fontFaces.values());
  77. }
  78. function loadDeferredImagesStart(options) {
  79. if (options.loadDeferredImagesBlockCookies) {
  80. document$2.dispatchEvent(new CustomEvent$1(BLOCK_COOKIES_START_EVENT));
  81. }
  82. if (options.loadDeferredImagesBlockStorage) {
  83. document$2.dispatchEvent(new CustomEvent$1(BLOCK_STORAGE_START_EVENT));
  84. }
  85. if (options.loadDeferredImagesDispatchScrollEvent) {
  86. document$2.dispatchEvent(new CustomEvent$1(DISPATCH_SCROLL_START_EVENT));
  87. }
  88. if (options.loadDeferredImagesKeepZoomLevel) {
  89. document$2.dispatchEvent(new CustomEvent$1(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT));
  90. } else {
  91. document$2.dispatchEvent(new CustomEvent$1(LOAD_DEFERRED_IMAGES_START_EVENT));
  92. }
  93. }
  94. function loadDeferredImagesEnd(options) {
  95. if (options.loadDeferredImagesBlockCookies) {
  96. document$2.dispatchEvent(new CustomEvent$1(BLOCK_COOKIES_END_EVENT));
  97. }
  98. if (options.loadDeferredImagesBlockStorage) {
  99. document$2.dispatchEvent(new CustomEvent$1(BLOCK_STORAGE_END_EVENT));
  100. }
  101. if (options.loadDeferredImagesDispatchScrollEvent) {
  102. document$2.dispatchEvent(new CustomEvent$1(DISPATCH_SCROLL_END_EVENT));
  103. }
  104. if (options.loadDeferredImagesKeepZoomLevel) {
  105. document$2.dispatchEvent(new CustomEvent$1(LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_END_EVENT));
  106. } else {
  107. document$2.dispatchEvent(new CustomEvent$1(LOAD_DEFERRED_IMAGES_END_EVENT));
  108. }
  109. }
  110. /*
  111. * The MIT License (MIT)
  112. *
  113. * Author: Gildas Lormeau
  114. *
  115. * Permission is hereby granted, free of charge, to any person obtaining a copy
  116. * of this software and associated documentation files (the "Software"), to deal
  117. * in the Software without restriction, including without limitation the rights
  118. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  119. * copies of the Software, and to permit persons to whom the Software is
  120. * furnished to do so, subject to the following conditions:
  121. *
  122. * The above copyright notice and this permission notice shall be included in all
  123. * copies or substantial portions of the Software.
  124. *
  125. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  126. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  127. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  128. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  129. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  130. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  131. * SOFTWARE.
  132. */
  133. // derived from https://github.com/postcss/postcss-selector-parser/blob/master/src/util/unesc.js
  134. /*
  135. * The MIT License (MIT)
  136. * Copyright (c) Ben Briggs <beneb.info@gmail.com> (http://beneb.info)
  137. *
  138. * Permission is hereby granted, free of charge, to any person obtaining a copy
  139. * of this software and associated documentation files (the "Software"), to deal
  140. * in the Software without restriction, including without limitation the rights
  141. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  142. * copies of the Software, and to permit persons to whom the Software is
  143. * furnished to do so, subject to the following conditions:
  144. *
  145. * The above copyright notice and this permission notice shall be included in
  146. * all copies or substantial portions of the Software.
  147. *
  148. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  149. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  150. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  151. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  152. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  153. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  154. * THE SOFTWARE.
  155. */
  156. const whitespace = "[\\x20\\t\\r\\n\\f]";
  157. const unescapeRegExp = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig");
  158. function process$2(str) {
  159. return str.replace(unescapeRegExp, (_, escaped, escapedWhitespace) => {
  160. const high = "0x" + escaped - 0x10000;
  161. // NaN means non-codepoint
  162. // Workaround erroneous numeric interpretation of +"0x"
  163. // eslint-disable-next-line no-self-compare
  164. return high !== high || escapedWhitespace
  165. ? escaped
  166. : high < 0
  167. ? // BMP codepoint
  168. String.fromCharCode(high + 0x10000)
  169. : // Supplemental Plane codepoint (surrogate pair)
  170. String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
  171. });
  172. }
  173. const SINGLE_FILE_PREFIX = "single-file-";
  174. const COMMENT_HEADER = "Page saved with SingleFile";
  175. const WAIT_FOR_USERSCRIPT_PROPERTY_NAME = "_singleFile_waitForUserScript";
  176. const MESSAGE_PREFIX = "__frameTree__::";
  177. /*
  178. * Copyright 2010-2022 Gildas Lormeau
  179. * contact : gildas.lormeau <at> gmail.com
  180. *
  181. * This file is part of SingleFile.
  182. *
  183. * The code in this file is free software: you can redistribute it and/or
  184. * modify it under the terms of the GNU Affero General Public License
  185. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  186. * of the License, or (at your option) any later version.
  187. *
  188. * The code in this file is distributed in the hope that it will be useful,
  189. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  190. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  191. * General Public License for more details.
  192. *
  193. * As additional permission under GNU AGPL version 3 section 7, you may
  194. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  195. * AGPL normally required by section 4, provided you include this license
  196. * notice and a URL through which recipients can access the Corresponding
  197. * Source.
  198. */
  199. const INFOBAR_TAGNAME$1 = "single-file-infobar";
  200. /*
  201. * Copyright 2010-2022 Gildas Lormeau
  202. * contact : gildas.lormeau <at> gmail.com
  203. *
  204. * This file is part of SingleFile.
  205. *
  206. * The code in this file is free software: you can redistribute it and/or
  207. * modify it under the terms of the GNU Affero General Public License
  208. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  209. * of the License, or (at your option) any later version.
  210. *
  211. * The code in this file is distributed in the hope that it will be useful,
  212. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  213. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  214. * General Public License for more details.
  215. *
  216. * As additional permission under GNU AGPL version 3 section 7, you may
  217. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  218. * AGPL normally required by section 4, provided you include this license
  219. * notice and a URL through which recipients can access the Corresponding
  220. * Source.
  221. */
  222. const ON_BEFORE_CAPTURE_EVENT_NAME = SINGLE_FILE_PREFIX + "on-before-capture";
  223. const ON_AFTER_CAPTURE_EVENT_NAME = SINGLE_FILE_PREFIX + "on-after-capture";
  224. const GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "request-get-adopted-stylesheets";
  225. const GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT = SINGLE_FILE_PREFIX + "response-get-adopted-stylesheets";
  226. const UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT = SINGLE_FILE_PREFIX + "unregister-request-get-adopted-stylesheets";
  227. const ON_INIT_USERSCRIPT_EVENT = SINGLE_FILE_PREFIX + "user-script-init";
  228. const REMOVED_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "removed-content";
  229. const HIDDEN_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-content";
  230. const KEPT_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "kept-content";
  231. const HIDDEN_FRAME_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "hidden-frame";
  232. const PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "preserved-space-element";
  233. const SHADOW_ROOT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "shadow-root-element";
  234. const WIN_ID_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "win-id";
  235. const IMAGE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "image";
  236. const POSTER_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "poster";
  237. const VIDEO_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "video";
  238. const CANVAS_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "canvas";
  239. const STYLE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "movable-style";
  240. const INPUT_VALUE_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "input-value";
  241. const LAZY_SRC_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "lazy-loaded-src";
  242. const STYLESHEET_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "stylesheet";
  243. const DISABLED_NOSCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "disabled-noscript";
  244. const INVALID_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "invalid-element";
  245. const ASYNC_SCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "async-script";
  246. const FLOW_ELEMENTS_SELECTOR = "*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)";
  247. const KEPT_TAG_NAMES = ["NOSCRIPT", "DISABLED-NOSCRIPT", "META", "LINK", "STYLE", "TITLE", "TEMPLATE", "SOURCE", "OBJECT", "SCRIPT", "HEAD", "BODY"];
  248. const IGNORED_TAG_NAMES = ["SCRIPT", "NOSCRIPT", "META", "LINK", "TEMPLATE"];
  249. const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
  250. const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
  251. const FONT_WEIGHTS = {
  252. regular: "400",
  253. normal: "400",
  254. bold: "700",
  255. bolder: "700",
  256. lighter: "100"
  257. };
  258. const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
  259. const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
  260. const INFOBAR_TAGNAME = INFOBAR_TAGNAME$1;
  261. const EMPTY_RESOURCE = "data:,";
  262. const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
  263. const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
  264. const JSON$1 = globalThis.JSON;
  265. const CustomEvent = globalThis.CustomEvent;
  266. const MutationObserver$2 = globalThis.MutationObserver;
  267. function initUserScriptHandler() {
  268. addEventListener(ON_INIT_USERSCRIPT_EVENT, () => globalThis[WAIT_FOR_USERSCRIPT_PROPERTY_NAME] = async eventPrefixName => {
  269. const event = new CustomEvent(eventPrefixName + "-request", { cancelable: true });
  270. const promiseResponse = new Promise(resolve => addEventListener(eventPrefixName + "-response", resolve));
  271. dispatchEvent(event);
  272. if (event.defaultPrevented) {
  273. await promiseResponse;
  274. }
  275. });
  276. new MutationObserver$2(initUserScriptHandler).observe(globalThis.document, { childList: true });
  277. }
  278. function initDoc(doc) {
  279. doc.querySelectorAll("meta[http-equiv=refresh]").forEach(element => {
  280. element.removeAttribute("http-equiv");
  281. element.setAttribute("disabled-http-equiv", "refresh");
  282. });
  283. }
  284. function preProcessDoc(doc, win, options) {
  285. doc.querySelectorAll("noscript:not([" + DISABLED_NOSCRIPT_ATTRIBUTE_NAME + "])").forEach(element => {
  286. element.setAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME, element.textContent);
  287. element.textContent = "";
  288. });
  289. initDoc(doc);
  290. if (doc.head) {
  291. doc.head.querySelectorAll(FLOW_ELEMENTS_SELECTOR).forEach(element => element.hidden = true);
  292. }
  293. doc.querySelectorAll("svg foreignObject").forEach(element => {
  294. const flowElements = element.querySelectorAll("html > head > " + FLOW_ELEMENTS_SELECTOR + ", html > body > " + FLOW_ELEMENTS_SELECTOR);
  295. if (flowElements.length) {
  296. Array.from(element.childNodes).forEach(node => node.remove());
  297. flowElements.forEach(flowElement => element.appendChild(flowElement));
  298. }
  299. });
  300. const invalidElements = new Map();
  301. let elementsInfo;
  302. if (win && doc.documentElement) {
  303. doc.querySelectorAll("button button, a a").forEach(element => {
  304. const placeHolderElement = doc.createElement("template");
  305. placeHolderElement.setAttribute(INVALID_ELEMENT_ATTRIBUTE_NAME, "");
  306. placeHolderElement.content.appendChild(element.cloneNode(true));
  307. invalidElements.set(element, placeHolderElement);
  308. element.replaceWith(placeHolderElement);
  309. });
  310. elementsInfo = getElementsInfo(win, doc, doc.documentElement, options);
  311. if (options.moveStylesInHead) {
  312. doc.querySelectorAll("body style, body ~ style").forEach(element => {
  313. const computedStyle = getComputedStyle(win, element);
  314. if (computedStyle && testHiddenElement(element, computedStyle)) {
  315. element.setAttribute(STYLE_ATTRIBUTE_NAME, "");
  316. elementsInfo.markedElements.push(element);
  317. }
  318. });
  319. }
  320. } else {
  321. elementsInfo = {
  322. canvases: [],
  323. images: [],
  324. posters: [],
  325. videos: [],
  326. usedFonts: [],
  327. shadowRoots: [],
  328. markedElements: []
  329. };
  330. }
  331. return {
  332. canvases: elementsInfo.canvases,
  333. fonts: getFontsData(),
  334. stylesheets: getStylesheetsData(doc),
  335. images: elementsInfo.images,
  336. posters: elementsInfo.posters,
  337. videos: elementsInfo.videos,
  338. usedFonts: Array.from(elementsInfo.usedFonts.values()),
  339. shadowRoots: elementsInfo.shadowRoots,
  340. referrer: doc.referrer,
  341. markedElements: elementsInfo.markedElements,
  342. invalidElements,
  343. scrollPosition: { x: win.scrollX, y: win.scrollY },
  344. adoptedStyleSheets: getStylesheetsContent(doc.adoptedStyleSheets)
  345. };
  346. }
  347. function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], markedElements: [] }, ascendantHidden) {
  348. if (element.childNodes) {
  349. const elements = Array.from(element.childNodes).filter(node => (node instanceof win.HTMLElement) || (node instanceof win.SVGElement) || (node instanceof globalThis.HTMLElement) || (node instanceof globalThis.SVGElement));
  350. elements.forEach(element => {
  351. let elementHidden, elementKept, computedStyle;
  352. if (!options.autoSaveExternalSave && (options.removeHiddenElements || options.removeUnusedFonts || options.compressHTML)) {
  353. computedStyle = getComputedStyle(win, element);
  354. if ((element instanceof win.HTMLElement) || (element instanceof globalThis.HTMLElement)) {
  355. if (options.removeHiddenElements) {
  356. elementKept = ((ascendantHidden || element.closest("html > head")) && KEPT_TAG_NAMES.includes(element.tagName.toUpperCase())) || element.closest("details");
  357. if (!elementKept) {
  358. elementHidden = ascendantHidden || testHiddenElement(element, computedStyle);
  359. if (elementHidden && !IGNORED_TAG_NAMES.includes(element.tagName.toUpperCase())) {
  360. element.setAttribute(HIDDEN_CONTENT_ATTRIBUTE_NAME, "");
  361. data.markedElements.push(element);
  362. }
  363. }
  364. }
  365. }
  366. if (!elementHidden) {
  367. if (options.compressHTML && computedStyle) {
  368. const whiteSpace = computedStyle.getPropertyValue("white-space");
  369. if (whiteSpace && whiteSpace.startsWith("pre")) {
  370. element.setAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, "");
  371. data.markedElements.push(element);
  372. }
  373. }
  374. if (options.removeUnusedFonts) {
  375. getUsedFont(computedStyle, options, data.usedFonts);
  376. getUsedFont(getComputedStyle(win, element, ":first-letter"), options, data.usedFonts);
  377. getUsedFont(getComputedStyle(win, element, ":before"), options, data.usedFonts);
  378. getUsedFont(getComputedStyle(win, element, ":after"), options, data.usedFonts);
  379. }
  380. }
  381. }
  382. getResourcesInfo(win, doc, element, options, data, elementHidden, computedStyle);
  383. const shadowRoot = !((element instanceof win.SVGElement) || (element instanceof globalThis.SVGElement)) && getShadowRoot(element);
  384. if (shadowRoot && !element.classList.contains(SINGLE_FILE_UI_ELEMENT_CLASS) && element.tagName.toLowerCase() != INFOBAR_TAGNAME) {
  385. const shadowRootInfo = {};
  386. element.setAttribute(SHADOW_ROOT_ATTRIBUTE_NAME, data.shadowRoots.length);
  387. data.markedElements.push(element);
  388. data.shadowRoots.push(shadowRootInfo);
  389. try {
  390. if (shadowRoot.adoptedStyleSheets) {
  391. if (shadowRoot.adoptedStyleSheets.length) {
  392. shadowRootInfo.adoptedStyleSheets = getStylesheetsContent(shadowRoot.adoptedStyleSheets);
  393. } else if (shadowRoot.adoptedStyleSheets.length === undefined) {
  394. const listener = event => shadowRootInfo.adoptedStyleSheets = event.detail.adoptedStyleSheets;
  395. shadowRoot.addEventListener(GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT, listener);
  396. shadowRoot.dispatchEvent(new CustomEvent(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
  397. if (!shadowRootInfo.adoptedStyleSheets) {
  398. element.dispatchEvent(new CustomEvent(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
  399. }
  400. shadowRoot.removeEventListener(GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT, listener);
  401. }
  402. }
  403. } catch (error) {
  404. // ignored
  405. }
  406. getElementsInfo(win, doc, shadowRoot, options, data, elementHidden);
  407. shadowRootInfo.content = shadowRoot.innerHTML;
  408. shadowRootInfo.mode = shadowRoot.mode;
  409. try {
  410. if (shadowRoot.adoptedStyleSheets && shadowRoot.adoptedStyleSheets.length === undefined) {
  411. shadowRoot.dispatchEvent(new CustomEvent(UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
  412. }
  413. } catch (error) {
  414. // ignored
  415. }
  416. }
  417. getElementsInfo(win, doc, element, options, data, elementHidden);
  418. if (!options.autoSaveExternalSave && options.removeHiddenElements && ascendantHidden) {
  419. if (elementKept || element.getAttribute(KEPT_CONTENT_ATTRIBUTE_NAME) == "") {
  420. if (element.parentElement) {
  421. element.parentElement.setAttribute(KEPT_CONTENT_ATTRIBUTE_NAME, "");
  422. data.markedElements.push(element.parentElement);
  423. }
  424. } else if (elementHidden) {
  425. element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, "");
  426. data.markedElements.push(element);
  427. }
  428. }
  429. });
  430. }
  431. return data;
  432. }
  433. function getStylesheetsContent(styleSheets) {
  434. return styleSheets ? Array.from(styleSheets).map(stylesheet => Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n")) : [];
  435. }
  436. function getResourcesInfo(win, doc, element, options, data, elementHidden, computedStyle) {
  437. const tagName = element.tagName && element.tagName.toUpperCase();
  438. if (tagName == "CANVAS") {
  439. try {
  440. data.canvases.push({
  441. dataURI: element.toDataURL("image/png", ""),
  442. backgroundColor: computedStyle.getPropertyValue("background-color")
  443. });
  444. element.setAttribute(CANVAS_ATTRIBUTE_NAME, data.canvases.length - 1);
  445. data.markedElements.push(element);
  446. } catch (error) {
  447. // ignored
  448. }
  449. }
  450. if (tagName == "IMG") {
  451. const imageData = {
  452. currentSrc: elementHidden ?
  453. EMPTY_RESOURCE :
  454. (options.loadDeferredImages && element.getAttribute(LAZY_SRC_ATTRIBUTE_NAME)) || element.currentSrc
  455. };
  456. data.images.push(imageData);
  457. element.setAttribute(IMAGE_ATTRIBUTE_NAME, data.images.length - 1);
  458. data.markedElements.push(element);
  459. element.removeAttribute(LAZY_SRC_ATTRIBUTE_NAME);
  460. computedStyle = computedStyle || getComputedStyle(win, element);
  461. if (computedStyle) {
  462. imageData.size = getSize(win, element, computedStyle);
  463. const boxShadow = computedStyle.getPropertyValue("box-shadow");
  464. const backgroundImage = computedStyle.getPropertyValue("background-image");
  465. if ((!boxShadow || boxShadow == "none") &&
  466. (!backgroundImage || backgroundImage == "none") &&
  467. (imageData.size.pxWidth > 1 || imageData.size.pxHeight > 1)) {
  468. imageData.replaceable = true;
  469. imageData.backgroundColor = computedStyle.getPropertyValue("background-color");
  470. imageData.objectFit = computedStyle.getPropertyValue("object-fit");
  471. imageData.boxSizing = computedStyle.getPropertyValue("box-sizing");
  472. imageData.objectPosition = computedStyle.getPropertyValue("object-position");
  473. }
  474. }
  475. }
  476. if (tagName == "VIDEO") {
  477. const src = element.currentSrc;
  478. if (src && !src.startsWith("blob:") && !src.startsWith("data:")) {
  479. const computedStyle = getComputedStyle(win, element.parentNode);
  480. data.videos.push({
  481. positionParent: computedStyle && computedStyle.getPropertyValue("position"),
  482. src,
  483. size: {
  484. pxWidth: element.clientWidth,
  485. pxHeight: element.clientHeight
  486. },
  487. currentTime: element.currentTime
  488. });
  489. element.setAttribute(VIDEO_ATTRIBUTE_NAME, data.videos.length - 1);
  490. }
  491. if (!element.getAttribute("poster")) {
  492. const canvasElement = doc.createElement("canvas");
  493. const context = canvasElement.getContext("2d");
  494. canvasElement.width = element.clientWidth;
  495. canvasElement.height = element.clientHeight;
  496. try {
  497. context.drawImage(element, 0, 0, canvasElement.width, canvasElement.height);
  498. data.posters.push(canvasElement.toDataURL("image/png", ""));
  499. element.setAttribute(POSTER_ATTRIBUTE_NAME, data.posters.length - 1);
  500. data.markedElements.push(element);
  501. } catch (error) {
  502. // ignored
  503. }
  504. }
  505. }
  506. if (tagName == "IFRAME") {
  507. if (elementHidden && options.removeHiddenElements) {
  508. element.setAttribute(HIDDEN_FRAME_ATTRIBUTE_NAME, "");
  509. data.markedElements.push(element);
  510. }
  511. }
  512. if (tagName == "INPUT") {
  513. if (element.type != "password") {
  514. element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
  515. data.markedElements.push(element);
  516. }
  517. if (element.type == "radio" || element.type == "checkbox") {
  518. element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.checked);
  519. data.markedElements.push(element);
  520. }
  521. }
  522. if (tagName == "TEXTAREA") {
  523. element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
  524. data.markedElements.push(element);
  525. }
  526. if (tagName == "SELECT") {
  527. element.querySelectorAll("option").forEach(option => {
  528. if (option.selected) {
  529. option.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, "");
  530. data.markedElements.push(option);
  531. }
  532. });
  533. }
  534. if (tagName == "SCRIPT") {
  535. if (element.async && element.getAttribute("async") != "" && element.getAttribute("async") != "async") {
  536. element.setAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME, "");
  537. data.markedElements.push(element);
  538. }
  539. element.textContent = element.textContent.replace(/<\/script>/gi, "<\\/script>");
  540. }
  541. }
  542. function getUsedFont(computedStyle, options, usedFonts) {
  543. if (computedStyle) {
  544. const fontStyle = computedStyle.getPropertyValue("font-style") || "normal";
  545. computedStyle.getPropertyValue("font-family").split(",").forEach(fontFamilyName => {
  546. fontFamilyName = normalizeFontFamily(fontFamilyName);
  547. if (!options.loadedFonts || options.loadedFonts.find(font => normalizeFontFamily(font.family) == fontFamilyName && font.style == fontStyle)) {
  548. const fontWeight = getFontWeight(computedStyle.getPropertyValue("font-weight"));
  549. const fontVariant = computedStyle.getPropertyValue("font-variant") || "normal";
  550. const value = [fontFamilyName, fontWeight, fontStyle, fontVariant];
  551. usedFonts.set(JSON$1.stringify(value), [fontFamilyName, fontWeight, fontStyle, fontVariant]);
  552. }
  553. });
  554. }
  555. }
  556. function getShadowRoot(element) {
  557. const chrome = globalThis.chrome;
  558. if (element.openOrClosedShadowRoot) {
  559. return element.openOrClosedShadowRoot;
  560. } else if (chrome && chrome.dom && chrome.dom.openOrClosedShadowRoot) {
  561. try {
  562. return chrome.dom.openOrClosedShadowRoot(element);
  563. } catch (error) {
  564. return element.shadowRoot;
  565. }
  566. } else {
  567. return element.shadowRoot;
  568. }
  569. }
  570. function normalizeFontFamily(fontFamilyName = "") {
  571. return removeQuotes(process$2(fontFamilyName.trim())).toLowerCase();
  572. }
  573. function testHiddenElement(element, computedStyle) {
  574. let hidden = false;
  575. if (computedStyle) {
  576. const display = computedStyle.getPropertyValue("display");
  577. const opacity = computedStyle.getPropertyValue("opacity");
  578. const visibility = computedStyle.getPropertyValue("visibility");
  579. hidden = display == "none";
  580. if (!hidden && (opacity == "0" || visibility == "hidden") && element.getBoundingClientRect) {
  581. const boundingRect = element.getBoundingClientRect();
  582. hidden = !boundingRect.width && !boundingRect.height;
  583. }
  584. }
  585. return Boolean(hidden);
  586. }
  587. function postProcessDoc(doc, markedElements, invalidElements) {
  588. doc.querySelectorAll("[" + DISABLED_NOSCRIPT_ATTRIBUTE_NAME + "]").forEach(element => {
  589. element.textContent = element.getAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
  590. element.removeAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
  591. });
  592. doc.querySelectorAll("meta[disabled-http-equiv]").forEach(element => {
  593. element.setAttribute("http-equiv", element.getAttribute("disabled-http-equiv"));
  594. element.removeAttribute("disabled-http-equiv");
  595. });
  596. if (doc.head) {
  597. doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.removeAttribute("hidden"));
  598. }
  599. if (!markedElements) {
  600. const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
  601. markedElements = doc.querySelectorAll(singleFileAttributes.map(name => "[" + name + "]").join(","));
  602. }
  603. markedElements.forEach(element => {
  604. element.removeAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME);
  605. element.removeAttribute(HIDDEN_CONTENT_ATTRIBUTE_NAME);
  606. element.removeAttribute(KEPT_CONTENT_ATTRIBUTE_NAME);
  607. element.removeAttribute(HIDDEN_FRAME_ATTRIBUTE_NAME);
  608. element.removeAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME);
  609. element.removeAttribute(IMAGE_ATTRIBUTE_NAME);
  610. element.removeAttribute(POSTER_ATTRIBUTE_NAME);
  611. element.removeAttribute(VIDEO_ATTRIBUTE_NAME);
  612. element.removeAttribute(CANVAS_ATTRIBUTE_NAME);
  613. element.removeAttribute(INPUT_VALUE_ATTRIBUTE_NAME);
  614. element.removeAttribute(SHADOW_ROOT_ATTRIBUTE_NAME);
  615. element.removeAttribute(STYLESHEET_ATTRIBUTE_NAME);
  616. element.removeAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME);
  617. element.removeAttribute(STYLE_ATTRIBUTE_NAME);
  618. });
  619. if (invalidElements) {
  620. invalidElements.forEach((placeholderElement, element) => placeholderElement.replaceWith(element));
  621. }
  622. }
  623. function getStylesheetsData(doc) {
  624. if (doc) {
  625. const contents = [];
  626. doc.querySelectorAll("style").forEach((styleElement, styleIndex) => {
  627. try {
  628. if (!styleElement.sheet.disabled) {
  629. const tempStyleElement = doc.createElement("style");
  630. tempStyleElement.textContent = styleElement.textContent;
  631. doc.body.appendChild(tempStyleElement);
  632. const stylesheet = tempStyleElement.sheet;
  633. tempStyleElement.remove();
  634. const textContentStylesheet = Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
  635. const sheetStylesheet = Array.from(styleElement.sheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
  636. if (!stylesheet || textContentStylesheet != sheetStylesheet) {
  637. styleElement.setAttribute(STYLESHEET_ATTRIBUTE_NAME, styleIndex);
  638. contents[styleIndex] = Array.from(styleElement.sheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
  639. }
  640. }
  641. } catch (error) {
  642. // ignored
  643. }
  644. });
  645. return contents;
  646. }
  647. }
  648. function getSize(win, imageElement, computedStyle) {
  649. let pxWidth = imageElement.naturalWidth;
  650. let pxHeight = imageElement.naturalHeight;
  651. if (!pxWidth && !pxHeight) {
  652. const noStyleAttribute = imageElement.getAttribute("style") == null;
  653. computedStyle = computedStyle || getComputedStyle(win, imageElement);
  654. if (computedStyle) {
  655. let removeBorderWidth = false;
  656. if (computedStyle.getPropertyValue("box-sizing") == "content-box") {
  657. const boxSizingValue = imageElement.style.getPropertyValue("box-sizing");
  658. const boxSizingPriority = imageElement.style.getPropertyPriority("box-sizing");
  659. const clientWidth = imageElement.clientWidth;
  660. imageElement.style.setProperty("box-sizing", "border-box", "important");
  661. removeBorderWidth = imageElement.clientWidth != clientWidth;
  662. if (boxSizingValue) {
  663. imageElement.style.setProperty("box-sizing", boxSizingValue, boxSizingPriority);
  664. } else {
  665. imageElement.style.removeProperty("box-sizing");
  666. }
  667. }
  668. let paddingLeft, paddingRight, paddingTop, paddingBottom, borderLeft, borderRight, borderTop, borderBottom;
  669. paddingLeft = getWidth("padding-left", computedStyle);
  670. paddingRight = getWidth("padding-right", computedStyle);
  671. paddingTop = getWidth("padding-top", computedStyle);
  672. paddingBottom = getWidth("padding-bottom", computedStyle);
  673. if (removeBorderWidth) {
  674. borderLeft = getWidth("border-left-width", computedStyle);
  675. borderRight = getWidth("border-right-width", computedStyle);
  676. borderTop = getWidth("border-top-width", computedStyle);
  677. borderBottom = getWidth("border-bottom-width", computedStyle);
  678. } else {
  679. borderLeft = borderRight = borderTop = borderBottom = 0;
  680. }
  681. pxWidth = Math.max(0, imageElement.clientWidth - paddingLeft - paddingRight - borderLeft - borderRight);
  682. pxHeight = Math.max(0, imageElement.clientHeight - paddingTop - paddingBottom - borderTop - borderBottom);
  683. if (noStyleAttribute) {
  684. imageElement.removeAttribute("style");
  685. }
  686. }
  687. }
  688. return { pxWidth, pxHeight };
  689. }
  690. function getWidth(styleName, computedStyle) {
  691. if (computedStyle.getPropertyValue(styleName).endsWith("px")) {
  692. return parseFloat(computedStyle.getPropertyValue(styleName));
  693. }
  694. }
  695. function getFontsData() {
  696. return getFontsData$1();
  697. }
  698. function serialize$1(doc) {
  699. const docType = doc.doctype;
  700. let docTypeString = "";
  701. if (docType) {
  702. docTypeString = "<!DOCTYPE " + docType.nodeName;
  703. if (docType.publicId) {
  704. docTypeString += " PUBLIC \"" + docType.publicId + "\"";
  705. if (docType.systemId) {
  706. docTypeString += " \"" + docType.systemId + "\"";
  707. }
  708. } else if (docType.systemId) {
  709. docTypeString += " SYSTEM \"" + docType.systemId + "\"";
  710. } if (docType.internalSubset) {
  711. docTypeString += " [" + docType.internalSubset + "]";
  712. }
  713. docTypeString += "> ";
  714. }
  715. return docTypeString + doc.documentElement.outerHTML;
  716. }
  717. function removeQuotes(string) {
  718. if (string.match(REGEXP_SIMPLE_QUOTES_STRING)) {
  719. string = string.replace(REGEXP_SIMPLE_QUOTES_STRING, "$1");
  720. } else {
  721. string = string.replace(REGEXP_DOUBLE_QUOTES_STRING, "$1");
  722. }
  723. return string.trim();
  724. }
  725. function getFontWeight(weight) {
  726. return FONT_WEIGHTS[weight.toLowerCase().trim()] || weight;
  727. }
  728. function getComputedStyle(win, element, pseudoElement) {
  729. try {
  730. return win.getComputedStyle(element, pseudoElement);
  731. } catch (error) {
  732. // ignored
  733. }
  734. }
  735. /*
  736. * Copyright 2010-2022 Gildas Lormeau
  737. * contact : gildas.lormeau <at> gmail.com
  738. *
  739. * This file is part of SingleFile.
  740. *
  741. * The code in this file is free software: you can redistribute it and/or
  742. * modify it under the terms of the GNU Affero General Public License
  743. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  744. * of the License, or (at your option) any later version.
  745. *
  746. * The code in this file is distributed in the hope that it will be useful,
  747. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  748. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  749. * General Public License for more details.
  750. *
  751. * As additional permission under GNU AGPL version 3 section 7, you may
  752. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  753. * AGPL normally required by section 4, provided you include this license
  754. * notice and a URL through which recipients can access the Corresponding
  755. * Source.
  756. */
  757. const helper$2 = {
  758. LAZY_SRC_ATTRIBUTE_NAME,
  759. SINGLE_FILE_UI_ELEMENT_CLASS
  760. };
  761. const MAX_IDLE_TIMEOUT_CALLS = 10;
  762. const ATTRIBUTES_MUTATION_TYPE = "attributes";
  763. const browser$1 = globalThis.browser;
  764. const document$1 = globalThis.document;
  765. const MutationObserver$1 = globalThis.MutationObserver;
  766. const timeouts = new Map();
  767. let idleTimeoutCalls;
  768. if (browser$1 && browser$1.runtime && browser$1.runtime.onMessage && browser$1.runtime.onMessage.addListener) {
  769. browser$1.runtime.onMessage.addListener(message => {
  770. if (message.method == "singlefile.lazyTimeout.onTimeout") {
  771. const timeoutData = timeouts.get(message.type);
  772. if (timeoutData) {
  773. timeouts.delete(message.type);
  774. try {
  775. timeoutData.callback();
  776. } catch (error) {
  777. clearRegularTimeout(message.type);
  778. }
  779. }
  780. }
  781. });
  782. }
  783. async function process$1(options) {
  784. if (document$1.documentElement) {
  785. timeouts.clear();
  786. const bodyHeight = document$1.body ? Math.max(document$1.body.scrollHeight, document$1.documentElement.scrollHeight) : document$1.documentElement.scrollHeight;
  787. const bodyWidth = document$1.body ? Math.max(document$1.body.scrollWidth, document$1.documentElement.scrollWidth) : document$1.documentElement.scrollWidth;
  788. if (bodyHeight > globalThis.innerHeight || bodyWidth > globalThis.innerWidth) {
  789. const maxScrollY = Math.max(bodyHeight - (globalThis.innerHeight * 1.5), 0);
  790. const maxScrollX = Math.max(bodyWidth - (globalThis.innerWidth * 1.5), 0);
  791. if (globalThis.scrollY < maxScrollY || globalThis.scrollX < maxScrollX) {
  792. return triggerLazyLoading(options);
  793. }
  794. }
  795. }
  796. }
  797. function triggerLazyLoading(options) {
  798. idleTimeoutCalls = 0;
  799. return new Promise(async resolve => { // eslint-disable-line no-async-promise-executor
  800. let loadingImages;
  801. const pendingImages = new Set();
  802. const observer = new MutationObserver$1(async mutations => {
  803. mutations = mutations.filter(mutation => mutation.type == ATTRIBUTES_MUTATION_TYPE);
  804. if (mutations.length) {
  805. const updated = mutations.filter(mutation => {
  806. if (mutation.attributeName == "src") {
  807. mutation.target.setAttribute(helper$2.LAZY_SRC_ATTRIBUTE_NAME, mutation.target.src);
  808. mutation.target.addEventListener("load", onResourceLoad);
  809. }
  810. if (mutation.attributeName == "src" || mutation.attributeName == "srcset" ||
  811. (mutation.target.tagName && mutation.target.tagName.toUpperCase() == "SOURCE")) {
  812. return !mutation.target.classList || !mutation.target.classList.contains(helper$2.SINGLE_FILE_UI_ELEMENT_CLASS);
  813. }
  814. });
  815. if (updated.length) {
  816. loadingImages = true;
  817. await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
  818. if (!pendingImages.size) {
  819. await deferLazyLoadEnd(observer, options, cleanupAndResolve);
  820. }
  821. }
  822. }
  823. });
  824. await setIdleTimeout(options.loadDeferredImagesMaxIdleTime * 2);
  825. await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
  826. observer.observe(document$1, { subtree: true, childList: true, attributes: true });
  827. document$1.addEventListener(LOAD_IMAGE_EVENT, onImageLoadEvent);
  828. document$1.addEventListener(IMAGE_LOADED_EVENT, onImageLoadedEvent);
  829. loadDeferredImagesStart(options);
  830. async function setIdleTimeout(delay) {
  831. await setAsyncTimeout("idleTimeout", async () => {
  832. if (!loadingImages) {
  833. clearAsyncTimeout("loadTimeout");
  834. clearAsyncTimeout("maxTimeout");
  835. lazyLoadEnd(observer, options, cleanupAndResolve);
  836. } else if (idleTimeoutCalls < MAX_IDLE_TIMEOUT_CALLS) {
  837. idleTimeoutCalls++;
  838. clearAsyncTimeout("idleTimeout");
  839. await setIdleTimeout(Math.max(500, delay / 2));
  840. }
  841. }, delay, options.loadDeferredImagesNativeTimeout);
  842. }
  843. function onResourceLoad(event) {
  844. const element = event.target;
  845. element.removeAttribute(helper$2.LAZY_SRC_ATTRIBUTE_NAME);
  846. element.removeEventListener("load", onResourceLoad);
  847. }
  848. async function onImageLoadEvent(event) {
  849. loadingImages = true;
  850. await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
  851. await deferLazyLoadEnd(observer, options, cleanupAndResolve);
  852. if (event.detail) {
  853. pendingImages.add(event.detail);
  854. }
  855. }
  856. async function onImageLoadedEvent(event) {
  857. await deferForceLazyLoadEnd(observer, options, cleanupAndResolve);
  858. await deferLazyLoadEnd(observer, options, cleanupAndResolve);
  859. pendingImages.delete(event.detail);
  860. if (!pendingImages.size) {
  861. await deferLazyLoadEnd(observer, options, cleanupAndResolve);
  862. }
  863. }
  864. function cleanupAndResolve(value) {
  865. observer.disconnect();
  866. document$1.removeEventListener(LOAD_IMAGE_EVENT, onImageLoadEvent);
  867. document$1.removeEventListener(IMAGE_LOADED_EVENT, onImageLoadedEvent);
  868. resolve(value);
  869. }
  870. });
  871. }
  872. async function deferLazyLoadEnd(observer, options, resolve) {
  873. await setAsyncTimeout("loadTimeout", () => lazyLoadEnd(observer, options, resolve), options.loadDeferredImagesMaxIdleTime, options.loadDeferredImagesNativeTimeout);
  874. }
  875. async function deferForceLazyLoadEnd(observer, options, resolve) {
  876. await setAsyncTimeout("maxTimeout", async () => {
  877. await clearAsyncTimeout("loadTimeout");
  878. await lazyLoadEnd(observer, options, resolve);
  879. }, options.loadDeferredImagesMaxIdleTime * 10, options.loadDeferredImagesNativeTimeout);
  880. }
  881. async function lazyLoadEnd(observer, options, resolve) {
  882. await clearAsyncTimeout("idleTimeout");
  883. loadDeferredImagesEnd(options);
  884. await setAsyncTimeout("endTimeout", async () => {
  885. await clearAsyncTimeout("maxTimeout");
  886. resolve();
  887. }, options.loadDeferredImagesMaxIdleTime / 2, options.loadDeferredImagesNativeTimeout);
  888. observer.disconnect();
  889. }
  890. async function setAsyncTimeout(type, callback, delay, forceNativeTimeout) {
  891. if (browser$1 && browser$1.runtime && browser$1.runtime.sendMessage && !forceNativeTimeout) {
  892. if (!timeouts.get(type) || !timeouts.get(type).pending) {
  893. const timeoutData = { callback, pending: true };
  894. timeouts.set(type, timeoutData);
  895. try {
  896. await browser$1.runtime.sendMessage({ method: "singlefile.lazyTimeout.setTimeout", type, delay });
  897. } catch (error) {
  898. setRegularTimeout(type, callback, delay);
  899. }
  900. timeoutData.pending = false;
  901. }
  902. } else {
  903. setRegularTimeout(type, callback, delay);
  904. }
  905. }
  906. function setRegularTimeout(type, callback, delay) {
  907. const timeoutId = timeouts.get(type);
  908. if (timeoutId) {
  909. globalThis.clearTimeout(timeoutId);
  910. }
  911. timeouts.set(type, callback);
  912. globalThis.setTimeout(callback, delay);
  913. }
  914. async function clearAsyncTimeout(type) {
  915. if (browser$1 && browser$1.runtime && browser$1.runtime.sendMessage) {
  916. try {
  917. await browser$1.runtime.sendMessage({ method: "singlefile.lazyTimeout.clearTimeout", type });
  918. } catch (error) {
  919. clearRegularTimeout(type);
  920. }
  921. } else {
  922. clearRegularTimeout(type);
  923. }
  924. }
  925. function clearRegularTimeout(type) {
  926. const previousTimeoutId = timeouts.get(type);
  927. timeouts.delete(type);
  928. if (previousTimeoutId) {
  929. globalThis.clearTimeout(previousTimeoutId);
  930. }
  931. }
  932. /*
  933. * Copyright 2010-2022 Gildas Lormeau
  934. * contact : gildas.lormeau <at> gmail.com
  935. *
  936. * This file is part of SingleFile.
  937. *
  938. * The code in this file is free software: you can redistribute it and/or
  939. * modify it under the terms of the GNU Affero General Public License
  940. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  941. * of the License, or (at your option) any later version.
  942. *
  943. * The code in this file is distributed in the hope that it will be useful,
  944. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  945. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  946. * General Public License for more details.
  947. *
  948. * As additional permission under GNU AGPL version 3 section 7, you may
  949. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  950. * AGPL normally required by section 4, provided you include this license
  951. * notice and a URL through which recipients can access the Corresponding
  952. * Source.
  953. */
  954. const helper$1 = {
  955. ON_BEFORE_CAPTURE_EVENT_NAME,
  956. ON_AFTER_CAPTURE_EVENT_NAME,
  957. WIN_ID_ATTRIBUTE_NAME,
  958. WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
  959. preProcessDoc,
  960. serialize: serialize$1,
  961. postProcessDoc,
  962. getShadowRoot
  963. };
  964. const FRAMES_CSS_SELECTOR = "iframe, frame, object[type=\"text/html\"][data]";
  965. const ALL_ELEMENTS_CSS_SELECTOR = "*";
  966. const INIT_REQUEST_MESSAGE = "singlefile.frameTree.initRequest";
  967. const ACK_INIT_REQUEST_MESSAGE = "singlefile.frameTree.ackInitRequest";
  968. const CLEANUP_REQUEST_MESSAGE = "singlefile.frameTree.cleanupRequest";
  969. const INIT_RESPONSE_MESSAGE = "singlefile.frameTree.initResponse";
  970. const TARGET_ORIGIN = "*";
  971. const TIMEOUT_INIT_REQUEST_MESSAGE = 5000;
  972. const TIMEOUT_INIT_RESPONSE_MESSAGE = 10000;
  973. const TOP_WINDOW_ID = "0";
  974. const WINDOW_ID_SEPARATOR = ".";
  975. const TOP_WINDOW = globalThis.window == globalThis.top;
  976. const browser = globalThis.browser;
  977. const top = globalThis.top;
  978. const MessageChannel = globalThis.MessageChannel;
  979. const document = globalThis.document;
  980. const JSON = globalThis.JSON;
  981. const MutationObserver = globalThis.MutationObserver;
  982. let sessions = globalThis.sessions;
  983. if (!sessions) {
  984. sessions = globalThis.sessions = new Map();
  985. }
  986. let windowId;
  987. if (TOP_WINDOW) {
  988. windowId = TOP_WINDOW_ID;
  989. if (browser && browser.runtime && browser.runtime.onMessage && browser.runtime.onMessage.addListener) {
  990. browser.runtime.onMessage.addListener(message => {
  991. if (message.method == INIT_RESPONSE_MESSAGE) {
  992. initResponse(message);
  993. return Promise.resolve({});
  994. } else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
  995. clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
  996. createFrameResponseTimeout(message.sessionId, message.windowId);
  997. return Promise.resolve({});
  998. }
  999. });
  1000. }
  1001. }
  1002. init();
  1003. new MutationObserver(init).observe(document, { childList: true });
  1004. function init() {
  1005. globalThis.addEventListener("message", async event => {
  1006. if (typeof event.data == "string" && event.data.startsWith(MESSAGE_PREFIX)) {
  1007. event.preventDefault();
  1008. event.stopPropagation();
  1009. const message = JSON.parse(event.data.substring(MESSAGE_PREFIX.length));
  1010. if (message.method == INIT_REQUEST_MESSAGE) {
  1011. if (event.source) {
  1012. sendMessage(event.source, { method: ACK_INIT_REQUEST_MESSAGE, windowId: message.windowId, sessionId: message.sessionId });
  1013. }
  1014. if (!TOP_WINDOW) {
  1015. globalThis.stop();
  1016. if (message.options.loadDeferredImages) {
  1017. process$1(message.options);
  1018. }
  1019. await initRequestAsync(message);
  1020. }
  1021. } else if (message.method == ACK_INIT_REQUEST_MESSAGE) {
  1022. clearFrameTimeout("requestTimeouts", message.sessionId, message.windowId);
  1023. createFrameResponseTimeout(message.sessionId, message.windowId);
  1024. } else if (message.method == CLEANUP_REQUEST_MESSAGE) {
  1025. cleanupRequest(message);
  1026. } else if (message.method == INIT_RESPONSE_MESSAGE && sessions.get(message.sessionId)) {
  1027. const port = event.ports[0];
  1028. port.onmessage = event => initResponse(event.data);
  1029. }
  1030. }
  1031. }, true);
  1032. }
  1033. function getAsync(options) {
  1034. const sessionId = getNewSessionId();
  1035. options = JSON.parse(JSON.stringify(options));
  1036. return new Promise(resolve => {
  1037. sessions.set(sessionId, {
  1038. frames: [],
  1039. requestTimeouts: {},
  1040. responseTimeouts: {},
  1041. resolve: frames => {
  1042. frames.sessionId = sessionId;
  1043. resolve(frames);
  1044. }
  1045. });
  1046. initRequestAsync({ windowId, sessionId, options });
  1047. });
  1048. }
  1049. function getSync(options) {
  1050. const sessionId = getNewSessionId();
  1051. options = JSON.parse(JSON.stringify(options));
  1052. sessions.set(sessionId, {
  1053. frames: [],
  1054. requestTimeouts: {},
  1055. responseTimeouts: {}
  1056. });
  1057. initRequestSync({ windowId, sessionId, options });
  1058. const frames = sessions.get(sessionId).frames;
  1059. frames.sessionId = sessionId;
  1060. return frames;
  1061. }
  1062. function cleanup(sessionId) {
  1063. sessions.delete(sessionId);
  1064. cleanupRequest({ windowId, sessionId, options: { sessionId } });
  1065. }
  1066. function getNewSessionId() {
  1067. return globalThis.crypto.getRandomValues(new Uint32Array(32)).join("");
  1068. }
  1069. function initRequestSync(message) {
  1070. const sessionId = message.sessionId;
  1071. const waitForUserScript = globalThis[helper$1.WAIT_FOR_USERSCRIPT_PROPERTY_NAME];
  1072. delete globalThis._singleFile_cleaningUp;
  1073. if (!TOP_WINDOW) {
  1074. windowId = globalThis.frameId = message.windowId;
  1075. }
  1076. processFrames(document, message.options, windowId, sessionId);
  1077. if (!TOP_WINDOW) {
  1078. if (message.options.userScriptEnabled && waitForUserScript) {
  1079. waitForUserScript(helper$1.ON_BEFORE_CAPTURE_EVENT_NAME);
  1080. }
  1081. sendInitResponse({ frames: [getFrameData(document, globalThis, windowId, message.options, message.scrolling)], sessionId, requestedFrameId: document.documentElement.dataset.requestedFrameId && windowId });
  1082. if (message.options.userScriptEnabled && waitForUserScript) {
  1083. waitForUserScript(helper$1.ON_AFTER_CAPTURE_EVENT_NAME);
  1084. }
  1085. delete document.documentElement.dataset.requestedFrameId;
  1086. }
  1087. }
  1088. async function initRequestAsync(message) {
  1089. const sessionId = message.sessionId;
  1090. const waitForUserScript = globalThis[helper$1.WAIT_FOR_USERSCRIPT_PROPERTY_NAME];
  1091. delete globalThis._singleFile_cleaningUp;
  1092. if (!TOP_WINDOW) {
  1093. windowId = globalThis.frameId = message.windowId;
  1094. }
  1095. processFrames(document, message.options, windowId, sessionId);
  1096. if (!TOP_WINDOW) {
  1097. if (message.options.userScriptEnabled && waitForUserScript) {
  1098. await waitForUserScript(helper$1.ON_BEFORE_CAPTURE_EVENT_NAME);
  1099. }
  1100. sendInitResponse({ frames: [getFrameData(document, globalThis, windowId, message.options, message.scrolling)], sessionId, requestedFrameId: document.documentElement.dataset.requestedFrameId && windowId });
  1101. if (message.options.userScriptEnabled && waitForUserScript) {
  1102. await waitForUserScript(helper$1.ON_AFTER_CAPTURE_EVENT_NAME);
  1103. }
  1104. delete document.documentElement.dataset.requestedFrameId;
  1105. }
  1106. }
  1107. function cleanupRequest(message) {
  1108. if (!globalThis._singleFile_cleaningUp) {
  1109. globalThis._singleFile_cleaningUp = true;
  1110. const sessionId = message.sessionId;
  1111. cleanupFrames(getFrames(document), message.windowId, sessionId);
  1112. }
  1113. }
  1114. function initResponse(message) {
  1115. message.frames.forEach(frameData => clearFrameTimeout("responseTimeouts", message.sessionId, frameData.windowId));
  1116. const windowData = sessions.get(message.sessionId);
  1117. if (windowData) {
  1118. if (message.requestedFrameId) {
  1119. windowData.requestedFrameId = message.requestedFrameId;
  1120. }
  1121. message.frames.forEach(messageFrameData => {
  1122. let frameData = windowData.frames.find(frameData => messageFrameData.windowId == frameData.windowId);
  1123. if (!frameData) {
  1124. frameData = { windowId: messageFrameData.windowId };
  1125. windowData.frames.push(frameData);
  1126. }
  1127. if (!frameData.processed) {
  1128. frameData.content = messageFrameData.content;
  1129. frameData.baseURI = messageFrameData.baseURI;
  1130. frameData.title = messageFrameData.title;
  1131. frameData.url = messageFrameData.url;
  1132. frameData.canvases = messageFrameData.canvases;
  1133. frameData.fonts = messageFrameData.fonts;
  1134. frameData.stylesheets = messageFrameData.stylesheets;
  1135. frameData.images = messageFrameData.images;
  1136. frameData.posters = messageFrameData.posters;
  1137. frameData.videos = messageFrameData.videos;
  1138. frameData.usedFonts = messageFrameData.usedFonts;
  1139. frameData.shadowRoots = messageFrameData.shadowRoots;
  1140. frameData.processed = messageFrameData.processed;
  1141. frameData.scrollPosition = messageFrameData.scrollPosition;
  1142. frameData.scrolling = messageFrameData.scrolling;
  1143. frameData.adoptedStyleSheets = messageFrameData.adoptedStyleSheets;
  1144. }
  1145. });
  1146. const remainingFrames = windowData.frames.filter(frameData => !frameData.processed).length;
  1147. if (!remainingFrames) {
  1148. windowData.frames = windowData.frames.sort((frame1, frame2) => frame2.windowId.split(WINDOW_ID_SEPARATOR).length - frame1.windowId.split(WINDOW_ID_SEPARATOR).length);
  1149. if (windowData.resolve) {
  1150. if (windowData.requestedFrameId) {
  1151. windowData.frames.forEach(frameData => {
  1152. if (frameData.windowId == windowData.requestedFrameId) {
  1153. frameData.requestedFrame = true;
  1154. }
  1155. });
  1156. }
  1157. windowData.resolve(windowData.frames);
  1158. }
  1159. }
  1160. }
  1161. }
  1162. function processFrames(doc, options, parentWindowId, sessionId) {
  1163. const frameElements = getFrames(doc);
  1164. processFramesAsync(doc, frameElements, options, parentWindowId, sessionId);
  1165. if (frameElements.length) {
  1166. processFramesSync(doc, frameElements, options, parentWindowId, sessionId);
  1167. }
  1168. }
  1169. function processFramesAsync(doc, frameElements, options, parentWindowId, sessionId) {
  1170. const frames = [];
  1171. let requestTimeouts;
  1172. if (sessions.get(sessionId)) {
  1173. requestTimeouts = sessions.get(sessionId).requestTimeouts;
  1174. } else {
  1175. requestTimeouts = {};
  1176. sessions.set(sessionId, { requestTimeouts });
  1177. }
  1178. frameElements.forEach((frameElement, frameIndex) => {
  1179. const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
  1180. frameElement.setAttribute(helper$1.WIN_ID_ATTRIBUTE_NAME, windowId);
  1181. frames.push({ windowId });
  1182. });
  1183. sendInitResponse({ frames, sessionId, requestedFrameId: doc.documentElement.dataset.requestedFrameId && parentWindowId });
  1184. frameElements.forEach((frameElement, frameIndex) => {
  1185. const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
  1186. try {
  1187. sendMessage(frameElement.contentWindow, { method: INIT_REQUEST_MESSAGE, windowId, sessionId, options, scrolling: frameElement.scrolling });
  1188. } catch (error) {
  1189. // ignored
  1190. }
  1191. requestTimeouts[windowId] = globalThis.setTimeout(() => sendInitResponse({ frames: [{ windowId, processed: true }], sessionId }), TIMEOUT_INIT_REQUEST_MESSAGE);
  1192. });
  1193. delete doc.documentElement.dataset.requestedFrameId;
  1194. }
  1195. function processFramesSync(doc, frameElements, options, parentWindowId, sessionId) {
  1196. const frames = [];
  1197. frameElements.forEach((frameElement, frameIndex) => {
  1198. const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
  1199. let frameDoc;
  1200. try {
  1201. frameDoc = frameElement.contentDocument;
  1202. } catch (error) {
  1203. // ignored
  1204. }
  1205. if (frameDoc) {
  1206. try {
  1207. const frameWindow = frameElement.contentWindow;
  1208. frameWindow.stop();
  1209. clearFrameTimeout("requestTimeouts", sessionId, windowId);
  1210. processFrames(frameDoc, options, windowId, sessionId);
  1211. frames.push(getFrameData(frameDoc, frameWindow, windowId, options, frameElement.scrolling));
  1212. } catch (error) {
  1213. frames.push({ windowId, processed: true });
  1214. }
  1215. }
  1216. });
  1217. sendInitResponse({ frames, sessionId, requestedFrameId: doc.documentElement.dataset.requestedFrameId && parentWindowId });
  1218. delete doc.documentElement.dataset.requestedFrameId;
  1219. }
  1220. function clearFrameTimeout(type, sessionId, windowId) {
  1221. const session = sessions.get(sessionId);
  1222. if (session && session[type]) {
  1223. const timeout = session[type][windowId];
  1224. if (timeout) {
  1225. globalThis.clearTimeout(timeout);
  1226. delete session[type][windowId];
  1227. }
  1228. }
  1229. }
  1230. function createFrameResponseTimeout(sessionId, windowId) {
  1231. const session = sessions.get(sessionId);
  1232. if (session && session.responseTimeouts) {
  1233. session.responseTimeouts[windowId] = globalThis.setTimeout(() => sendInitResponse({ frames: [{ windowId: windowId, processed: true }], sessionId: sessionId }), TIMEOUT_INIT_RESPONSE_MESSAGE);
  1234. }
  1235. }
  1236. function cleanupFrames(frameElements, parentWindowId, sessionId) {
  1237. frameElements.forEach((frameElement, frameIndex) => {
  1238. const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
  1239. frameElement.removeAttribute(helper$1.WIN_ID_ATTRIBUTE_NAME);
  1240. try {
  1241. sendMessage(frameElement.contentWindow, { method: CLEANUP_REQUEST_MESSAGE, windowId, sessionId });
  1242. } catch (error) {
  1243. // ignored
  1244. }
  1245. });
  1246. frameElements.forEach((frameElement, frameIndex) => {
  1247. const windowId = parentWindowId + WINDOW_ID_SEPARATOR + frameIndex;
  1248. let frameDoc;
  1249. try {
  1250. frameDoc = frameElement.contentDocument;
  1251. } catch (error) {
  1252. // ignored
  1253. }
  1254. if (frameDoc) {
  1255. try {
  1256. cleanupFrames(getFrames(frameDoc), windowId, sessionId);
  1257. } catch (error) {
  1258. // ignored
  1259. }
  1260. }
  1261. });
  1262. }
  1263. function sendInitResponse(message) {
  1264. message.method = INIT_RESPONSE_MESSAGE;
  1265. try {
  1266. top.singlefile.processors.frameTree.initResponse(message);
  1267. } catch (error) {
  1268. sendMessage(top, message, true);
  1269. }
  1270. }
  1271. function sendMessage(targetWindow, message, useChannel) {
  1272. if (targetWindow == top && browser && browser.runtime && browser.runtime.sendMessage) {
  1273. browser.runtime.sendMessage(message);
  1274. } else {
  1275. if (useChannel) {
  1276. const channel = new MessageChannel();
  1277. targetWindow.postMessage(MESSAGE_PREFIX + JSON.stringify({ method: message.method, sessionId: message.sessionId }), TARGET_ORIGIN, [channel.port2]);
  1278. channel.port1.postMessage(message);
  1279. } else {
  1280. targetWindow.postMessage(MESSAGE_PREFIX + JSON.stringify(message), TARGET_ORIGIN);
  1281. }
  1282. }
  1283. }
  1284. function getFrameData(document, globalThis, windowId, options, scrolling) {
  1285. const docData = helper$1.preProcessDoc(document, globalThis, options);
  1286. const content = helper$1.serialize(document);
  1287. helper$1.postProcessDoc(document, docData.markedElements, docData.invalidElements);
  1288. const baseURI = document.baseURI.split("#")[0];
  1289. return {
  1290. windowId,
  1291. content,
  1292. baseURI,
  1293. url: document.documentURI,
  1294. title: document.title,
  1295. canvases: docData.canvases,
  1296. fonts: docData.fonts,
  1297. stylesheets: docData.stylesheets,
  1298. images: docData.images,
  1299. posters: docData.posters,
  1300. videos: docData.videos,
  1301. usedFonts: docData.usedFonts,
  1302. shadowRoots: docData.shadowRoots,
  1303. scrollPosition: docData.scrollPosition,
  1304. scrolling,
  1305. adoptedStyleSheets: docData.adoptedStyleSheets,
  1306. processed: true
  1307. };
  1308. }
  1309. function getFrames(document) {
  1310. let frames = Array.from(document.querySelectorAll(FRAMES_CSS_SELECTOR));
  1311. document.querySelectorAll(ALL_ELEMENTS_CSS_SELECTOR).forEach(element => {
  1312. const shadowRoot = helper$1.getShadowRoot(element);
  1313. if (shadowRoot) {
  1314. frames = frames.concat(...shadowRoot.querySelectorAll(FRAMES_CSS_SELECTOR));
  1315. }
  1316. });
  1317. return frames;
  1318. }
  1319. var frameTree = /*#__PURE__*/Object.freeze({
  1320. __proto__: null,
  1321. getAsync: getAsync,
  1322. getSync: getSync,
  1323. cleanup: cleanup,
  1324. initResponse: initResponse,
  1325. TIMEOUT_INIT_REQUEST_MESSAGE: TIMEOUT_INIT_REQUEST_MESSAGE
  1326. });
  1327. /*
  1328. * Copyright 2010-2022 Gildas Lormeau
  1329. * contact : gildas.lormeau <at> gmail.com
  1330. *
  1331. * This file is part of SingleFile.
  1332. *
  1333. * The code in this file is free software: you can redistribute it and/or
  1334. * modify it under the terms of the GNU Affero General Public License
  1335. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  1336. * of the License, or (at your option) any later version.
  1337. *
  1338. * The code in this file is distributed in the hope that it will be useful,
  1339. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1340. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  1341. * General Public License for more details.
  1342. *
  1343. * As additional permission under GNU AGPL version 3 section 7, you may
  1344. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  1345. * AGPL normally required by section 4, provided you include this license
  1346. * notice and a URL through which recipients can access the Corresponding
  1347. * Source.
  1348. */
  1349. const SELF_CLOSED_TAG_NAMES = ["AREA", "BASE", "BR", "COL", "COMMAND", "EMBED", "HR", "IMG", "INPUT", "KEYGEN", "LINK", "META", "PARAM", "SOURCE", "TRACK", "WBR"];
  1350. const Node_ELEMENT_NODE = 1;
  1351. const Node_TEXT_NODE = 3;
  1352. const Node_COMMENT_NODE = 8;
  1353. // see https://www.w3.org/TR/html5/syntax.html#optional-tags
  1354. const OMITTED_START_TAGS = [
  1355. { tagName: "HEAD", accept: element => !element.childNodes.length || element.childNodes[0].nodeType == Node_ELEMENT_NODE },
  1356. { tagName: "BODY", accept: element => !element.childNodes.length }
  1357. ];
  1358. const OMITTED_END_TAGS = [
  1359. { tagName: "HTML", accept: next => !next || next.nodeType != Node_COMMENT_NODE },
  1360. { tagName: "HEAD", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
  1361. { tagName: "BODY", accept: next => !next || next.nodeType != Node_COMMENT_NODE },
  1362. { tagName: "LI", accept: (next, element) => (!next && element.parentElement && (getTagName(element.parentElement) == "UL" || getTagName(element.parentElement) == "OL")) || (next && ["LI"].includes(getTagName(next))) },
  1363. { tagName: "DT", accept: next => !next || ["DT", "DD"].includes(getTagName(next)) },
  1364. { tagName: "P", accept: next => next && ["ADDRESS", "ARTICLE", "ASIDE", "BLOCKQUOTE", "DETAILS", "DIV", "DL", "FIELDSET", "FIGCAPTION", "FIGURE", "FOOTER", "FORM", "H1", "H2", "H3", "H4", "H5", "H6", "HEADER", "HR", "MAIN", "NAV", "OL", "P", "PRE", "SECTION", "TABLE", "UL"].includes(getTagName(next)) },
  1365. { tagName: "DD", accept: next => !next || ["DT", "DD"].includes(getTagName(next)) },
  1366. { tagName: "RT", accept: next => !next || ["RT", "RP"].includes(getTagName(next)) },
  1367. { tagName: "RP", accept: next => !next || ["RT", "RP"].includes(getTagName(next)) },
  1368. { tagName: "OPTGROUP", accept: next => !next || ["OPTGROUP"].includes(getTagName(next)) },
  1369. { tagName: "OPTION", accept: next => !next || ["OPTION", "OPTGROUP"].includes(getTagName(next)) },
  1370. { tagName: "COLGROUP", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
  1371. { tagName: "CAPTION", accept: next => !next || (next.nodeType != Node_COMMENT_NODE && (next.nodeType != Node_TEXT_NODE || !startsWithSpaceChar(next.textContent))) },
  1372. { tagName: "THEAD", accept: next => !next || ["TBODY", "TFOOT"].includes(getTagName(next)) },
  1373. { tagName: "TBODY", accept: next => !next || ["TBODY", "TFOOT"].includes(getTagName(next)) },
  1374. { tagName: "TFOOT", accept: next => !next },
  1375. { tagName: "TR", accept: next => !next || ["TR"].includes(getTagName(next)) },
  1376. { tagName: "TD", accept: next => !next || ["TD", "TH"].includes(getTagName(next)) },
  1377. { tagName: "TH", accept: next => !next || ["TD", "TH"].includes(getTagName(next)) }
  1378. ];
  1379. const TEXT_NODE_TAGS = ["STYLE", "SCRIPT", "XMP", "IFRAME", "NOEMBED", "NOFRAMES", "PLAINTEXT", "NOSCRIPT"];
  1380. function process(doc, compressHTML) {
  1381. const docType = doc.doctype;
  1382. let docTypeString = "";
  1383. if (docType) {
  1384. docTypeString = "<!DOCTYPE " + docType.nodeName;
  1385. if (docType.publicId) {
  1386. docTypeString += " PUBLIC \"" + docType.publicId + "\"";
  1387. if (docType.systemId)
  1388. docTypeString += " \"" + docType.systemId + "\"";
  1389. } else if (docType.systemId)
  1390. docTypeString += " SYSTEM \"" + docType.systemId + "\"";
  1391. if (docType.internalSubset)
  1392. docTypeString += " [" + docType.internalSubset + "]";
  1393. docTypeString += "> ";
  1394. }
  1395. return docTypeString + serialize(doc.documentElement, compressHTML);
  1396. }
  1397. function serialize(node, compressHTML, isSVG) {
  1398. if (node.nodeType == Node_TEXT_NODE) {
  1399. return serializeTextNode(node);
  1400. } else if (node.nodeType == Node_COMMENT_NODE) {
  1401. return serializeCommentNode(node);
  1402. } else if (node.nodeType == Node_ELEMENT_NODE) {
  1403. return serializeElement(node, compressHTML, isSVG);
  1404. }
  1405. }
  1406. function serializeTextNode(textNode) {
  1407. const parentNode = textNode.parentNode;
  1408. let parentTagName;
  1409. if (parentNode && parentNode.nodeType == Node_ELEMENT_NODE) {
  1410. parentTagName = getTagName(parentNode);
  1411. }
  1412. if (!parentTagName || TEXT_NODE_TAGS.includes(parentTagName)) {
  1413. if (parentTagName == "SCRIPT" || parentTagName == "STYLE") {
  1414. return textNode.textContent.replace(/<\//gi, "<\\/").replace(/\/>/gi, "\\/>");
  1415. }
  1416. return textNode.textContent;
  1417. } else {
  1418. return textNode.textContent.replace(/&/g, "&amp;").replace(/\u00a0/g, "&nbsp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  1419. }
  1420. }
  1421. function serializeCommentNode(commentNode) {
  1422. return "<!--" + commentNode.textContent + "-->";
  1423. }
  1424. function serializeElement(element, compressHTML, isSVG) {
  1425. const tagName = getTagName(element);
  1426. const omittedStartTag = compressHTML && OMITTED_START_TAGS.find(omittedStartTag => tagName == getTagName(omittedStartTag) && omittedStartTag.accept(element));
  1427. let content = "";
  1428. if (!omittedStartTag || element.attributes.length) {
  1429. content = "<" + tagName.toLowerCase();
  1430. Array.from(element.attributes).forEach(attribute => content += serializeAttribute(attribute, element, compressHTML));
  1431. content += ">";
  1432. }
  1433. if (tagName == "TEMPLATE" && !element.childNodes.length) {
  1434. content += element.innerHTML;
  1435. } else {
  1436. Array.from(element.childNodes).forEach(childNode => content += serialize(childNode, compressHTML, isSVG || tagName == "svg"));
  1437. }
  1438. const omittedEndTag = compressHTML && OMITTED_END_TAGS.find(omittedEndTag => tagName == getTagName(omittedEndTag) && omittedEndTag.accept(element.nextSibling, element));
  1439. if (isSVG || (!omittedEndTag && !SELF_CLOSED_TAG_NAMES.includes(tagName))) {
  1440. content += "</" + tagName.toLowerCase() + ">";
  1441. }
  1442. return content;
  1443. }
  1444. function serializeAttribute(attribute, element, compressHTML) {
  1445. const name = attribute.name;
  1446. let content = "";
  1447. if (!name.match(/["'>/=]/)) {
  1448. let value = attribute.value;
  1449. if (compressHTML && name == "class") {
  1450. value = Array.from(element.classList).map(className => className.trim()).join(" ");
  1451. }
  1452. let simpleQuotesValue;
  1453. value = value.replace(/&/g, "&amp;").replace(/\u00a0/g, "&nbsp;");
  1454. if (value.includes("\"")) {
  1455. if (value.includes("'") || !compressHTML) {
  1456. value = value.replace(/"/g, "&quot;");
  1457. } else {
  1458. simpleQuotesValue = true;
  1459. }
  1460. }
  1461. const invalidUnquotedValue = !compressHTML || value.match(/[ \t\n\f\r'"`=<>]/);
  1462. content += " ";
  1463. if (!attribute.namespace) {
  1464. content += name;
  1465. } else if (attribute.namespaceURI == "http://www.w3.org/XML/1998/namespace") {
  1466. content += "xml:" + name;
  1467. } else if (attribute.namespaceURI == "http://www.w3.org/2000/xmlns/") {
  1468. if (name !== "xmlns") {
  1469. content += "xmlns:";
  1470. }
  1471. content += name;
  1472. } else if (attribute.namespaceURI == "http://www.w3.org/1999/xlink") {
  1473. content += "xlink:" + name;
  1474. } else {
  1475. content += name;
  1476. }
  1477. if (value != "") {
  1478. content += "=";
  1479. if (invalidUnquotedValue) {
  1480. content += simpleQuotesValue ? "'" : "\"";
  1481. }
  1482. content += value;
  1483. if (invalidUnquotedValue) {
  1484. content += simpleQuotesValue ? "'" : "\"";
  1485. }
  1486. }
  1487. }
  1488. return content;
  1489. }
  1490. function startsWithSpaceChar(textContent) {
  1491. return Boolean(textContent.match(/^[ \t\n\f\r]/));
  1492. }
  1493. function getTagName(element) {
  1494. return element.tagName && element.tagName.toUpperCase();
  1495. }
  1496. /*
  1497. * Copyright 2010-2022 Gildas Lormeau
  1498. * contact : gildas.lormeau <at> gmail.com
  1499. *
  1500. * This file is part of SingleFile.
  1501. *
  1502. * The code in this file is free software: you can redistribute it and/or
  1503. * modify it under the terms of the GNU Affero General Public License
  1504. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  1505. * of the License, or (at your option) any later version.
  1506. *
  1507. * The code in this file is distributed in the hope that it will be useful,
  1508. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1509. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  1510. * General Public License for more details.
  1511. *
  1512. * As additional permission under GNU AGPL version 3 section 7, you may
  1513. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  1514. * AGPL normally required by section 4, provided you include this license
  1515. * notice and a URL through which recipients can access the Corresponding
  1516. * Source.
  1517. */
  1518. const processors = { frameTree };
  1519. const helper = {
  1520. COMMENT_HEADER,
  1521. COMMENT_HEADER_LEGACY,
  1522. ON_BEFORE_CAPTURE_EVENT_NAME,
  1523. ON_AFTER_CAPTURE_EVENT_NAME,
  1524. WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
  1525. preProcessDoc,
  1526. postProcessDoc,
  1527. serialize(doc, compressHTML) {
  1528. return process(doc, compressHTML);
  1529. },
  1530. getShadowRoot
  1531. };
  1532. initUserScriptHandler();
  1533. exports.helper = helper;
  1534. exports.processors = processors;
  1535. Object.defineProperty(exports, '__esModule', { value: true });
  1536. }));