diff --git a/calendar/base/modules/calUtils.jsm b/calendar/base/modules/calUtils.jsm index 9dcd6117d3..4630e8a7ef 100644 --- a/calendar/base/modules/calUtils.jsm +++ b/calendar/base/modules/calUtils.jsm @@ -116,70 +116,6 @@ let cal = { return gCalThreadingEnabled; }, - getWorkerThread: function cal_getWorkerThread() { - let threads = cal_getWorkerThread.mThreads; - switch (threads) { - case null: - return null; // during shutdown - case undefined: { - cal_getWorkerThread.mThreads = threads = Components.classes["@mozilla.org/thread-pool;1"] - .createInstance(Components.interfaces.nsIThreadPool); - cal.addShutdownObserver(function() { - cal.LOG("cal.getWorkerThread() shutdown."); - let threads = cal_getWorkerThread.mThreads; - cal_getWorkerThread.mThreads = null; - threads.shutdown(); - }); - break; - } - } - return threads; - }, - - /** - * Executes the passed action function on a worker thread. - * Once the action function has been executed, respFunc is called - * on the same thread that execAsync() has been called on. - * - * xxx todo: think of leaks, ref cycles! - */ - execWorker: function cal_execWorker(actionFunc, respFunc) { - // response executed on the calling thread: - let callingThread = cal.getThreadManager().currentThread; - - let worker = { // nsIRunnable: - run: function worker_run() { - let res = null; - try { - actionFunc(callingThread); - } catch (exc) { - res = exc; - } - - if (!respFunc) { - return; - } - - let response = { // nsIRunnable: - run: function response_run() { - respFunc(res); - } - }; - callingThread.dispatch(response, Components.interfaces.nsIEventTarget.DISPATCH_NORMAL); - } - }; - - if (cal.threadingEnabled) { - let thread = cal.getWorkerThread(); - if (thread) { - thread.dispatch(worker, Components.interfaces.nsIEventTarget.DISPATCH_NORMAL); - return; - } // else shutdown ongoing - } - - worker.run(); // exec on current thread - }, - /** * Checks whether a timezone lacks a definition. */ diff --git a/calendar/base/src/Makefile.in b/calendar/base/src/Makefile.in index b4d84cf9db..466bdaaaf7 100644 --- a/calendar/base/src/Makefile.in +++ b/calendar/base/src/Makefile.in @@ -86,6 +86,7 @@ EXTRA_SCRIPTS = \ calEvent.js \ calFilter.js \ calIcsParser.js \ + calIcsParser-worker.js \ calIcsSerializer.js \ calItemBase.js \ calItipItem.js \ diff --git a/calendar/base/src/calIcsParser-worker.js b/calendar/base/src/calIcsParser-worker.js new file mode 100644 index 0000000000..bef9f08ee7 --- /dev/null +++ b/calendar/base/src/calIcsParser-worker.js @@ -0,0 +1,47 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Calendar code. + * + * The Initial Developer of the Original Code is + * Philipp Kewisch + * Portions created by the Initial Developer are Copyright (C) 2010-2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * Worker to parse ics service messages + * NOTE: Since this is a ChromeWorker, access to xpcom is limited! + */ + +function onmessage(event) { + let icsService = XPCOM.getService("@mozilla.org/calendar/ics-service;1"); + let rootComp = icsService.parseICS(event.data.icsString, event.data.tzProvider); + + postMessage(rootComp); +} diff --git a/calendar/base/src/calIcsParser.js b/calendar/base/src/calIcsParser.js index a662313ed7..be9edbfdf5 100644 --- a/calendar/base/src/calIcsParser.js +++ b/calendar/base/src/calIcsParser.js @@ -47,7 +47,6 @@ function calIcsParser() { this.mProperties = new Array(); } - calIcsParser.prototype = { contractID: "@mozilla.org/calendar/ics-parser;1", classDescription: "Calendar ICS Parser", @@ -221,70 +220,65 @@ calIcsParser.prototype = { if (aAsyncParsing) { let this_ = this; let rootComp = null; - // Do the actual ical parsing on a thread, but process the parsed ical - // components on main/UI thread. - cal.execWorker( - function parseString_worker(responseThread) { - rootComp = cal.getIcsService().parseICS(aICSString, aTzProvider); - }, - function parseString_done(exc) { + + let wf = Components.classes["@mozilla.org/threads/workerfactory;1"] + .createInstance(Components.interfaces.nsIWorkerFactory); + + let worker = wf.newChromeWorker("resource://calendar/calendar-js/calIcsParser-worker.js"); + + worker.onmessage = { + handleEvent: function(event) { + let rootComp = event.data.QueryInterface(Components.interfaces.calIIcalComponent); this_.processIcalComponent(rootComp); - aAsyncParsing.onParsingComplete(exc ? exc.result : Components.results.NS_OK, this_); - }); + aAsyncParsing.onParsingComplete(Components.results.NS_OK, this_); + } + }; + + worker.onerror = { + handleEvent: function(error) { + cal.ERROR("Error Parsing ICS: " + error.message); + aAsyncParsing.onParsingComplete(Components.results.NS_ERROR_FAILURE, this_); + } + }; + + worker.postMessage({ icsString: aICSString, tzProvider: aTzProvider }); } else { this.processIcalComponent(cal.getIcsService().parseICS(aICSString, aTzProvider)); } }, parseFromStream: function ip_parseFromStream(aStream, aTzProvider, aAsyncParsing) { - function readString(aStream_) { - // Read in the string. Note that it isn't a real string at this point, - // because likely, the file is utf8. The multibyte chars show up as multiple - // 'chars' in this string. So call it an array of octets for now. + // Read in the string. Note that it isn't a real string at this point, + // because likely, the file is utf8. The multibyte chars show up as multiple + // 'chars' in this string. So call it an array of octets for now. - let octetArray = []; - let binaryIS = Components.classes["@mozilla.org/binaryinputstream;1"] - .createInstance(Components.interfaces.nsIBinaryInputStream); - binaryIS.setInputStream(aStream); - octetArray = binaryIS.readByteArray(binaryIS.available()); + let octetArray = []; + let binaryIS = Components.classes["@mozilla.org/binaryinputstream;1"] + .createInstance(Components.interfaces.nsIBinaryInputStream); + binaryIS.setInputStream(aStream); + octetArray = binaryIS.readByteArray(binaryIS.available()); - // Some other apps (most notably, sunbird 0.2) happily splits an UTF8 - // character between the octets, and adds a newline and space between them, - // for ICS folding. Unfold manually before parsing the file as utf8.This is - // UTF8 safe, because octets with the first bit 0 are always one-octet - // characters. So the space or the newline never can be part of a multi-byte - // char. - for (let i = octetArray.length - 2; i >= 0; i--) { - if (octetArray[i] == "\n" && octetArray[i+1] == " ") { - octetArray = octetArray.splice(i, 2); - } + // Some other apps (most notably, sunbird 0.2) happily splits an UTF8 + // character between the octets, and adds a newline and space between them, + // for ICS folding. Unfold manually before parsing the file as utf8.This is + // UTF8 safe, because octets with the first bit 0 are always one-octet + // characters. So the space or the newline never can be part of a multi-byte + // char. + for (let i = octetArray.length - 2; i >= 0; i--) { + if (octetArray[i] == "\n" && octetArray[i+1] == " ") { + octetArray = octetArray.splice(i, 2); } - - // Interpret the byte-array as a UTF8-string, and convert into a - // javascript string. - let unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - // ICS files are always UTF8 - unicodeConverter.charset = "UTF-8"; - return unicodeConverter.convertFromByteArray(octetArray, octetArray.length); } - if (aAsyncParsing) { - let this_ = this; - let rootComp = null; - // Do the actual string reading and ical parsing on a athread, but process the parsed ical - // components on main/UI thread. - cal.execWorker( - function parseString_worker(responseThread) { - rootComp = cal.getIcsService().parseICS(readString(aStream), aTzProvider); - }, - function parseString_done(exc) { - this_.processIcalComponent(rootComp); - aAsyncParsing.onParsingComplete(exc ? exc.result : Components.results.NS_OK, this_); - }); - } else { - this.processIcalComponent(cal.getIcsService().parseICS(readString(aStream), aTzProvider)); - } + // Interpret the byte-array as a UTF8-string, and convert into a + // javascript string. + let unicodeConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] + .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + // ICS files are always UTF8 + unicodeConverter.charset = "UTF-8"; + let stringData = unicodeConverter.convertFromByteArray(octetArray, octetArray.length); + + this.parseString(stringData, aTzProvider, aAsyncParsing); }, getItems: function ip_getItems(aCount) {