1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * Protocol + host parts of extension URL.
12 var FILE_MANAGER_HOST = 'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj';
14 // All of these scripts could be imported with a single call to importScripts,
15 // but then load and compile time errors would all be reported from the same
17 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/metadata_parser.js');
18 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/byte_reader.js');
19 importScripts(FILE_MANAGER_HOST + '/common/js/util.js');
22 * Dispatches metadata requests to the correct parser.
24 * @param {Object} port Worker port.
27 function MetadataDispatcher(port) {
29 this.port_.onmessage = this.onMessage.bind(this);
31 // Make sure to update component_extension_resources.grd
32 // when adding new parsers.
33 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/exif_parser.js');
34 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/image_parsers.js');
35 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/mpeg_parser.js');
36 importScripts(FILE_MANAGER_HOST + '/foreground/js/metadata/id3_parser.js');
40 this.parserInstances_ = [];
41 for (var i = 0; i < MetadataDispatcher.parserClasses_.length; i++) {
42 var parserClass = MetadataDispatcher.parserClasses_[i];
43 var parser = new parserClass(this);
44 this.parserInstances_.push(parser);
45 patterns.push(parser.urlFilter.source);
48 this.parserRegexp_ = new RegExp('(' + patterns.join('|') + ')', 'i');
50 this.messageHandlers_ = {
51 init: this.init_.bind(this),
52 request: this.request_.bind(this)
57 * List of registered parser classes.
60 MetadataDispatcher.parserClasses_ = [];
63 * @param {function} parserClass Parser constructor function.
65 MetadataDispatcher.registerParserClass = function(parserClass) {
66 MetadataDispatcher.parserClasses_.push(parserClass);
70 * Verbose logging for the dispatcher.
72 * Individual parsers also take this as their default verbosity setting.
74 MetadataDispatcher.prototype.verbose = false;
77 * |init| message handler.
80 MetadataDispatcher.prototype.init_ = function() {
81 // Inform our owner that we're done initializing.
82 // If we need to pass more data back, we can add it to the param array.
83 this.postMessage('initialized', [this.parserRegexp_]);
84 this.log('initialized with URL filter ' + this.parserRegexp_);
88 * |request| message handler.
89 * @param {string} fileURL File URL.
92 MetadataDispatcher.prototype.request_ = function(fileURL) {
94 this.processOneFile(fileURL, function callback(metadata) {
95 this.postMessage('result', [fileURL, metadata]);
98 this.error(fileURL, ex);
103 * Indicate to the caller that an operation has failed.
105 * No other messages relating to the failed operation should be sent.
106 * @param {...Object} var_args Arguments.
108 MetadataDispatcher.prototype.error = function(var_args) {
109 var ary = Array.apply(null, arguments);
110 this.postMessage('error', ary);
114 * Send a log message to the caller.
116 * Callers must not parse log messages for control flow.
117 * @param {...Object} var_args Arguments.
119 MetadataDispatcher.prototype.log = function(var_args) {
120 var ary = Array.apply(null, arguments);
121 this.postMessage('log', ary);
125 * Send a log message to the caller only if this.verbose is true.
126 * @param {...Object} var_args Arguments.
128 MetadataDispatcher.prototype.vlog = function(var_args) {
130 this.log.apply(this, arguments);
134 * Post a properly formatted message to the caller.
135 * @param {string} verb Message type descriptor.
136 * @param {Array.<Object>} args Arguments array.
138 MetadataDispatcher.prototype.postMessage = function(verb, args) {
139 this.port_.postMessage({verb: verb, arguments: args});
144 * @param {Event} event Event object.
146 MetadataDispatcher.prototype.onMessage = function(event) {
147 var data = event.data;
149 if (this.messageHandlers_.hasOwnProperty(data.verb)) {
150 this.messageHandlers_[data.verb].apply(this, data.arguments);
152 this.log('Unknown message from client: ' + data.verb, data);
157 * @param {string} fileURL File URL.
158 * @param {function(Object)} callback Completion callback.
160 MetadataDispatcher.prototype.processOneFile = function(fileURL, callback) {
162 var currentStep = -1;
164 function nextStep(var_args) {
165 self.vlog('nextStep: ' + steps[currentStep + 1].name);
166 steps[++currentStep].apply(self, arguments);
171 function onError(err, stepName) {
172 self.error(fileURL, stepName || steps[currentStep].name, err.toString(),
177 [ // Step one, find the parser matching the url.
178 function detectFormat() {
179 for (var i = 0; i != self.parserInstances_.length; i++) {
180 var parser = self.parserInstances_[i];
181 if (fileURL.match(parser.urlFilter)) {
182 // Create the metadata object as early as possible so that we can
183 // pass it with the error message.
184 metadata = parser.createDefaultMetadata();
189 onError('unsupported format');
192 // Step two, turn the url into an entry.
193 function getEntry(parser) {
194 webkitResolveLocalFileSystemURL(
196 function(entry) { nextStep(entry, parser) },
200 // Step three, turn the entry into a file.
201 function getFile(entry, parser) {
202 entry.file(function(file) { nextStep(file, parser) }, onError);
205 // Step four, parse the file content.
206 function parseContent(file, parser) {
207 metadata.fileSize = file.size;
209 parser.parse(file, metadata, callback, onError);
219 // Webworker spec says that the worker global object is called self. That's
220 // a terrible name since we use it all over the chrome codebase to capture
221 // the 'this' keyword in lambdas.
224 if (global.constructor.name == 'SharedWorkerGlobalScope') {
225 global.addEventListener('connect', function(e) {
226 var port = e.ports[0];
227 new MetadataDispatcher(port);
231 // Non-shared worker.
232 new MetadataDispatcher(global);