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 // 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. 9 10 var cr = {}; 11 12 cr.googleTranslate = (function(key) { 13 /** 14 * The Translate Element library's instance. 15 * @type {object} 16 */ 17 var lib; 18 19 /** 20 * A flag representing if the Translate Element library is initialized. 21 * @type {boolean} 22 */ 23 var libReady = false; 24 25 /** 26 * A flag representing if the Translate Element library returns error while 27 * performing translation. Also it is set to true when the Translate Element 28 * library is not initialized in 600 msec from the library is loaded. 29 * @type {boolean} 30 */ 31 var error = false; 32 33 /** 34 * A flag representing if the Translate Element has finished a translation. 35 * @type {boolean} 36 */ 37 var finished = false; 38 39 /** 40 * Counts how many times the checkLibReady function is called. The function 41 * is called in every 100 msec and counted up to 6. 42 * @type {number} 43 */ 44 var checkReadyCount = 0; 45 46 /** 47 * Time in msec when this script is injected. 48 * @type {number} 49 */ 50 var injectedTime = performance.now(); 51 52 /** 53 * Time in msec when the Translate Element library is loaded completely. 54 * @type {number} 55 */ 56 var loadedTime = 0.0; 57 58 /** 59 * Time in msec when the Translate Element library is initialized and ready 60 * for performing translation. 61 * @type {number} 62 */ 63 var readyTime = 0.0; 64 65 /** 66 * Time in msec when the Translate Element library starts a translation. 67 * @type {number} 68 */ 69 var startTime = 0.0; 70 71 /** 72 * Time in msec when the Translate Element library ends a translation. 73 * @type {number} 74 */ 75 var endTime = 0.0; 76 77 // Create another call point for appendChild. 78 var head = document.head; 79 head._appendChild = head.appendChild; 80 81 // TODO(toyoshim): This is temporary solution and will be removed once server 82 // side fixed to use https always. See also, http://crbug.com/164584 . 83 function forceToHttps(url) { 84 if (url.indexOf('http:') == 0) 85 return url.replace('http', 'https'); 86 87 return url; 88 } 89 90 /** 91 * Inserts CSS element into the main world. 92 * @param {Object} child Link element for CSS. 93 */ 94 function insertCSS(child) { 95 child.href = forceToHttps(child.href); 96 head._appendChild(child); 97 } 98 99 /** 100 * Inserts JavaScript into the isolated world. 101 * @param {string} src JavaScript URL. 102 */ 103 function insertJS(src) { 104 var xhr = new XMLHttpRequest(); 105 xhr.open('GET', forceToHttps(src), true); 106 xhr.onreadystatechange = function() { 107 if (this.readyState != this.DONE || this.status != 200) 108 return; 109 eval(this.responseText); 110 } 111 xhr.send(); 112 } 113 114 /** 115 * Alternate implementation of appendChild. In the isolated world, appendChild 116 * for the first head element is replaced with this function in order to make 117 * CSS link tag and script tag injection work fine like the main world. 118 */ 119 head.appendChild = function(child) { 120 if (child.type == 'text/css') 121 insertCSS(child); 122 else 123 insertJS(child.src); 124 }; 125 126 function checkLibReady() { 127 if (lib.isAvailable()) { 128 readyTime = performance.now(); 129 libReady = true; 130 return; 131 } 132 if (checkReadyCount++ > 5) { 133 error = true; 134 return; 135 } 136 setTimeout(checkLibReady, 100); 137 } 138 139 function onTranslateProgress(progress, opt_finished, opt_error) { 140 finished = opt_finished; 141 // opt_error can be 'undefined'. 142 if (typeof opt_error == 'boolean' && opt_error) { 143 error = true; 144 // We failed to translate, restore so the page is in a consistent state. 145 lib.restore(); 146 } 147 if (finished) 148 endTime = performance.now(); 149 } 150 151 // Public API. 152 return { 153 /** 154 * Whether the library is ready. 155 * The translate function should only be called when |libReady| is true. 156 * @type {boolean} 157 */ 158 get libReady() { 159 return libReady; 160 }, 161 162 /** 163 * Whether the current translate has finished successfully. 164 * @type {boolean} 165 */ 166 get finished() { 167 return finished; 168 }, 169 170 /** 171 * Whether an error occured initializing the library of translating the 172 * page. 173 * @type {boolean} 174 */ 175 get error() { 176 return error; 177 }, 178 179 /** 180 * The language the page translated was in. Is valid only after the page 181 * has been successfully translated and the original language specified to 182 * the translate function was 'auto'. Is empty otherwise. 183 * Some versions of Element library don't provide |getDetectedLanguage| 184 * function. In that case, this function returns 'und'. 185 * @type {boolean} 186 */ 187 get sourceLang() { 188 if (!libReady || !finished || error) 189 return ''; 190 if (!lib.getDetectedLanguage) 191 return 'und'; // defined as chrome::kUnknownLanguageCode in C++ world. 192 return lib.getDetectedLanguage(); 193 }, 194 195 /** 196 * Time in msec from this script being injected to all server side scripts 197 * being loaded. 198 * @type {number} 199 */ 200 get loadTime() { 201 if (loadedTime == 0) 202 return 0; 203 return loadedTime - injectedTime; 204 }, 205 206 /** 207 * Time in msec from this script being injected to the Translate Element 208 * library being ready. 209 * @type {number} 210 */ 211 get readyTime() { 212 if (!libReady) 213 return 0; 214 return readyTime - injectedTime; 215 }, 216 217 /** 218 * Time in msec to perform translation. 219 * @type {number} 220 */ 221 get translationTime() { 222 if (!finished) 223 return 0; 224 return endTime - startTime; 225 }, 226 227 /** 228 * Translate the page contents. Note that the translation is asynchronous. 229 * You need to regularly check the state of |finished| and |error| to know 230 * if the translation finished or if there was an error. 231 * @param {string} originalLang The language the page is in. 232 * @param {string} targetLang The language the page should be translated to. 233 * @return {boolean} False if the translate library was not ready, in which 234 * case the translation is not started. True otherwise. 235 */ 236 translate: function(originalLang, targetLang) { 237 finished = false; 238 error = false; 239 if (!libReady) 240 return false; 241 startTime = performance.now(); 242 lib.translatePage(originalLang, targetLang, onTranslateProgress); 243 return true; 244 }, 245 246 /** 247 * Reverts the page contents to its original value, effectively reverting 248 * any performed translation. Does nothing if the page was not translated. 249 */ 250 revert: function() { 251 lib.restore(); 252 }, 253 254 /** 255 * Entry point called by the Translate Element once it has been injected in 256 * the page. 257 */ 258 onTranslateElementLoad: function() { 259 loadedTime = performance.now(); 260 try { 261 lib = google.translate.TranslateService({ 262 'key': key, 263 'useSecureConnection': true 264 }); 265 } catch (err) { 266 error = true; 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 } 273 }; 274 })/* Calling code '(|key|);' will be appended by TranslateHelper in C++ here. */ 275