core.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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;
  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) {
  53. clone = targetDoc.documentElement.cloneNode(true);
  54. mask = clone.querySelector("#__SingleFile_mask__");
  55. if (mask) {
  56. clone.querySelector("body").removeChild(mask);
  57. return clone;
  58. }
  59. }
  60. return targetDoc.documentElement;
  61. }
  62. function setDocument() {
  63. if (options.removeScripts)
  64. targetDoc.replaceChild(doc, targetDoc.documentElement);
  65. }
  66. function initProcess(opt) {
  67. options = opt;
  68. if (options.removeHidden)
  69. holder.filters.element.removeHidden();
  70. doc = getDocument();
  71. holder.filters.element.clean(doc);
  72. if (options.removeFrames)
  73. holder.filters.frame.remove(doc);
  74. if (options.removeScripts)
  75. holder.filters.script.remove(doc);
  76. if (options.removeObjects)
  77. holder.filters.object.remove(doc);
  78. }
  79. function getStylesheets() {
  80. holder.filters.document.getStylesheets(doc, storeRequest);
  81. sendRequests("setStylesheets");
  82. if (requestCount == 0) {
  83. holder.filters.document.get(doc, storeRequest, window == top);
  84. bgPort.postMessage( {
  85. setTotalResources : true,
  86. totalResources : requestCount
  87. });
  88. }
  89. }
  90. function getData() {
  91. sendRequests("setData");
  92. if (requestCount == 0)
  93. bgPort.postMessage( {
  94. setDataDone : true,
  95. frameCount : holder.filters.frame.count(doc),
  96. winID : winID
  97. });
  98. }
  99. function setData(data, callback, isStylesheet) {
  100. var callbacks = requests[data.url].callbacks;
  101. responseCount++;
  102. callbacks.forEach(function(cb) {
  103. cb(data);
  104. });
  105. if (!isStylesheet)
  106. bgPort.postMessage( {
  107. incProcessedResources : true
  108. });
  109. if (responseCount == requestCount) {
  110. responseCount = 0;
  111. requestCount = 0;
  112. requests = {};
  113. if (callback)
  114. callback();
  115. }
  116. }
  117. function getDocData(data) {
  118. if (options.removeUnused) {
  119. setDocument();
  120. holder.filters.style.removeUnused();
  121. doc = getDocument();
  122. }
  123. holder.filters.frame.set(doc, data);
  124. bgPort.postMessage( {
  125. setDocData : true,
  126. data : window != top ? holder.filters.document.getDoctype() + doc.outerHTML : null,
  127. mimeType : "text/html"
  128. });
  129. }
  130. function done(winProperties) {
  131. var callbackId;
  132. setDocument();
  133. targetDoc.addEventListener("DOMNodeInsertedIntoDocument", function(event) {
  134. if (!callbackId)
  135. if (event.target.querySelectorAll)
  136. holder.filters.document.get(event.target, storeRequest, false);
  137. if (callbackId)
  138. clearTimeout(callbackId);
  139. callbackId = setTimeout(function() {
  140. if (requestCount)
  141. sendRequests("setDynamicData");
  142. callbackId = null;
  143. }, 20);
  144. }, true);
  145. bgPort.postMessage( {
  146. done : true
  147. });
  148. if (options.removeScripts) {
  149. function resetWindowProperties(winPropertiesStr) {
  150. var property, winProp = JSON.parse(winPropertiesStr);
  151. for (property in window)
  152. if (!winProp[property])
  153. window[property] = null;
  154. }
  155. window.location.href = "javascript:(" + resetWindowProperties.toString() + ")('" + JSON.stringify(winProperties) + "')";
  156. }
  157. }
  158. function start() {
  159. timeoutCallback = setTimeout(initPageCallback, 1000);
  160. setWinId("0");
  161. }
  162. function setWinId(id) {
  163. winID = id;
  164. holder.filters.frame.clean();
  165. if (holder.filters.frame.count())
  166. winPort.postMessageToFrames('iframe[src], frame[src]', function(index, params) {
  167. return {
  168. setID : true,
  169. winID : params.winID + "." + index
  170. };
  171. }, {
  172. winID : id
  173. });
  174. else
  175. setWinIdDone();
  176. }
  177. function setWinIdDone() {
  178. processedFrames++;
  179. if (processedFrames == holder.filters.frame.count()) {
  180. if (top != window)
  181. winPort.postMessageToParent( {
  182. done : true
  183. });
  184. else {
  185. clearTimeout(timeoutCallback);
  186. initPageCallback();
  187. }
  188. }
  189. }
  190. holder.core = {
  191. init : function(srcDoc, backgroundPort, windowPort) {
  192. windowPort.addListener(function(message) {
  193. if (message.setID)
  194. setWinId(message.winID);
  195. else if (message.done)
  196. setWinIdDone();
  197. });
  198. bgPort = backgroundPort;
  199. winPort = windowPort;
  200. initPageCallback = function() {
  201. bgPort.postMessage( {
  202. startDone : true
  203. });
  204. };
  205. bgPort.addListener(function(message) {
  206. if (message.start)
  207. start();
  208. else if (message.getStylesheets) {
  209. initProcess(message.options);
  210. getStylesheets();
  211. } else if (message.setStylesheets)
  212. setData(message.data, getStylesheets, true);
  213. else if (message.getData)
  214. getData();
  215. else if (message.setData)
  216. setData(message.data, function() {
  217. bgPort.postMessage( {
  218. setDataDone : true,
  219. frameCount : holder.filters.frame.count(doc),
  220. winID : winID
  221. });
  222. });
  223. else if (message.setDynamicData)
  224. setData(message.data, null, true);
  225. else if (message.getDocData)
  226. getDocData(message.data);
  227. else if (message.done)
  228. done(message.winProperties);
  229. });
  230. targetDoc = srcDoc;
  231. doc = targetDoc.documentElement;
  232. if (doc instanceof HTMLHtmlElement)
  233. bgPort.postMessage( {
  234. init : true,
  235. topWindow : window == top,
  236. url : location.href
  237. });
  238. }
  239. };
  240. holder.init = function(currDoc, backgroundPort, windowPort) {
  241. holder.filters.init(currDoc);
  242. holder.ui.init(backgroundPort);
  243. holder.core.init(currDoc, backgroundPort, windowPort);
  244. };
  245. })(singlefile);