bgcore.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright 2011 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile Core.
  6. *
  7. * SingleFile Core 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 Core 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 Core. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. (function() {
  21. singlefile.PageData = PageData;
  22. singlefile.DocData = DocData;
  23. function PageData(tabId, pageId, senderId, config, processSelection, callback) {
  24. var timeoutError, that = this;
  25. this.pageId = pageId;
  26. this.docs = [];
  27. this.processedDocs = 0;
  28. this.initializedDocs = 0;
  29. this.processableDocs = 0;
  30. this.senderId = senderId;
  31. this.config = config;
  32. this.processSelection = processSelection;
  33. this.processing = true;
  34. this.tabId = tabId;
  35. this.requestManager = new singlefile.nio.RequestManager();
  36. this.progressIndex = 0;
  37. this.progressMax = 0;
  38. this.title = null;
  39. this.url = null;
  40. this.top = null;
  41. this.timeoutPageInit = null;
  42. this.portsId = [];
  43. timeoutError = setTimeout(function() {
  44. that.processing = false;
  45. chrome.extension.sendRequest(that.senderId, {
  46. processError : true,
  47. tabId : tabId
  48. });
  49. }, 15000);
  50. wininfo.init(tabId, function(processableDocs) {
  51. clearTimeout(timeoutError);
  52. that.processableDocs = processableDocs;
  53. callback();
  54. });
  55. }
  56. PageData.prototype = {
  57. initProcess : function() {
  58. var that = this;
  59. this.docs.forEach(function(docData) {
  60. if (that.config.processInBackground) {
  61. if (docData.processDocCallback)
  62. docData.processDocCallback();
  63. } else
  64. docData.process();
  65. });
  66. },
  67. processDoc : function(port, topWindow, winId, index, content, title, url, baseURI, characterSet, canvasData, callbacks) {
  68. var that = this, docData;
  69. docData = new DocData(port, winId, index, content, baseURI, characterSet, canvasData);
  70. if (topWindow) {
  71. this.top = docData;
  72. this.title = title || "";
  73. this.url = url;
  74. }
  75. this.docs.push(docData);
  76. if (this.config.processInBackground && docData.content) {
  77. docData.parseContent();
  78. docData.processDocCallback = singlefile.initProcess(docData.doc, docData.doc.documentElement, topWindow, baseURI, characterSet, this.config,
  79. canvasData, this.requestManager, function(maxIndex) {
  80. callbacks.init(that, docData, maxIndex);
  81. }, function(index, maxIndex) {
  82. callbacks.progress(that, docData, index);
  83. }, function() {
  84. callbacks.end(that, docData);
  85. });
  86. }
  87. },
  88. processDocFragment : function(docData, mutationEventId, content) {
  89. var doc = document.implementation.createHTMLDocument();
  90. doc.body.innerHTML = content;
  91. docData.processDocCallback = singlefile.initProcess(doc, doc.documentElement, this.top == docData, docData.baseURI, docData.characterSet,
  92. this.config, null, this.requestManager, function() {
  93. docData.processDocCallback();
  94. }, null, function() {
  95. docData.setDocFragment(doc.body.innerHTML, mutationEventId);
  96. });
  97. },
  98. setDocContent : function(docData, content, callback) {
  99. var that = this;
  100. function buildPage(docData, setFrameContent, getContent, callback) {
  101. function setContent(docData) {
  102. var parent = docData.parent;
  103. if (parent)
  104. setFrameContent(docData, function() {
  105. parent.processedChildren++;
  106. if (parent.processedChildren == parent.childrenLength)
  107. getContent(parent, function() {
  108. setContent(parent);
  109. });
  110. });
  111. else if (callback)
  112. callback(docData);
  113. }
  114. if (docData.childrenLength)
  115. docData.children.forEach(function(data) {
  116. buildPage(data, setFrameContent, getContent, callback);
  117. });
  118. else
  119. setContent(docData);
  120. }
  121. function bgPageEnd(pageData, docData, callback) {
  122. var content = singlefile.util.getDocContent(docData.doc);
  123. if (pageData.config.displayProcessedPage)
  124. pageData.top.setContent(content);
  125. else
  126. callback(pageData.tabId, pageData.pageId, pageData.top, content);
  127. }
  128. if (content)
  129. docData.content = content;
  130. this.processedDocs++;
  131. if (this.processSelection)
  132. if (this.config.processInBackground)
  133. bgPageEnd(this, docData, callback);
  134. else
  135. docData.getContent(function() {
  136. that.top.setContent(docData.content);
  137. });
  138. else if (this.processedDocs == this.docs.length) {
  139. this.docs.forEach(function(docData) {
  140. var parentWinId = docData.winId.match(/((?:\d*\.?)*)\.\d*/);
  141. parentWinId = parentWinId ? parentWinId[1] : null;
  142. if (parentWinId)
  143. that.docs.forEach(function(data) {
  144. if (data.winId && data.winId == parentWinId)
  145. docData.parent = data;
  146. });
  147. if (docData.parent)
  148. docData.parent.setChild(docData);
  149. });
  150. if (this.config.processInBackground)
  151. buildPage(this.top, function(docData, callback) {
  152. docData.parent.docFrames[docData.index].setAttribute("src", "data:text/html;charset=utf-8,"
  153. + encodeURI(singlefile.util.getDocContent(docData.doc)));
  154. delete docData.doc;
  155. callback();
  156. }, function(docData, callback) {
  157. callback();
  158. }, function(docData) {
  159. bgPageEnd(that, docData, callback);
  160. });
  161. else
  162. buildPage(this.top, function(docData, callback) {
  163. docData.parent.setFrameContent(docData, callback);
  164. }, function(docData, callback) {
  165. docData.getContent(callback);
  166. }, function(docData) {
  167. docData.setContent();
  168. });
  169. }
  170. },
  171. computeProgress : function() {
  172. var that = this;
  173. this.progressIndex = 0;
  174. this.progressMax = 0;
  175. this.docs.forEach(function(docData) {
  176. that.progressIndex += docData.progressIndex || 0;
  177. that.progressMax += docData.progressMax || 0;
  178. });
  179. },
  180. endProcess : function(content) {
  181. var that = this;
  182. if (this.config.savePage)
  183. singlefile.storage.addContent(this.title ? this.title.replace(/[\\\/:\*\?\"><|]/gi, "").trim() || "Unnamed page" : "Unnamed page", content
  184. .replace(/<meta[^>]*http-equiv\s*=\s*["']?content-type[^>]*>/gi, "").replace(/<meta[^>]*charset\s*=[^>]*>/gi, ""),
  185. this.config.filenameMaxLength, function(processed, filename) {
  186. chrome.extension.sendRequest(that.senderId, {
  187. pageSaved : true,
  188. tabId : that.tabId,
  189. pageId : that.pageId,
  190. processed : processed,
  191. filename : filename
  192. });
  193. });
  194. },
  195. getResourceContentRequest : function(url, requestId, winId, characterSet, mediaTypeParam, docData) {
  196. this.requestManager.send(url, function(content) {
  197. docData.getResourceContentResponse(content, requestId);
  198. }, characterSet, mediaTypeParam);
  199. },
  200. getDocData : function(winId) {
  201. var found;
  202. this.docs.forEach(function(docData) {
  203. if (docData.winId == winId)
  204. found = docData;
  205. });
  206. return found;
  207. }
  208. };
  209. function DocData(port, winId, index, content, baseURI, characterSet, canvasData) {
  210. this.port = port;
  211. this.content = content;
  212. this.baseURI = baseURI;
  213. this.characterSet = characterSet;
  214. this.canvasData = canvasData;
  215. this.winId = winId;
  216. this.index = index;
  217. this.children = [];
  218. this.doc = null;
  219. this.docFrames = null;
  220. this.processDocCallback = null;
  221. this.getContentCallback = null;
  222. this.setFrameContentCallback = null;
  223. this.processedChildren = 0;
  224. this.childrenLength = 0;
  225. }
  226. DocData.prototype = {
  227. parseContent : function() {
  228. var doc = document.implementation.createHTMLDocument();
  229. doc.open();
  230. doc.write(this.content);
  231. doc.close();
  232. this.doc = doc;
  233. this.docFrames = doc.querySelectorAll("iframe, frame");
  234. delete this.content;
  235. },
  236. setChild : function(childDoc) {
  237. this.children[childDoc.index] = childDoc;
  238. this.childrenLength++;
  239. },
  240. process : function() {
  241. this.port.postMessage({
  242. processDoc : true,
  243. winId : this.winId
  244. });
  245. },
  246. setDocFragment : function(content, mutationEventId) {
  247. this.port.postMessage({
  248. setDocFragment : true,
  249. content : content,
  250. mutationEventId : mutationEventId
  251. });
  252. },
  253. getResourceContentResponse : function(content, requestId) {
  254. this.port.postMessage({
  255. getResourceContentResponse : true,
  256. requestId : requestId,
  257. winId : this.winId,
  258. content : content
  259. });
  260. },
  261. setContent : function(content) {
  262. this.port.postMessage({
  263. setContentRequest : true,
  264. content : content,
  265. winProperties : singlefile.winProperties
  266. });
  267. },
  268. getContent : function(callback) {
  269. this.getContentCallback = callback;
  270. this.port.postMessage({
  271. getContentRequest : true,
  272. winId : this.winId
  273. });
  274. },
  275. setFrameContent : function(docData, callback) {
  276. docData.setFrameContentCallback = callback;
  277. this.port.postMessage({
  278. setFrameContentRequest : true,
  279. winId : this.winId,
  280. index : docData.index,
  281. content : docData.content
  282. });
  283. }
  284. };
  285. (function() {
  286. var property, winProperties = {};
  287. for (property in window)
  288. winProperties[property] = true;
  289. singlefile.winProperties = winProperties;
  290. })();
  291. })();