core.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Copyright 2010 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * SingleFile is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * SingleFile 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
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with SingleFile. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. (function(holder) {
  21. var targetDoc, doc, options, winID, processedFrames = 0, initPageCallback, timeoutCallback, bgPort, winPort, requests = {}, requestCount = 0, responseCount = 0, sendContent, scrapbooking;
  22. function storeRequest(url, callback, base64, encodedText, characterSet) {
  23. if (!requests[url]) {
  24. requests[url] = {
  25. base64 : base64,
  26. encodedText : encodedText,
  27. characterSet : characterSet,
  28. callbacks : []
  29. };
  30. requestCount++;
  31. }
  32. requests[url].callbacks.push(callback);
  33. }
  34. function sendRequests(methodName) {
  35. var url, data, msg;
  36. for (url in requests) {
  37. data = requests[url];
  38. msg = {
  39. data : {
  40. url : url,
  41. base64 : data.base64,
  42. encodedText : data.encodedText,
  43. characterSet : data.base64 ? "x-user-defined" : data.characterSet
  44. }
  45. };
  46. msg[methodName] = true;
  47. bgPort.postMessage(msg);
  48. }
  49. }
  50. function getDocument() {
  51. var clone, mask;
  52. if (options.removeScripts || scrapbooking) {
  53. clone = targetDoc.documentElement.cloneNode(true);
  54. mask = clone.querySelector("#__SingleFile_mask__");
  55. if (mask)
  56. mask.parentElement.removeChild(mask);
  57. return clone;
  58. }
  59. return targetDoc.documentElement;
  60. }
  61. function setDocument() {
  62. if (options.removeScripts && !scrapbooking)
  63. targetDoc.replaceChild(doc, targetDoc.documentElement);
  64. }
  65. function initProcess(opt) {
  66. options = opt;
  67. if (options.removeScripts || scrapbooking)
  68. holder.filters.canvas.replace();
  69. if (options.removeHidden)
  70. holder.filters.element.removeHidden();
  71. doc = getDocument();
  72. holder.filters.element.clean(doc);
  73. if (options.removeFrames)
  74. holder.filters.frame.remove(doc);
  75. if (options.removeScripts || scrapbooking)
  76. holder.filters.script.remove(doc);
  77. if (options.removeObjects)
  78. holder.filters.object.remove(doc);
  79. holder.filters.a.setAbsolute(doc);
  80. }
  81. function getStylesheets() {
  82. holder.filters.document.getStylesheets(doc, storeRequest);
  83. sendRequests("setStylesheets");
  84. if (requestCount == 0) {
  85. holder.filters.document.get(doc, storeRequest, window == top);
  86. bgPort.postMessage({
  87. setTotalResources : true,
  88. totalResources : requestCount
  89. });
  90. }
  91. }
  92. function getData() {
  93. sendRequests("setData");
  94. if (requestCount == 0)
  95. bgPort.postMessage({
  96. setDataDone : true,
  97. data : window == top && options.removeFrames && sendContent ? holder.filters.document.getDoctype() + doc.outerHTML : null,
  98. favicoData : window == top && options.removeFrames && sendContent ? holder.filters.image.getFavicoData(doc) : null,
  99. frameCount : holder.filters.frame.count(doc),
  100. winID : winID
  101. });
  102. }
  103. function setData(data, callback, isStylesheet) {
  104. var callbacks = requests[data.url].callbacks;
  105. responseCount++;
  106. callbacks.forEach(function(cb) {
  107. cb(data);
  108. });
  109. if (!isStylesheet)
  110. bgPort.postMessage({
  111. incProcessedResources : true
  112. });
  113. if (responseCount == requestCount) {
  114. responseCount = 0;
  115. requestCount = 0;
  116. requests = {};
  117. if (callback)
  118. callback();
  119. }
  120. }
  121. function getDocData(data) {
  122. if (options.removeUnused) {
  123. setDocument();
  124. holder.filters.style.removeUnused();
  125. doc = getDocument();
  126. }
  127. holder.filters.frame.set(doc, data);
  128. bgPort.postMessage({
  129. setDocData : true,
  130. data : window != top || sendContent ? holder.filters.document.getDoctype() + doc.outerHTML : null,
  131. favicoData : window == top && sendContent ? holder.filters.image.getFavicoData(doc) : null,
  132. mimeType : "text/html"
  133. });
  134. }
  135. function done(winProperties) {
  136. var callbackId;
  137. setDocument();
  138. targetDoc.addEventListener("DOMNodeInsertedIntoDocument", function(event) {
  139. if (!callbackId)
  140. if (event.target.querySelectorAll)
  141. holder.filters.document.get(event.target, storeRequest, false);
  142. if (callbackId)
  143. clearTimeout(callbackId);
  144. callbackId = setTimeout(function() {
  145. if (requestCount)
  146. sendRequests("setDynamicData");
  147. callbackId = null;
  148. }, 20);
  149. }, true);
  150. bgPort.postMessage({
  151. done : true
  152. });
  153. if (options.removeScripts && !scrapbooking) {
  154. function resetWindowProperties(winPropertiesStr) {
  155. var property, winProp = JSON.parse(winPropertiesStr);
  156. for (property in window)
  157. if (!winProp[property])
  158. window[property] = null;
  159. }
  160. window.location.href = "javascript:(" + resetWindowProperties.toString() + ")('" + JSON.stringify(winProperties) + "')";
  161. }
  162. }
  163. function start(msg) {
  164. sendContent = msg.sendContent;
  165. scrapbooking = msg.scrapbooking;
  166. document.documentElement.insertBefore(document.createComment("\n Archive processed by SingleFile \n url: " + document.location.href + " \n saved date: " + new Date() + " \n"),
  167. document.documentElement.firstChild);
  168. timeoutCallback = setTimeout(initPageCallback, 1000);
  169. setWinId("0");
  170. }
  171. function setWinId(id) {
  172. winID = id;
  173. holder.filters.frame.clean();
  174. if (holder.filters.frame.count())
  175. winPort.postMessageToFrames('iframe[src], frame[src]', function(index, params) {
  176. return {
  177. setID : true,
  178. winID : params.winID + "." + index
  179. };
  180. }, {
  181. winID : id
  182. });
  183. else
  184. setWinIdDone();
  185. }
  186. function setWinIdDone() {
  187. processedFrames++;
  188. if (processedFrames == holder.filters.frame.count()) {
  189. if (top != window)
  190. winPort.postMessageToParent({
  191. done : true
  192. });
  193. else {
  194. clearTimeout(timeoutCallback);
  195. initPageCallback();
  196. }
  197. }
  198. }
  199. holder.core = {
  200. init : function(srcDoc, backgroundPort, windowPort) {
  201. windowPort.addListener(function(message) {
  202. if (message.setID)
  203. setWinId(message.winID);
  204. else if (message.done)
  205. setWinIdDone();
  206. });
  207. bgPort = backgroundPort;
  208. winPort = windowPort;
  209. initPageCallback = function() {
  210. bgPort.postMessage({
  211. startDone : true
  212. });
  213. };
  214. bgPort.addListener(function(message) {
  215. if (message.start)
  216. start(message);
  217. else if (message.getStylesheets) {
  218. initProcess(message.options);
  219. getStylesheets();
  220. } else if (message.setStylesheets)
  221. setData(message.data, getStylesheets, true);
  222. else if (message.getData)
  223. getData();
  224. else if (message.setData)
  225. setData(message.data, function() {
  226. bgPort.postMessage({
  227. setDataDone : true,
  228. data : window == top && sendContent ? holder.filters.document.getDoctype() + doc.outerHTML : null,
  229. favicoData : window == top && options.removeFrames && sendContent ? holder.filters.image.getFavicoData(doc) : null,
  230. frameCount : holder.filters.frame.count(doc),
  231. winID : winID
  232. });
  233. });
  234. else if (message.setDynamicData)
  235. setData(message.data, null, true);
  236. else if (message.getDocData)
  237. getDocData(message.data);
  238. else if (message.done)
  239. done(message.winProperties);
  240. });
  241. targetDoc = srcDoc;
  242. doc = targetDoc.documentElement;
  243. if (doc instanceof HTMLHtmlElement)
  244. bgPort.postMessage({
  245. init : true,
  246. topWindow : window == top,
  247. url : location.href,
  248. title : document.title
  249. });
  250. }
  251. };
  252. holder.init = function(currDoc, backgroundPort, windowPort) {
  253. holder.filters.init(currDoc);
  254. holder.ui.init(backgroundPort);
  255. holder.core.init(currDoc, backgroundPort, windowPort);
  256. };
  257. })(singlefile);