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