home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 June / PCpro_2006_06.ISO / files / techtalk / odfreader-0.1.1-fx.xpi / components / odfReader-service.js
Encoding:
Text File  |  2006-01-17  |  9.7 KB  |  297 lines

  1. /*
  2.  * Open Document Format (ODF) Reader.
  3.  * (C) 2005-6 Talin
  4.  * (C) 2006 Alex Hudson
  5.  * Released under the MPL/LGPL/GPL trilicence
  6.  *
  7.  * This component allows ODF files to be viewed directly in Firefox,
  8.  * without a seperate helper application.
  9.  *
  10.  * Theory of operation:
  11.  *
  12.  *  1. Associate this stream handler with the various mime types corresponding
  13.  *     to the ODF document types.
  14.  *  2. The incoming byte stream is saved to a temp file.
  15.  *  3. The tempfile is unzipped, and the "content.xml" portion
  16.  *     is parsed into a DOM.
  17.  *  4. The DOM is transformed using an XML stylesheet.
  18.  *  5. The stylesheet will convert the ODF XML documents into HTML.
  19.  */
  20.  
  21. // Table of mimetypes and associated file extensions
  22. const ODF_TYPES = [
  23.     [ "application/vnd.oasis.opendocument.text", "odt" ],
  24. /*    [ "application/vnd.oasis.opendocument.text-template", "ott" ],
  25.     [ "application/vnd.oasis.opendocument.text-web", "oth" ],
  26.     [ "application/vnd.oasis.opendocument.text-master", "odm" ],
  27.     [ "application/vnd.oasis.opendocument.graphics", "odg" ],
  28.     [ "application/vnd.oasis.opendocument.graphics-template", "otg" ],
  29.     [ "application/vnd.oasis.opendocument.presentation", "odp" ],
  30.     [ "application/vnd.oasis.opendocument.presentation-template", "otp" ],
  31.     [ "application/vnd.oasis.opendocument.spreadsheet", "ods" ],
  32.     [ "application/vnd.oasis.opendocument.spreadsheet-template", "ots" ],
  33.     [ "application/vnd.oasis.opendocument.chart", "odc" ],
  34.     [ "application/vnd.oasis.opendocument.formula", "odf" ],
  35.     [ "application/vnd.oasis.opendocument.database", "odb" ],
  36.     [ "application/vnd.oasis.opendocument.image", "odi" ], */
  37. ]
  38.  
  39. /* controls whether or not we output messages to the javascript console */
  40. const debugging = 1;
  41.  
  42. const Cc = Components.classes;
  43. const Ci = Components.interfaces;
  44.  
  45. const ODFSTREAM_CONVERTER_CID = Components.ID("{1899fea0-66da-11da-952b-00e08161165f}");
  46.  
  47. /* TODO: can we have this to accept multiple MIMEs? */
  48. const ODFSTREAM_CONVERT_CONVERSION = "?from=application/vnd.oasis.opendocument.text&to=*/*";
  49.  
  50. const ODFSTREAM_CONVERTER_CONTRACTID =
  51.     "@mozilla.org/streamconv;1" + ODFSTREAM_CONVERT_CONVERSION;
  52.     
  53. const tempDir = Cc[ "@mozilla.org/file/directory_service;1" ]
  54.     .getService( Ci.nsIProperties )
  55.     .get( "TmpD", Ci.nsIFile );
  56.  
  57. /*** ODFStreamConverter class ***/
  58.  
  59. function ODFStreamConverter () {
  60. }
  61.  
  62. /* onStartRequest:
  63.  *  - Choose the correct stylesheet for this document type
  64.  *  - Create a temporary file to store the zipped data
  65.  *  - Create an output stream for the temp file and open it.
  66.  */
  67. ODFStreamConverter.prototype.onStartRequest = 
  68. function( request, context ) {
  69.     odfReader_logMessage( "onStartRequest" )
  70.     
  71.     this.uri = request.QueryInterface( Ci.nsIChannel ).URI.spec;
  72.     
  73.     this.channel = request;
  74.     this.xslt = "chrome://odfreader/" + this.channel.contentType + ".xsl"
  75.     this.channel.contentType = "text/html";
  76.     
  77.     this.listener.onStartRequest( this.channel, context );
  78.     
  79.     // Create a temporary file to store the zip    
  80.     this.file = tempDir.clone()
  81.     this.file.append( "odftemp.zip" )
  82.     this.file.createUnique( Ci.nsIFile.NORMAL_FILE_TYPE, 0664 );
  83.     
  84.     // Create an output stream
  85.     this.ostream = Cc["@mozilla.org/network/file-output-stream;1"]
  86.         .createInstance( Ci.nsIFileOutputStream );
  87.     this.ostream.init( this.file, 0x02 | 0x08 | 0x20, 0664, 0 ); // write, create, truncate
  88. };
  89.  
  90. /* onStopRequest:
  91.  * - Extract the content from the zip file and convert to DOM
  92.  * - Run the XSLT processor on the DOM
  93.  * - Serialize the DOM back to HTML
  94.  * - Return the serialized data as a stream to the underlying handler.
  95.  */
  96. ODFStreamConverter.prototype.onStopRequest = 
  97. function( request, context, statusCode ) {
  98.     odfReader_logMessage( "Status = " + statusCode );
  99.     
  100.     this.ostream.close()
  101.     
  102.     var doc; // this will contain content.xml
  103.     if (statusCode == 0) {  // NS_OK
  104.         // Read the zip file
  105.         var zipReader = Cc[ '@mozilla.org/libjar/zip-reader;1' ]
  106.             .createInstance( Ci.nsIZipReader );
  107.         zipReader.init( this.file )
  108.         zipReader.open()
  109.         var entry = zipReader.getEntry( "content.xml" )
  110.         var zipStream = zipReader.getInputStream( entry.name );
  111.         var parser = Cc['@mozilla.org/xmlextras/domparser;1'].getService( Ci.nsIDOMParser );
  112.         doc = parser.parseFromStream( zipStream, null, entry.realSize, "text/xml" );
  113.         zipReader.close();
  114.     }
  115.     
  116.     // Remove the zip file, we don't need it anymore
  117.     this.file.remove( false )
  118.     
  119.     /*
  120.     // FIXME: what if there is an error in the XML?
  121.     var roottag = originalDoc.documentElement;
  122.     if ((roottag.tagName == "parserError") ||
  123.         (roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
  124.         // Use error stylesheet
  125.         var xslt = "chrome://xhtmlmp/content/errors.xsl";
  126.     }
  127.     */
  128.     
  129.     var processor = Cc[ "@mozilla.org/document-transformer;1?type=xslt" ]
  130.         .createInstance( Ci.nsIXSLTProcessor );
  131.     
  132.     // Use an XMLHttpRequest object to load our own stylesheet.
  133.     var styleLoad = Cc[ "@mozilla.org/xmlextras/xmlhttprequest;1" ]
  134.         .createInstance( Ci.nsIXMLHttpRequest );
  135.     styleLoad.open ( "GET", this.xslt, false ); // synchronous load
  136.     /* FIXME: if this doesn't work, we should give an appropriate error message */
  137.     odfReader_logMessage( "Loading " + this.xslt )
  138.     styleLoad.overrideMimeType( "text/xml" );
  139.     styleLoad.send( undefined );
  140.     processor.importStylesheet( styleLoad.responseXML.documentElement );
  141.     
  142.     // Transform the document using the processor
  143.     var transformedDoc = processor.transformToDocument( doc );
  144.     
  145.     var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]
  146.         .createInstance( Ci.nsIDOMSerializer );
  147.     
  148.     var memalloc = Cc ["@mozilla.org/xpcom/memory-service;1"]
  149.         .createInstance( Ci.nsIMemory );
  150.     var pipe = Cc ["@mozilla.org/pipe;1"]
  151.         .createInstance( Ci.nsIPipe );
  152.     
  153.     /* http://lxr.mozilla.org/mozilla1.8/source/xpcom/io/nsPipe3.cpp#1274
  154.      * We want to initialize pipe to some sensible memory size, but at the
  155.      * moment it's a bit of a back-of-the-envelope calculation.
  156.      * FIXME: there must be a better way of sending back data
  157.      */
  158.     var targetDocument = serializer.serializeToString( transformedDoc );
  159.     var docsize = targetDocument.length;
  160.     odfReader_logMessage ( "Using buffer size of " + docsize );
  161.     pipe.init( true, true, docsize, 2, memalloc );
  162.     serializer.serializeToStream( transformedDoc, pipe.outputStream, "utf-8" );
  163.     
  164.     if (debugging) {
  165.         // output a copy for testing purposes
  166.         var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  167.         file.initWithPath( "/tmp/testcontent.xml" );
  168.         var outputStream = Cc[ "@mozilla.org/network/file-output-stream;1" ].createInstance( Ci.nsIFileOutputStream );
  169.         outputStream.init( file, 0x04 | 0x08 | 0x20, 0777, 0 );
  170.         // serializer.serializeToStream ( transformedDoc, outputStream, "utf-8" );
  171.         outputStream.write ( targetDocument, targetDocument.length);
  172.         outputStream.close(); 
  173.     }
  174.     
  175.     // Pass the data to the main content listener
  176.     this.listener.onDataAvailable( this.channel, context, pipe.inputStream, 0, docsize );
  177.     this.listener.onStopRequest( this.channel, context, statusCode );
  178. };
  179.  
  180. // Our onDataAvailable takes the incoming bytes and writes them to the temp file.
  181. ODFStreamConverter.prototype.onDataAvailable =
  182. function( request, context, inputStream, offset, count ) {
  183.     odfReader_logMessage( "onDataAvailable" )
  184.     
  185.     bis = Cc[ "@mozilla.org/binaryinputstream;1" ]
  186.         .createInstance( Ci.nsIBinaryInputStream );
  187.     bis.setInputStream( inputStream );
  188.     
  189.     var data = bis.readBytes( count );
  190.     this.ostream.write( data, data.length );
  191. }
  192.  
  193. // Start an async conversion
  194. ODFStreamConverter.prototype.asyncConvertData = 
  195. function( fromType, toType, listener, ctxt ) {
  196.     // Store the listener passed to us
  197.     this.listener = listener;
  198. }
  199.  
  200. // Start a synchronous conversion
  201. // (null implementation)
  202. ODFStreamConverter.prototype.convert = 
  203. function( fromStream, fromType, toType, ctxt ) {
  204.     return fromStream;
  205. }
  206.  
  207. /***  ODFStreamConverterFactory class ***/
  208.  
  209. var ODFStreamConverterFactory = new Object();
  210.  
  211. ODFStreamConverterFactory.createInstance = 
  212. function( outer, iid ) {
  213.     odfReader_logMessage( "Created instance!" )
  214.     
  215.     if (outer != null)
  216.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  217.     
  218.     if (    iid.equals(Ci.nsISupports) ||
  219.         iid.equals(Ci.nsIStreamConverter) ||
  220.         iid.equals(Ci.nsIStreamListener) ||
  221.         iid.equals(Ci.nsIRequestObserver))
  222.         
  223.         return new ODFStreamConverter();
  224.  
  225.     throw Components.results.NS_ERROR_INVALID_ARG;
  226. }
  227.  
  228.  
  229. /*** ODFReaderModule class ***/
  230.  
  231. var ODFReaderModule = new Object();
  232.  
  233. ODFReaderModule.registerSelf = 
  234. function( compMgr, fileSpec, location, type ) {
  235.     var compMgr = compMgr.QueryInterface( Ci.nsIComponentRegistrar );
  236.     
  237.     var catman = Cc[ "@mozilla.org/categorymanager;1" ]
  238.         .getService( Ci.nsICategoryManager);
  239.     
  240.     // Add all the ODF MIME-types to the category manager
  241.     for (var i in ODF_TYPES) {
  242.         var mtype = ODF_TYPES[ i ][ 0 ];
  243.         var fext = ODF_TYPES[ i ][ 1 ];
  244.         catman.addCategoryEntry( "ext-to-type-mapping", fext, mtype, false, true );
  245.     }
  246.     
  247.     odfReader_logMessage( "Added the MIME types" );
  248.     compMgr.registerFactoryLocation(
  249.         ODFSTREAM_CONVERTER_CID,
  250.         "ODF Stream Converter",
  251.         ODFSTREAM_CONVERTER_CONTRACTID, 
  252.         fileSpec,
  253.         location, 
  254.         type);
  255.     
  256.     catman.addCategoryEntry(
  257.         "@mozilla.org/streamconv;1",
  258.         ODFSTREAM_CONVERT_CONVERSION,
  259.         "ODF to HTML stream converter",
  260.         true,
  261.         true);
  262. };
  263.  
  264. ODFReaderModule.unregisterSelf = 
  265. function( compMgr, fileSpec, location ) {
  266. }
  267.  
  268. ODFReaderModule.getClassObject = 
  269. function( compMgr, cid, iid ) {
  270.     if (cid.equals( ODFSTREAM_CONVERTER_CID ))
  271.         return ODFStreamConverterFactory;
  272.     
  273.     if (!iid.equals( Ci.nsIFactory ))
  274.         throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  275.     
  276.     throw Components.results.NS_ERROR_NO_INTERFACE;
  277. }
  278.  
  279. ODFReaderModule.canUnload = 
  280. function( compMgr ) {
  281.     return true;
  282. }
  283.  
  284. /* entry point */
  285. function NSGetModule( compMgr, fileSpec ) {
  286.     odfReader_logMessage( "Returning ODFReaderModule..." );
  287.     return ODFReaderModule;
  288. }
  289.  
  290. // Logging functions
  291. var gConsoleService = Cc['@mozilla.org/consoleservice;1']
  292.     .getService(Ci.nsIConsoleService);
  293.  
  294. function odfReader_logMessage( msg ) {
  295.     if (debugging) gConsoleService.logStringMessage( 'ODFReader: ' + msg );
  296. }
  297.