| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 // All of these scripts could be imported with a single call to importScripts, | |
| 8 // but then load and compile time errors would all be reported from the same | |
| 9 // line. | |
| 10 importScripts('metadata_parser.js'); | |
| 11 importScripts('byte_reader.js'); | |
| 12 importScripts('../util.js'); | |
| 13 | |
| 14 /** | |
| 15 * Dispatches metadata requests to the correct parser. | |
| 16 * | |
| 17 * @param {Object} port Worker port. | |
| 18 * @constructor | |
| 19 */ | |
| 20 function MetadataDispatcher(port) { | |
| 21 this.port_ = port; | |
| 22 this.port_.onmessage = this.onMessage.bind(this); | |
| 23 | |
| 24 // Make sure to update component_extension_resources.grd | |
| 25 // when adding new parsers. | |
| 26 importScripts('exif_parser.js'); | |
| 27 importScripts('image_parsers.js'); | |
| 28 importScripts('mpeg_parser.js'); | |
| 29 importScripts('id3_parser.js'); | |
| 30 | |
| 31 var patterns = []; | |
| 32 | |
| 33 this.parserInstances_ = []; | |
| 34 for (var i = 0; i < MetadataDispatcher.parserClasses_.length; i++) { | |
| 35 var parserClass = MetadataDispatcher.parserClasses_[i]; | |
| 36 var parser = new parserClass(this); | |
| 37 this.parserInstances_.push(parser); | |
| 38 patterns.push(parser.urlFilter.source); | |
| 39 } | |
| 40 | |
| 41 this.parserRegexp_ = new RegExp('(' + patterns.join('|') + ')', 'i'); | |
| 42 | |
| 43 this.messageHandlers_ = { | |
| 44 init: this.init_.bind(this), | |
| 45 request: this.request_.bind(this) | |
| 46 }; | |
| 47 } | |
| 48 | |
| 49 /** | |
| 50 * List of registered parser classes. | |
| 51 * @private | |
| 52 */ | |
| 53 MetadataDispatcher.parserClasses_ = []; | |
| 54 | |
| 55 /** | |
| 56 * @param {function} parserClass Parser constructor function. | |
| 57 */ | |
| 58 MetadataDispatcher.registerParserClass = function(parserClass) { | |
| 59 MetadataDispatcher.parserClasses_.push(parserClass); | |
| 60 }; | |
| 61 | |
| 62 /** | |
| 63 * Verbose logging for the dispatcher. | |
| 64 * | |
| 65 * Individual parsers also take this as their default verbosity setting. | |
| 66 */ | |
| 67 MetadataDispatcher.prototype.verbose = false; | |
| 68 | |
| 69 /** | |
| 70 * |init| message handler. | |
| 71 * @private | |
| 72 */ | |
| 73 MetadataDispatcher.prototype.init_ = function() { | |
| 74 // Inform our owner that we're done initializing. | |
| 75 // If we need to pass more data back, we can add it to the param array. | |
| 76 this.postMessage('initialized', [this.parserRegexp_]); | |
| 77 this.log('initialized with URL filter ' + this.parserRegexp_); | |
| 78 }; | |
| 79 | |
| 80 /** | |
| 81 * |request| message handler. | |
| 82 * @param {string} fileURL File URL. | |
| 83 * @private | |
| 84 */ | |
| 85 MetadataDispatcher.prototype.request_ = function(fileURL) { | |
| 86 try { | |
| 87 this.processOneFile(fileURL, function callback(metadata) { | |
| 88 this.postMessage('result', [fileURL, metadata]); | |
| 89 }.bind(this)); | |
| 90 } catch (ex) { | |
| 91 this.error(fileURL, ex); | |
| 92 } | |
| 93 }; | |
| 94 | |
| 95 /** | |
| 96 * Indicate to the caller that an operation has failed. | |
| 97 * | |
| 98 * No other messages relating to the failed operation should be sent. | |
| 99 * @param {...Object} var_args Arguments. | |
| 100 */ | |
| 101 MetadataDispatcher.prototype.error = function(var_args) { | |
| 102 var ary = Array.apply(null, arguments); | |
| 103 this.postMessage('error', ary); | |
| 104 }; | |
| 105 | |
| 106 /** | |
| 107 * Send a log message to the caller. | |
| 108 * | |
| 109 * Callers must not parse log messages for control flow. | |
| 110 * @param {...Object} var_args Arguments. | |
| 111 */ | |
| 112 MetadataDispatcher.prototype.log = function(var_args) { | |
| 113 var ary = Array.apply(null, arguments); | |
| 114 this.postMessage('log', ary); | |
| 115 }; | |
| 116 | |
| 117 /** | |
| 118 * Send a log message to the caller only if this.verbose is true. | |
| 119 * @param {...Object} var_args Arguments. | |
| 120 */ | |
| 121 MetadataDispatcher.prototype.vlog = function(var_args) { | |
| 122 if (this.verbose) | |
| 123 this.log.apply(this, arguments); | |
| 124 }; | |
| 125 | |
| 126 /** | |
| 127 * Post a properly formatted message to the caller. | |
| 128 * @param {string} verb Message type descriptor. | |
| 129 * @param {Array.<Object>} args Arguments array. | |
| 130 */ | |
| 131 MetadataDispatcher.prototype.postMessage = function(verb, args) { | |
| 132 this.port_.postMessage({verb: verb, arguments: args}); | |
| 133 }; | |
| 134 | |
| 135 /** | |
| 136 * Message handler. | |
| 137 * @param {Event} event Event object. | |
| 138 */ | |
| 139 MetadataDispatcher.prototype.onMessage = function(event) { | |
| 140 var data = event.data; | |
| 141 | |
| 142 if (this.messageHandlers_.hasOwnProperty(data.verb)) { | |
| 143 this.messageHandlers_[data.verb].apply(this, data.arguments); | |
| 144 } else { | |
| 145 this.log('Unknown message from client: ' + data.verb, data); | |
| 146 } | |
| 147 }; | |
| 148 | |
| 149 /** | |
| 150 * @param {string} fileURL File URL. | |
| 151 * @param {function(Object)} callback Completion callback. | |
| 152 */ | |
| 153 MetadataDispatcher.prototype.processOneFile = function(fileURL, callback) { | |
| 154 var self = this; | |
| 155 var currentStep = -1; | |
| 156 | |
| 157 function nextStep(var_args) { | |
| 158 self.vlog('nextStep: ' + steps[currentStep + 1].name); | |
| 159 steps[++currentStep].apply(self, arguments); | |
| 160 } | |
| 161 | |
| 162 var metadata; | |
| 163 | |
| 164 function onError(err, stepName) { | |
| 165 self.error(fileURL, stepName || steps[currentStep].name, err.toString(), | |
| 166 metadata); | |
| 167 } | |
| 168 | |
| 169 var steps = | |
| 170 [ // Step one, find the parser matching the url. | |
| 171 function detectFormat() { | |
| 172 for (var i = 0; i != self.parserInstances_.length; i++) { | |
| 173 var parser = self.parserInstances_[i]; | |
| 174 if (fileURL.match(parser.urlFilter)) { | |
| 175 // Create the metadata object as early as possible so that we can | |
| 176 // pass it with the error message. | |
| 177 metadata = parser.createDefaultMetadata(); | |
| 178 nextStep(parser); | |
| 179 return; | |
| 180 } | |
| 181 } | |
| 182 onError('unsupported format'); | |
| 183 }, | |
| 184 | |
| 185 // Step two, turn the url into an entry. | |
| 186 function getEntry(parser) { | |
| 187 webkitResolveLocalFileSystemURL( | |
| 188 fileURL, | |
| 189 function(entry) { nextStep(entry, parser) }, | |
| 190 onError); | |
| 191 }, | |
| 192 | |
| 193 // Step three, turn the entry into a file. | |
| 194 function getFile(entry, parser) { | |
| 195 entry.file(function(file) { nextStep(file, parser) }, onError); | |
| 196 }, | |
| 197 | |
| 198 // Step four, parse the file content. | |
| 199 function parseContent(file, parser) { | |
| 200 metadata.fileSize = file.size; | |
| 201 try { | |
| 202 parser.parse(file, metadata, callback, onError); | |
| 203 } catch (e) { | |
| 204 onError(e.stack); | |
| 205 } | |
| 206 } | |
| 207 ]; | |
| 208 | |
| 209 nextStep(); | |
| 210 }; | |
| 211 | |
| 212 // Webworker spec says that the worker global object is called self. That's | |
| 213 // a terrible name since we use it all over the chrome codebase to capture | |
| 214 // the 'this' keyword in lambdas. | |
| 215 var global = self; | |
| 216 | |
| 217 if (global.constructor.name == 'SharedWorkerGlobalScope') { | |
| 218 global.addEventListener('connect', function(e) { | |
| 219 var port = e.ports[0]; | |
| 220 new MetadataDispatcher(port); | |
| 221 port.start(); | |
| 222 }); | |
| 223 } else { | |
| 224 // Non-shared worker. | |
| 225 new MetadataDispatcher(global); | |
| 226 } | |
| OLD | NEW |