const CLASSNAME = "TD_AsynchService"; const CONTRACTID = "@skrul.com/threaddemo-asynch-service;1"; const CID = Components.ID("{02baf5b3-b399-4b44-9292-07550c23bae6}"); const Cc = Components.classes; const Ci = Components.interfaces; var asynchService = {}; // nsISupports asynchService.QueryInterface = function(aIID) { if(!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIRunnable) && !aIID.equals(Ci.tdIAsynchService) && !aIID.equals(Ci.nsIClassInfo)) throw Components.results.NS_ERROR_NO_INTERFACE; return this; } // nsIRunnable asynchService.run = function() { // Create an event queue for the asynch service this.eq = this.eqs.createFromIThread(this.thread, false); this.callback.notify("started"); // Start the event loop. this.eq.eventLoop(); // Clean up when the event loop exits this.eq.processPendingEvents(); this.eq.stopAcceptingEvents(); this.eq.processPendingEvents(); this.eqs.destroyThreadEventQueue(); this.callback.notify("stopped"); this.proxy = null; } // tdIAsynchService asynchService.start = function(callback) { this.pom = Cc["@mozilla.org/xpcomproxy;1"] .getService(Ci.nsIProxyObjectManager); this.eqs = Cc["@mozilla.org/event-queue-service;1"] .getService(Ci.nsIEventQueueService); // Wrap the supplied callback in a proxy using UI's event queue this.callback = this.pom.getProxyForObject(this.eqs.getSpecialEventQueue(Ci.UI_THREAD_EVENT_QUEUE), Ci.tdICallback, callback, Ci.nsIProxyObjectManager.INVOKE_ASYNC); // Create and start a thread for the asynch service this.thread = Cc["@mozilla.org/thread;1"] .createInstance(Ci.nsIThread); this.thread.init(this, 0, Ci.nsIThread.PRIORITY_NORMAL, Ci.nsIThread.SCOPE_GLOBAL, Ci.nsIThread.STATE_UNJOINABLE); /* * HACK: We must wait for the event queue to be created on the worker thread * before creating the proxy. */ var mainThread = Cc["@mozilla.org/thread;1"] .createInstance(Ci.nsIThread).currentThread; var i = 0; while(!this.eq && i < 10) { mainThread.sleep(10); } if(!this.eq) { throw Error("Event queue not found"); } // Create a proxied version of this object for the UI to use this.proxy = this.pom.getProxyForObject(this.eq, Ci.tdIAsynchService, this, Ci.nsIProxyObjectManager.INVOKE_ASYNC); return this.proxy; } asynchService.stop = function(callback) { this.thread.interrupt(); } asynchService.methodOne = function() { var i = 0; while(i < 5) { this.callback.notify(this.thread + " methodOne " + i); this.thread.sleep(1000); i++; } } asynchService.methodTwo = function() { var i = 0; while(i < 5) { this.callback.notify(this.thread + " methodTwo " + i); this.thread.sleep(1000); i++; } } // nsIClassInfo asynchService.getInterfaces = function(count) { var ifaces = [ Ci.nsISupports, Ci.nsIRunnable, Ci.tdIAsynchService, Ci.nsIClassInfo ]; count.value = ifaces.length; return ifaces; } asynchService.getHelperForLanguage = function(language) { return null; } asynchService.contractID = CONTRACTID; asynchService.classDescription = CLASSNAME; asynchService.classID = CID; asynchService.implementationLanguage = Ci.nsIProgrammingLanguage.JAVASCRIPT; asynchService.flags = Ci.nsIClassInfo.THREADSAFE; /** * XPCOM Registration */ var Module = new Object(); Module.registerSelf = function(compMgr, fileSpec, location, type) { compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); compMgr.registerFactoryLocation(CID, CLASSNAME, CONTRACTID, fileSpec, location, type); } Module.getClassObject = function(compMgr, cid, iid) { if(!cid.equals(CID)) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; } if(!iid.equals(Ci.nsIFactory)) { throw Components.results.NS_ERROR_NO_INTERFACE; } return Factory; } Module.canUnload = function(compMgr) { return true; } var Factory = {}; Factory.createInstance = function(outer, iid) { if(outer != null) { throw Components.results.NS_ERROR_NO_AGGREGATION; } return asynchService.QueryInterface(iid); } function NSGetModule(compMgr, fileSpec) { return Module; }