Bug 1140172 - Use a single reader worker instead of spawning infinite workers. r=bnicholson f=Yoric
rename from toolkit/components/reader/content/JSDOMParser.js
rename to toolkit/components/reader/JSDOMParser.js
rename from toolkit/components/reader/content/Readability.js
rename to toolkit/components/reader/Readability.js
--- a/toolkit/components/reader/ReaderMode.jsm
+++ b/toolkit/components/reader/ReaderMode.jsm
@@ -10,16 +10,17 @@ const { classes: Cc, interfaces: Ci, uti
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["XMLHttpRequest"]);
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", "resource://services-common/utils.js");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
this.ReaderMode = {
// Version of the cache schema.
CACHE_VERSION: 1,
DEBUG: 0,
@@ -194,67 +195,51 @@ this.ReaderMode = {
* Attempts to parse a document into an article. Heavy lifting happens
* in readerWorker.js.
*
* @param uri The article URI.
* @param doc The document to parse.
* @return {Promise}
* @resolves JS object representing the article, or null if no article is found.
*/
- _readerParse: function (uri, doc) {
- return new Promise((resolve, reject) => {
- let numTags = doc.getElementsByTagName("*").length;
- if (numTags > this.MAX_ELEMS_TO_PARSE) {
- this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
- resolve(null);
- return;
- }
+ _readerParse: Task.async(function* (uri, doc) {
+ let numTags = doc.getElementsByTagName("*").length;
+ if (numTags > this.MAX_ELEMS_TO_PARSE) {
+ this.log("Aborting parse for " + uri.spec + "; " + numTags + " elements found");
+ return null;
+ }
- let worker = new ChromeWorker("chrome://global/content/reader/readerWorker.js");
- worker.onmessage = evt => {
- let article = evt.data;
-
- if (!article) {
- this.log("Worker did not return an article");
- resolve(null);
- return;
- }
-
- // Readability returns a URI object, but we only care about the URL.
- article.url = article.uri.spec;
- delete article.uri;
+ let uriParam = {
+ spec: uri.spec,
+ host: uri.host,
+ prePath: uri.prePath,
+ scheme: uri.scheme,
+ pathBase: Services.io.newURI(".", null, uri).spec
+ };
- let flags = Ci.nsIDocumentEncoder.OutputSelectionOnly | Ci.nsIDocumentEncoder.OutputAbsoluteLinks;
- article.title = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils)
- .convertToPlainText(article.title, flags, 0);
- resolve(article);
- };
+ let serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
+ createInstance(Ci.nsIDOMSerializer);
+ let serializedDoc = yield Promise.resolve(serializer.serializeToString(doc));
- worker.onerror = evt => {
- reject("Error in worker: " + evt.message);
- };
+ let article = yield ReaderWorker.post("parseDocument", [uriParam, serializedDoc]);
- try {
- let serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"].
- createInstance(Ci.nsIDOMSerializer);
- worker.postMessage({
- uri: {
- spec: uri.spec,
- host: uri.host,
- prePath: uri.prePath,
- scheme: uri.scheme,
- pathBase: Services.io.newURI(".", null, uri).spec
- },
- doc: serializer.serializeToString(doc)
- });
- } catch (e) {
- reject("Reader: could not build Readability arguments: " + e);
- }
- });
- },
+ if (!article) {
+ this.log("Worker did not return an article");
+ return null;
+ }
+
+ // Readability returns a URI object, but we only care about the URL.
+ article.url = article.uri.spec;
+ delete article.uri;
+
+ let flags = Ci.nsIDocumentEncoder.OutputSelectionOnly | Ci.nsIDocumentEncoder.OutputAbsoluteLinks;
+ article.title = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils)
+ .convertToPlainText(article.title, flags, 0);
+ return article;
+ }),
get _cryptoHash() {
delete this._cryptoHash;
return this._cryptoHash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
},
get _unicodeConverter() {
delete this._unicodeConverter;
rename from toolkit/components/reader/content/readerWorker.js
rename to toolkit/components/reader/ReaderWorker.js
--- a/toolkit/components/reader/content/readerWorker.js
+++ b/toolkit/components/reader/ReaderWorker.js
@@ -1,12 +1,46 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
-importScripts("JSDOMParser.js", "Readability.js");
+"use strict";
+
+/**
+ * A worker dedicated to handle parsing documents for reader view.
+ */
+
+importScripts("resource://gre/modules/workers/require.js",
+ "resource://gre/modules/reader/JSDOMParser.js",
+ "resource://gre/modules/reader/Readability.js");
+
+let PromiseWorker = require("resource://gre/modules/workers/PromiseWorker.js");
-self.onmessage = function (msg) {
- let uri = msg.data.uri;
- let doc = new JSDOMParser().parse(msg.data.doc);
- let article = new Readability(uri, doc).parse();
- postMessage(article);
+let worker = new PromiseWorker.AbstractWorker();
+worker.dispatch = function(method, args = []) {
+ return Agent[method](...args);
+};
+worker.postMessage = function(result, ...transfers) {
+ self.postMessage(result, ...transfers);
+};
+worker.close = function() {
+ self.close();
+};
+worker.log = function(...args) {
+ dump("ReaderWorker: " + args.join(" ") + "\n");
};
+
+self.addEventListener("message", msg => worker.handleMessage(msg));
+
+let Agent = {
+ /**
+ * Parses structured article data from a document.
+ *
+ * @param {object} uri URI data for the document.
+ * @param {string} serializedDoc The serialized document.
+ *
+ * @return {object} Article object returned from Readability.
+ */
+ parseDocument: function (uri, serializedDoc) {
+ let doc = new JSDOMParser().parse(serializedDoc);
+ return new Readability(uri, doc).parse();
+ },
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/reader/ReaderWorker.jsm
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Interface to a dedicated thread handling readability parsing.
+ */
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
+
+this.EXPORTED_SYMBOLS = ["ReaderWorker"];
+
+this.ReaderWorker = new BasePromiseWorker("resource://gre/modules/reader/ReaderWorker.js");
--- a/toolkit/components/reader/jar.mn
+++ b/toolkit/components/reader/jar.mn
@@ -1,10 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
toolkit.jar:
content/global/reader/aboutReader.html (content/aboutReader.html)
content/global/reader/aboutReader.js (content/aboutReader.js)
- content/global/reader/Readability.js (content/Readability.js)
- content/global/reader/JSDOMParser.js (content/JSDOMParser.js)
- content/global/reader/readerWorker.js (content/readerWorker.js)
--- a/toolkit/components/reader/moz.build
+++ b/toolkit/components/reader/moz.build
@@ -5,8 +5,15 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
JAR_MANIFESTS += ['jar.mn']
EXTRA_JS_MODULES += [
'AboutReader.jsm',
'ReaderMode.jsm'
]
+
+EXTRA_JS_MODULES.reader = [
+ 'JSDOMParser.js',
+ 'Readability.js',
+ 'ReaderWorker.js',
+ 'ReaderWorker.jsm'
+]