Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / metadata / metadata_dispatcher.js
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.
4
5 'use strict';
6
7 /**
8  * Protocol + host parts of extension URL.
9  * @type {string}
10  * @const
11  */
12 var FILE_MANAGER_HOST = 'chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj';
13
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
16 // line.
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');
20
21 /**
22  * Dispatches metadata requests to the correct parser.
23  *
24  * @param {Object} port Worker port.
25  * @constructor
26  */
27 function MetadataDispatcher(port) {
28   this.port_ = port;
29   this.port_.onmessage = this.onMessage.bind(this);
30
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');
37
38   var patterns = [];
39
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);
46   }
47
48   this.parserRegexp_ = new RegExp('(' + patterns.join('|') + ')', 'i');
49
50   this.messageHandlers_ = {
51     init: this.init_.bind(this),
52     request: this.request_.bind(this)
53   };
54 }
55
56 /**
57  * List of registered parser classes.
58  * @private
59  */
60 MetadataDispatcher.parserClasses_ = [];
61
62 /**
63  * @param {function} parserClass Parser constructor function.
64  */
65 MetadataDispatcher.registerParserClass = function(parserClass) {
66   MetadataDispatcher.parserClasses_.push(parserClass);
67 };
68
69 /**
70  * Verbose logging for the dispatcher.
71  *
72  * Individual parsers also take this as their default verbosity setting.
73  */
74 MetadataDispatcher.prototype.verbose = false;
75
76 /**
77  * |init| message handler.
78  * @private
79  */
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_);
85 };
86
87 /**
88  * |request| message handler.
89  * @param {string} fileURL File URL.
90  * @private
91  */
92 MetadataDispatcher.prototype.request_ = function(fileURL) {
93   try {
94     this.processOneFile(fileURL, function callback(metadata) {
95         this.postMessage('result', [fileURL, metadata]);
96     }.bind(this));
97   } catch (ex) {
98     this.error(fileURL, ex);
99   }
100 };
101
102 /**
103  * Indicate to the caller that an operation has failed.
104  *
105  * No other messages relating to the failed operation should be sent.
106  * @param {...Object} var_args Arguments.
107  */
108 MetadataDispatcher.prototype.error = function(var_args) {
109   var ary = Array.apply(null, arguments);
110   this.postMessage('error', ary);
111 };
112
113 /**
114  * Send a log message to the caller.
115  *
116  * Callers must not parse log messages for control flow.
117  * @param {...Object} var_args Arguments.
118  */
119 MetadataDispatcher.prototype.log = function(var_args) {
120   var ary = Array.apply(null, arguments);
121   this.postMessage('log', ary);
122 };
123
124 /**
125  * Send a log message to the caller only if this.verbose is true.
126  * @param {...Object} var_args Arguments.
127  */
128 MetadataDispatcher.prototype.vlog = function(var_args) {
129   if (this.verbose)
130     this.log.apply(this, arguments);
131 };
132
133 /**
134  * Post a properly formatted message to the caller.
135  * @param {string} verb Message type descriptor.
136  * @param {Array.<Object>} args Arguments array.
137  */
138 MetadataDispatcher.prototype.postMessage = function(verb, args) {
139   this.port_.postMessage({verb: verb, arguments: args});
140 };
141
142 /**
143  * Message handler.
144  * @param {Event} event Event object.
145  */
146 MetadataDispatcher.prototype.onMessage = function(event) {
147   var data = event.data;
148
149   if (this.messageHandlers_.hasOwnProperty(data.verb)) {
150     this.messageHandlers_[data.verb].apply(this, data.arguments);
151   } else {
152     this.log('Unknown message from client: ' + data.verb, data);
153   }
154 };
155
156 /**
157  * @param {string} fileURL File URL.
158  * @param {function(Object)} callback Completion callback.
159  */
160 MetadataDispatcher.prototype.processOneFile = function(fileURL, callback) {
161   var self = this;
162   var currentStep = -1;
163
164   function nextStep(var_args) {
165     self.vlog('nextStep: ' + steps[currentStep + 1].name);
166     steps[++currentStep].apply(self, arguments);
167   }
168
169   var metadata;
170
171   function onError(err, stepName) {
172     self.error(fileURL, stepName || steps[currentStep].name, err.toString(),
173         metadata);
174   }
175
176   var steps =
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();
185           nextStep(parser);
186           return;
187         }
188       }
189       onError('unsupported format');
190     },
191
192     // Step two, turn the url into an entry.
193     function getEntry(parser) {
194       webkitResolveLocalFileSystemURL(
195           fileURL,
196           function(entry) { nextStep(entry, parser) },
197           onError);
198     },
199
200     // Step three, turn the entry into a file.
201     function getFile(entry, parser) {
202       entry.file(function(file) { nextStep(file, parser) }, onError);
203     },
204
205     // Step four, parse the file content.
206     function parseContent(file, parser) {
207       metadata.fileSize = file.size;
208       try {
209         parser.parse(file, metadata, callback, onError);
210       } catch (e) {
211         onError(e.stack);
212       }
213     }
214   ];
215
216   nextStep();
217 };
218
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.
222 var global = self;
223
224 if (global.constructor.name == 'SharedWorkerGlobalScope') {
225   global.addEventListener('connect', function(e) {
226     var port = e.ports[0];
227     new MetadataDispatcher(port);
228     port.start();
229   });
230 } else {
231   // Non-shared worker.
232   new MetadataDispatcher(global);
233 }