home *** CD-ROM | disk | FTP | other *** search
- /*
- * Open Document Format (ODF) Reader.
- * (C) 2005-6 Talin
- * (C) 2006 Alex Hudson
- * Released under the MPL/LGPL/GPL trilicence
- *
- * This component allows ODF files to be viewed directly in Firefox,
- * without a seperate helper application.
- *
- * Theory of operation:
- *
- * 1. Associate this stream handler with the various mime types corresponding
- * to the ODF document types.
- * 2. The incoming byte stream is saved to a temp file.
- * 3. The tempfile is unzipped, and the "content.xml" portion
- * is parsed into a DOM.
- * 4. The DOM is transformed using an XML stylesheet.
- * 5. The stylesheet will convert the ODF XML documents into HTML.
- */
-
- // Table of mimetypes and associated file extensions
- const ODF_TYPES = [
- [ "application/vnd.oasis.opendocument.text", "odt" ],
- /* [ "application/vnd.oasis.opendocument.text-template", "ott" ],
- [ "application/vnd.oasis.opendocument.text-web", "oth" ],
- [ "application/vnd.oasis.opendocument.text-master", "odm" ],
- [ "application/vnd.oasis.opendocument.graphics", "odg" ],
- [ "application/vnd.oasis.opendocument.graphics-template", "otg" ],
- [ "application/vnd.oasis.opendocument.presentation", "odp" ],
- [ "application/vnd.oasis.opendocument.presentation-template", "otp" ],
- [ "application/vnd.oasis.opendocument.spreadsheet", "ods" ],
- [ "application/vnd.oasis.opendocument.spreadsheet-template", "ots" ],
- [ "application/vnd.oasis.opendocument.chart", "odc" ],
- [ "application/vnd.oasis.opendocument.formula", "odf" ],
- [ "application/vnd.oasis.opendocument.database", "odb" ],
- [ "application/vnd.oasis.opendocument.image", "odi" ], */
- ]
-
- /* controls whether or not we output messages to the javascript console */
- const debugging = 1;
-
- const Cc = Components.classes;
- const Ci = Components.interfaces;
-
- const ODFSTREAM_CONVERTER_CID = Components.ID("{1899fea0-66da-11da-952b-00e08161165f}");
-
- /* TODO: can we have this to accept multiple MIMEs? */
- const ODFSTREAM_CONVERT_CONVERSION = "?from=application/vnd.oasis.opendocument.text&to=*/*";
-
- const ODFSTREAM_CONVERTER_CONTRACTID =
- "@mozilla.org/streamconv;1" + ODFSTREAM_CONVERT_CONVERSION;
-
- const tempDir = Cc[ "@mozilla.org/file/directory_service;1" ]
- .getService( Ci.nsIProperties )
- .get( "TmpD", Ci.nsIFile );
-
- /*** ODFStreamConverter class ***/
-
- function ODFStreamConverter () {
- }
-
- /* onStartRequest:
- * - Choose the correct stylesheet for this document type
- * - Create a temporary file to store the zipped data
- * - Create an output stream for the temp file and open it.
- */
- ODFStreamConverter.prototype.onStartRequest =
- function( request, context ) {
- odfReader_logMessage( "onStartRequest" )
-
- this.uri = request.QueryInterface( Ci.nsIChannel ).URI.spec;
-
- this.channel = request;
- this.xslt = "chrome://odfreader/" + this.channel.contentType + ".xsl"
- this.channel.contentType = "text/html";
-
- this.listener.onStartRequest( this.channel, context );
-
- // Create a temporary file to store the zip
- this.file = tempDir.clone()
- this.file.append( "odftemp.zip" )
- this.file.createUnique( Ci.nsIFile.NORMAL_FILE_TYPE, 0664 );
-
- // Create an output stream
- this.ostream = Cc["@mozilla.org/network/file-output-stream;1"]
- .createInstance( Ci.nsIFileOutputStream );
- this.ostream.init( this.file, 0x02 | 0x08 | 0x20, 0664, 0 ); // write, create, truncate
- };
-
- /* onStopRequest:
- * - Extract the content from the zip file and convert to DOM
- * - Run the XSLT processor on the DOM
- * - Serialize the DOM back to HTML
- * - Return the serialized data as a stream to the underlying handler.
- */
- ODFStreamConverter.prototype.onStopRequest =
- function( request, context, statusCode ) {
- odfReader_logMessage( "Status = " + statusCode );
-
- this.ostream.close()
-
- var doc; // this will contain content.xml
- if (statusCode == 0) { // NS_OK
- // Read the zip file
- var zipReader = Cc[ '@mozilla.org/libjar/zip-reader;1' ]
- .createInstance( Ci.nsIZipReader );
- zipReader.init( this.file )
- zipReader.open()
- var entry = zipReader.getEntry( "content.xml" )
- var zipStream = zipReader.getInputStream( entry.name );
- var parser = Cc['@mozilla.org/xmlextras/domparser;1'].getService( Ci.nsIDOMParser );
- doc = parser.parseFromStream( zipStream, null, entry.realSize, "text/xml" );
- zipReader.close();
- }
-
- // Remove the zip file, we don't need it anymore
- this.file.remove( false )
-
- /*
- // FIXME: what if there is an error in the XML?
- var roottag = originalDoc.documentElement;
- if ((roottag.tagName == "parserError") ||
- (roottag.namespaceURI == "http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
- // Use error stylesheet
- var xslt = "chrome://xhtmlmp/content/errors.xsl";
- }
- */
-
- var processor = Cc[ "@mozilla.org/document-transformer;1?type=xslt" ]
- .createInstance( Ci.nsIXSLTProcessor );
-
- // Use an XMLHttpRequest object to load our own stylesheet.
- var styleLoad = Cc[ "@mozilla.org/xmlextras/xmlhttprequest;1" ]
- .createInstance( Ci.nsIXMLHttpRequest );
- styleLoad.open ( "GET", this.xslt, false ); // synchronous load
- /* FIXME: if this doesn't work, we should give an appropriate error message */
- odfReader_logMessage( "Loading " + this.xslt )
- styleLoad.overrideMimeType( "text/xml" );
- styleLoad.send( undefined );
- processor.importStylesheet( styleLoad.responseXML.documentElement );
-
- // Transform the document using the processor
- var transformedDoc = processor.transformToDocument( doc );
-
- var serializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]
- .createInstance( Ci.nsIDOMSerializer );
-
- var memalloc = Cc ["@mozilla.org/xpcom/memory-service;1"]
- .createInstance( Ci.nsIMemory );
- var pipe = Cc ["@mozilla.org/pipe;1"]
- .createInstance( Ci.nsIPipe );
-
- /* http://lxr.mozilla.org/mozilla1.8/source/xpcom/io/nsPipe3.cpp#1274
- * We want to initialize pipe to some sensible memory size, but at the
- * moment it's a bit of a back-of-the-envelope calculation.
- * FIXME: there must be a better way of sending back data
- */
- var targetDocument = serializer.serializeToString( transformedDoc );
- var docsize = targetDocument.length;
- odfReader_logMessage ( "Using buffer size of " + docsize );
- pipe.init( true, true, docsize, 2, memalloc );
- serializer.serializeToStream( transformedDoc, pipe.outputStream, "utf-8" );
-
- if (debugging) {
- // output a copy for testing purposes
- var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
- file.initWithPath( "/tmp/testcontent.xml" );
- var outputStream = Cc[ "@mozilla.org/network/file-output-stream;1" ].createInstance( Ci.nsIFileOutputStream );
- outputStream.init( file, 0x04 | 0x08 | 0x20, 0777, 0 );
- // serializer.serializeToStream ( transformedDoc, outputStream, "utf-8" );
- outputStream.write ( targetDocument, targetDocument.length);
- outputStream.close();
- }
-
- // Pass the data to the main content listener
- this.listener.onDataAvailable( this.channel, context, pipe.inputStream, 0, docsize );
- this.listener.onStopRequest( this.channel, context, statusCode );
- };
-
- // Our onDataAvailable takes the incoming bytes and writes them to the temp file.
- ODFStreamConverter.prototype.onDataAvailable =
- function( request, context, inputStream, offset, count ) {
- odfReader_logMessage( "onDataAvailable" )
-
- bis = Cc[ "@mozilla.org/binaryinputstream;1" ]
- .createInstance( Ci.nsIBinaryInputStream );
- bis.setInputStream( inputStream );
-
- var data = bis.readBytes( count );
- this.ostream.write( data, data.length );
- }
-
- // Start an async conversion
- ODFStreamConverter.prototype.asyncConvertData =
- function( fromType, toType, listener, ctxt ) {
- // Store the listener passed to us
- this.listener = listener;
- }
-
- // Start a synchronous conversion
- // (null implementation)
- ODFStreamConverter.prototype.convert =
- function( fromStream, fromType, toType, ctxt ) {
- return fromStream;
- }
-
- /*** ODFStreamConverterFactory class ***/
-
- var ODFStreamConverterFactory = new Object();
-
- ODFStreamConverterFactory.createInstance =
- function( outer, iid ) {
- odfReader_logMessage( "Created instance!" )
-
- if (outer != null)
- throw Components.results.NS_ERROR_NO_AGGREGATION;
-
- if ( iid.equals(Ci.nsISupports) ||
- iid.equals(Ci.nsIStreamConverter) ||
- iid.equals(Ci.nsIStreamListener) ||
- iid.equals(Ci.nsIRequestObserver))
-
- return new ODFStreamConverter();
-
- throw Components.results.NS_ERROR_INVALID_ARG;
- }
-
-
- /*** ODFReaderModule class ***/
-
- var ODFReaderModule = new Object();
-
- ODFReaderModule.registerSelf =
- function( compMgr, fileSpec, location, type ) {
- var compMgr = compMgr.QueryInterface( Ci.nsIComponentRegistrar );
-
- var catman = Cc[ "@mozilla.org/categorymanager;1" ]
- .getService( Ci.nsICategoryManager);
-
- // Add all the ODF MIME-types to the category manager
- for (var i in ODF_TYPES) {
- var mtype = ODF_TYPES[ i ][ 0 ];
- var fext = ODF_TYPES[ i ][ 1 ];
- catman.addCategoryEntry( "ext-to-type-mapping", fext, mtype, false, true );
- }
-
- odfReader_logMessage( "Added the MIME types" );
- compMgr.registerFactoryLocation(
- ODFSTREAM_CONVERTER_CID,
- "ODF Stream Converter",
- ODFSTREAM_CONVERTER_CONTRACTID,
- fileSpec,
- location,
- type);
-
- catman.addCategoryEntry(
- "@mozilla.org/streamconv;1",
- ODFSTREAM_CONVERT_CONVERSION,
- "ODF to HTML stream converter",
- true,
- true);
- };
-
- ODFReaderModule.unregisterSelf =
- function( compMgr, fileSpec, location ) {
- }
-
- ODFReaderModule.getClassObject =
- function( compMgr, cid, iid ) {
- if (cid.equals( ODFSTREAM_CONVERTER_CID ))
- return ODFStreamConverterFactory;
-
- if (!iid.equals( Ci.nsIFactory ))
- throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-
- throw Components.results.NS_ERROR_NO_INTERFACE;
- }
-
- ODFReaderModule.canUnload =
- function( compMgr ) {
- return true;
- }
-
- /* entry point */
- function NSGetModule( compMgr, fileSpec ) {
- odfReader_logMessage( "Returning ODFReaderModule..." );
- return ODFReaderModule;
- }
-
- // Logging functions
- var gConsoleService = Cc['@mozilla.org/consoleservice;1']
- .getService(Ci.nsIConsoleService);
-
- function odfReader_logMessage( msg ) {
- if (debugging) gConsoleService.logStringMessage( 'ODFReader: ' + msg );
- }
-