node-webodf
Version:
WebODF - JavaScript Document Engine http://webodf.org/
436 lines (398 loc) • 19.7 kB
JavaScript
/**
* Copyright (C) 2012 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* This file is part of WebODF.
*
* WebODF is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License (GNU AGPL)
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* WebODF is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
* @licend
*
* @source: http://www.webodf.org/
* @source: https://github.com/kogmbh/WebODF/
*/
/*global Components, XPCOMUtils, Services, NetUtil, dump*/
function makeODFStreamConverters() {
"use strict";
var Cc = Components.classes,
Ci = Components.interfaces,
Cr = Components.results,
Cu = Components.utils,
WEBODF_MESSAGE_EVENT_ID = 'webodf.message';
Cu["import"]('resource://gre/modules/XPCOMUtils.jsm');
Cu["import"]('resource://gre/modules/Services.jsm');
Cu["import"]('resource://gre/modules/NetUtil.jsm');
function log(aMsg) {
var msg = 'OdfStreamConverter.js: '
+ (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function getDOMWindow(aChannel) {
var requestor = aChannel.notificationCallbacks,
win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
return win;
}
function getMimetypeByPath(path) {
var mimetypeLookupTable = [
{ext: ".odt", type: "application/vnd.oasis.opendocument.text"},
{ext: ".fodt", type: "application/vnd.oasis.opendocument.text-flat-xml"},
{ext: ".ott", type: "application/vnd.oasis.opendocument.text-template"},
{ext: ".odp", type: "application/vnd.oasis.opendocument.presentation"},
{ext: ".fodp", type: "application/vnd.oasis.opendocument.presentation-flat-xml"},
{ext: ".otp", type: "application/vnd.oasis.opendocument.presentation-template"},
{ext: ".ods", type: "application/vnd.oasis.opendocument.spreadsheet"},
{ext: ".fods", type: "application/vnd.oasis.opendocument.spreadsheet-flat-xml"},
{ext: ".ots", type: "application/vnd.oasis.opendocument.spreadsheet-template"}
],
mimetype = "application/vnd.oasis.opendocument.text";
mimetypeLookupTable.some(function (mimetypeEntry) {
var isMatch = (path.substr(-mimetypeEntry.ext.length) === mimetypeEntry.ext);
if (isMatch) {
mimetype = mimetypeEntry.type;
}
return isMatch;
});
return mimetype;
}
// All the priviledged actions.
function ChromeActions(domWindow) {
this.domWindow = domWindow;
}
ChromeActions.prototype = {
download: function (data, sendResponse) {
var originalUrl = data.originalUrl,
// The data may not be downloaded so we need just retry getting
// the odf with the original url.
originalUri = NetUtil.newURI(data.originalUrl),
blobUri = data.blobUrl ? NetUtil.newURI(data.blobUrl)
: originalUri,
extHelperAppSvc =
Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
getService(Ci.nsIExternalHelperAppService),
frontWindow = Cc['@mozilla.org/embedcomp/window-watcher;1'].
getService(Ci.nsIWindowWatcher).activeWindow,
listener;
listener = {
extListener: null,
onStartRequest: function (aRequest, aContext) {
var mimetype = getMimetypeByPath(aRequest.URI.path);
this.extListener = extHelperAppSvc.doContent(mimetype,
aRequest, frontWindow, false);
this.extListener.onStartRequest(aRequest, aContext);
},
onStopRequest: function (aRequest, aContext, aStatusCode) {
if (this.extListener) {
this.extListener.onStopRequest(aRequest, aContext,
aStatusCode);
}
// Notify the content code we're done downloading.
if (sendResponse) {
sendResponse(false);
}
},
onDataAvailable: function (aRequest, aContext, aInputStream,
aOffset, aCount) {
this.extListener.onDataAvailable(aRequest, aContext,
aInputStream, aOffset, aCount);
}
};
NetUtil.asyncFetch(blobUri, function (aInputStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
if (sendResponse) {
sendResponse(true);
}
return;
}
// Create a nsIInputStreamChannel so we can set the url on the
// channel so the filename will be correct.
var channel = Cc['@mozilla.org/network/input-stream-channel;1'].
createInstance(Ci.nsIInputStreamChannel);
channel.setURI(originalUri);
channel.contentStream = aInputStream;
channel.QueryInterface(Ci.nsIChannel);
channel.asyncOpen(listener, null);
});
}
};
// Event listener to trigger chrome privedged code.
function RequestListener(actions) {
this.actions = actions;
}
// Receive an event and synchronously or asynchronously responds.
RequestListener.prototype.receive = function (event) {
var action = event.detail.action,
data = event.detail.data,
actions = this.actions;
if (!(action in actions)) {
log('Unknown action: ' + action);
return;
}
actions[action].call(this.actions, data);
};
/* common base for ODT/OTT, ODS/OTS and ODP/OTP stream converters */
var OdfStreamConverter = {
// properties required for XPCOM registration, except classID
classDescription: 'webodf.js Component',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.text&to=*/*',
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISupports,
Ci.nsIStreamConverter,
Ci.nsIStreamListener,
Ci.nsIRequestObserver
]),
/*
* This component works as such:
* 1. asyncConvertData stores the listener
* 2. onStartRequest creates a new channel, streams the viewer and
* cancels the request so webodf.js can do the request
* Since the request is cancelled onDataAvailable should not be called.
* The onStopRequest does nothing. The convert function just returns the
* stream, it's just the synchronous version of asyncConvertData.
*/
// nsIStreamConverter::convert
convert: function (aFromStream, aFromType, aToType, aCtxt) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
// nsIStreamConverter::asyncConvertData
asyncConvertData: function (aFromType, aToType, aListener, aCtxt) {
// Ignoring HTTP POST requests -- webodf.js has to repeat the
// request.
var skipConversion = false;
try {
var request = aCtxt;
request.QueryInterface(Ci.nsIHttpChannel);
skipConversion = (request.requestMethod !== 'GET');
} catch (e) {
// Non-HTTP request... continue normally.
}
if (skipConversion) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
// Store the listener passed to us
this.listener = aListener;
},
// nsIStreamListener::onDataAvailable
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset,
aCount) {
// Do nothing since all the data loading is handled by the viewer.
log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!');
},
// nsIRequestObserver::onStartRequest
onStartRequest: function (aRequest, aContext) {
// Setup the request so we can use it below.
aRequest.QueryInterface(Ci.nsIChannel);
// Cancel the request so the viewer can handle it.
aRequest.cancel(Cr.NS_BINDING_ABORTED);
// Create a new channel that is viewer loaded as a resource.
var ioService = Services.io,
channel = ioService.newChannel(
'resource://webodf.js/web/viewer.html',
null,
null
),
listener = this.listener,
// Proxy all the request observer calls, when it gets to
// onStopRequest we can get the dom window.
proxy = {
onStartRequest: function () {
listener.onStartRequest.apply(listener, arguments);
},
onDataAvailable: function () {
listener.onDataAvailable.apply(listener, arguments);
},
onStopRequest: function () {
var domWindow = getDOMWindow(channel),
requestListener;
// Double check the url is still the correct one.
if (domWindow.document.documentURIObject.equals(
aRequest.URI
)) {
requestListener = new RequestListener(
new ChromeActions(domWindow)
);
domWindow.addEventListener(WEBODF_MESSAGE_EVENT_ID,
function (event) {
requestListener.receive(event);
}, false, true, true);
}
listener.onStopRequest.apply(listener, arguments);
}
};
// Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI;
channel.asyncOpen(proxy, aContext);
},
// nsIRequestObserver::onStopRequest
onStopRequest: function (aRequest, aContext, aStatusCode) {
// Do nothing.
}
};
function OdtStreamConverter() {
}
OdtStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{43d50559-3e72-435e-9526-482b93f05bf1}'),
classDescription: 'OpenDocument Text converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.text&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function FOdtStreamConverter() {
}
FOdtStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{44327fea-721f-4f09-beeb-488179e3b308}'),
classDescription: 'OpenDocument Text Flat converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.text-flat-xml&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function OttStreamConverter() {
}
OttStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{0600b310-ae8d-4912-b21e-4d0aeef005af}'),
classDescription: 'OpenDocument Text Template converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.text-template&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function OdsStreamConverter() {
}
OdsStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{5f0cd28d-cd7a-4c38-8dd9-72135474b9bc}'),
classDescription: 'OpenDocument Spreadsheet converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.spreadsheet&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function FOdsStreamConverter() {
}
FOdsStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{03fb3d00-3274-4573-87ab-542e9649a2dc}'),
classDescription: 'OpenDocument Spreadsheet Flat converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.spreadsheet-flat-xml&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function OtsStreamConverter() {
}
OtsStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{4f93193f-4dff-40a0-98e7-f96138798dd0}'),
classDescription: 'OpenDocument Spreadsheet Template converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.spreadsheet-template&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function OdpStreamConverter() {
}
OdpStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{84492c35-9c3a-41cc-a512-eb8eb06744a0}'),
classDescription: 'OpenDocument Presentation converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.presentation&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function FOdpStreamConverter() {
}
FOdpStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{b1daa2e0-886d-41a6-8101-323be49531d3}'),
classDescription: 'OpenDocument Presentation Flat converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.presentation-flat-xml&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
function OtpStreamConverter() {
}
OtpStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{17192d8d-fc0a-4f15-b752-23652c781fec}'),
classDescription: 'OpenDocument Presentation Template converter',
contractID: '@mozilla.org/streamconv;1?from=application/vnd.oasis.opendocument.presentation-template&to=*/*',
QueryInterface: OdfStreamConverter.QueryInterface,
convert: OdfStreamConverter.convert,
asyncConvertData: OdfStreamConverter.asyncConvertData,
onDataAvailable: OdfStreamConverter.onDataAvailable,
onStartRequest: OdfStreamConverter.onStartRequest,
onStopRequest: OdfStreamConverter.onStopRequest
};
return {
EXPORTED_SYMBOLS: [
'OdtStreamConverter', 'FOdtStreamConverter', 'OttStreamConverter',
'OdsStreamConverter', 'FOdsStreamConverter', 'OtsStreamConverter',
'OdpStreamConverter', 'FOdpStreamConverter', 'OtpStreamConverter'],
OdtStreamConverter: OdtStreamConverter,
FOdtStreamConverter: FOdtStreamConverter,
OttStreamConverter: OttStreamConverter,
OdsStreamConverter: OdsStreamConverter,
FOdsStreamConverter: FOdsStreamConverter,
OtsStreamConverter: OtsStreamConverter,
OdpStreamConverter: OdpStreamConverter,
FOdpStreamConverter: FOdpStreamConverter,
OtpStreamConverter: OtpStreamConverter,
NSGetFactory: XPCOMUtils.generateNSGetFactory([
OdtStreamConverter, FOdtStreamConverter, OttStreamConverter,
OdsStreamConverter, FOdsStreamConverter, OtsStreamConverter,
OdpStreamConverter, FOdpStreamConverter, OtpStreamConverter])
};
}
var o = makeODFStreamConverters(),
EXPORTED_SYMBOLS = o.EXPORTED_SYMBOLS,
OdtStreamConverter = o.OdtStreamConverter,
FOdtStreamConverter = o.FOdtStreamConverter,
OttStreamConverter = o.OttStreamConverter,
OdsStreamConverter = o.OdsStreamConverter,
FOdsStreamConverter = o.FOdsStreamConverter,
OtsStreamConverter = o.OtsStreamConverter,
OdpStreamConverter = o.OdpStreamConverter,
FOdpStreamConverter = o.FOdpStreamConverter,
OtpStreamConverter = o.OtpStreamConverter,
NSGetFactory = o.NSGetFactory;