Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / resources / translate.js
blob9d389ff87a44b268fff82b06da3db45ab1d0954f
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.
5 // This code is used in conjunction with the Google Translate Element script.
6 // It is executed in an isolated world of a page to translate it from one
7 // language to another.
8 // It should be included in the page before the Translate Element script.
10 var cr = cr || {};
12 /**
13  * An object to provide functions to interact with the Translate library.
14  * @type {object}
15  */
16 cr.googleTranslate = (function() {
17   /**
18    * The Translate Element library's instance.
19    * @type {object}
20    */
21   var lib;
23   /**
24    * A flag representing if the Translate Element library is initialized.
25    * @type {boolean}
26    */
27   var libReady = false;
29   /**
30    * Error definitions for |errorCode|. See chrome/common/translate_errors.h
31    * to modify the definition.
32    * @const
33    */
34   var ERROR = {
35     'NONE': 0,
36     'INITIALIZATION_ERROR': 2,
37     'UNSUPPORTED_LANGUAGE': 4,
38     'TRANSLATION_ERROR': 6,
39     'TRANSLATION_TIMEOUT': 7,
40     'UNEXPECTED_SCRIPT_ERROR': 8,
41     'BAD_ORIGIN': 9,
42     'SCRIPT_LOAD_ERROR': 10
43   };
45   /**
46    * Error code map from te.dom.DomTranslator.Error to |errorCode|.
47    * See also go/dom_translator.js in google3.
48    * @const
49    */
50   var TRANSLATE_ERROR_TO_ERROR_CODE_MAP = {
51     0: ERROR['NONE'],
52     1: ERROR['TRANSLATION_ERROR'],
53     2: ERROR['UNSUPPORTED_LANGUAGE']
54   };
56   /**
57    * An error code happened in translate.js and the Translate Element library.
58    */
59   var errorCode = ERROR['NONE'];
61   /**
62    * A flag representing if the Translate Element has finished a translation.
63    * @type {boolean}
64    */
65   var finished = false;
67   /**
68    * Counts how many times the checkLibReady function is called. The function
69    * is called in every 100 msec and counted up to 6.
70    * @type {number}
71    */
72   var checkReadyCount = 0;
74   /**
75    * Time in msec when this script is injected.
76    * @type {number}
77    */
78   var injectedTime = performance.now();
80   /**
81    * Time in msec when the Translate Element library is loaded completely.
82    * @type {number}
83    */
84   var loadedTime = 0.0;
86   /**
87    * Time in msec when the Translate Element library is initialized and ready
88    * for performing translation.
89    * @type {number}
90    */
91   var readyTime = 0.0;
93   /**
94    * Time in msec when the Translate Element library starts a translation.
95    * @type {number}
96    */
97   var startTime = 0.0;
99   /**
100    * Time in msec when the Translate Element library ends a translation.
101    * @type {number}
102    */
103   var endTime = 0.0;
105   function checkLibReady() {
106     if (lib.isAvailable()) {
107       readyTime = performance.now();
108       libReady = true;
109       return;
110     }
111     if (checkReadyCount++ > 5) {
112       errorCode = ERROR['TRANSLATION_TIMEOUT'];
113       return;
114     }
115     setTimeout(checkLibReady, 100);
116   }
118   function onTranslateProgress(progress, opt_finished, opt_error) {
119     finished = opt_finished;
120     // opt_error can be 'undefined'.
121     if (typeof opt_error == 'boolean' && opt_error) {
122       // TODO(toyoshim): Remove boolean case once a server is updated.
123       errorCode = ERROR['TRANSLATION_ERROR'];
124       // We failed to translate, restore so the page is in a consistent state.
125       lib.restore();
126     } else if (typeof opt_error == 'number' && opt_error != 0) {
127       errorCode = TRANSLATE_ERROR_TO_ERROR_CODE_MAP[opt_error];
128       lib.restore();
129     }
130     if (finished)
131       endTime = performance.now();
132   }
134   // Public API.
135   return {
136     /**
137      * Whether the library is ready.
138      * The translate function should only be called when |libReady| is true.
139      * @type {boolean}
140      */
141     get libReady() {
142       return libReady;
143     },
145     /**
146      * Whether the current translate has finished successfully.
147      * @type {boolean}
148      */
149     get finished() {
150       return finished;
151     },
153     /**
154      * Whether an error occured initializing the library of translating the
155      * page.
156      * @type {boolean}
157      */
158     get error() {
159       return errorCode != ERROR['NONE'];
160     },
162     /**
163      * Returns a number to represent error type.
164      * @type {number}
165      */
166     get errorCode() {
167       return errorCode;
168     },
170     /**
171      * The language the page translated was in. Is valid only after the page
172      * has been successfully translated and the original language specified to
173      * the translate function was 'auto'. Is empty otherwise.
174      * Some versions of Element library don't provide |getDetectedLanguage|
175      * function. In that case, this function returns 'und'.
176      * @type {boolean}
177      */
178     get sourceLang() {
179       if (!libReady || !finished || errorCode != ERROR['NONE'])
180         return '';
181       if (!lib.getDetectedLanguage)
182         return 'und'; // Defined as translate::kUnknownLanguageCode in C++.
183       return lib.getDetectedLanguage();
184     },
186     /**
187      * Time in msec from this script being injected to all server side scripts
188      * being loaded.
189      * @type {number}
190      */
191     get loadTime() {
192       if (loadedTime == 0)
193         return 0;
194       return loadedTime - injectedTime;
195     },
197     /**
198      * Time in msec from this script being injected to the Translate Element
199      * library being ready.
200      * @type {number}
201      */
202     get readyTime() {
203       if (!libReady)
204         return 0;
205       return readyTime - injectedTime;
206     },
208     /**
209      * Time in msec to perform translation.
210      * @type {number}
211      */
212     get translationTime() {
213       if (!finished)
214         return 0;
215       return endTime - startTime;
216     },
218     /**
219      * Translate the page contents.  Note that the translation is asynchronous.
220      * You need to regularly check the state of |finished| and |errorCode| to
221      * know if the translation finished or if there was an error.
222      * @param {string} originalLang The language the page is in.
223      * @param {string} targetLang The language the page should be translated to.
224      * @return {boolean} False if the translate library was not ready, in which
225      *                   case the translation is not started.  True otherwise.
226      */
227     translate: function(originalLang, targetLang) {
228       finished = false;
229       errorCode = ERROR['NONE'];
230       if (!libReady)
231         return false;
232       startTime = performance.now();
233       try {
234         lib.translatePage(originalLang, targetLang, onTranslateProgress);
235       } catch (err) {
236         console.error('Translate: ' + err);
237         errorCode = ERROR['UNEXPECTED_SCRIPT_ERROR'];
238         return false;
239       }
240       return true;
241     },
243     /**
244      * Reverts the page contents to its original value, effectively reverting
245      * any performed translation.  Does nothing if the page was not translated.
246      */
247     revert: function() {
248       lib.restore();
249     },
251     /**
252      * Entry point called by the Translate Element once it has been injected in
253      * the page.
254      */
255     onTranslateElementLoad: function() {
256       loadedTime = performance.now();
257       try {
258         lib = google.translate.TranslateService({
259           // translateApiKey is predefined by translate_script.cc.
260           'key': translateApiKey,
261           'useSecureConnection': true
262         });
263         translateApiKey = undefined;
264       } catch (err) {
265         errorCode = ERROR['INITIALIZATION_ERROR'];
266         translateApiKey = undefined;
267         return;
268       }
269       // The TranslateService is not available immediately as it needs to start
270       // Flash.  Let's wait until it is ready.
271       checkLibReady();
272     },
274     /**
275      * Entry point called by the Translate Element when it want to load an
276      * external CSS resource into the page.
277      * @param {string} url URL of an external CSS resource to load.
278      */
279     onLoadCSS: function(url) {
280       var element = document.createElement('link');
281       element.type = 'text/css';
282       element.rel = 'stylesheet';
283       element.charset = 'UTF-8';
284       element.href = url;
285       document.head.appendChild(element);
286     },
288     /**
289      * Entry point called by the Translate Element when it want to load and run
290      * an external JavaScript on the page.
291      * @param {string} url URL of an external JavaScript to load.
292      */
293     onLoadJavascript: function(url) {
294       // securityOrigin is predefined by translate_script.cc.
295       if (url.indexOf(securityOrigin) != 0) {
296         console.error('Translate: ' + url + ' is not allowed to load.');
297         errorCode = ERROR['BAD_ORIGIN'];
298         return;
299       }
300       var xhr = new XMLHttpRequest();
301       xhr.open('GET', url, true);
302       xhr.onreadystatechange = function() {
303         if (this.readyState != this.DONE)
304           return;
305         if (this.status != 200) {
306           errorCode = ERROR['SCRIPT_LOAD_ERROR'];
307           return;
308         }
309         eval(this.responseText);
310       }
311       xhr.send();
312     }
313   };
314 })();