| 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 /** | |
| 6 * This variable structure is here to document the structure that the template | |
| 7 * expects to correctly populate the page. | |
| 8 */ | |
| 9 var extensionDataFormat = { | |
| 10 'developerMode': false, | |
| 11 'extensions': [ | |
| 12 { | |
| 13 'id': '0000000000000000000000000000000000000000', | |
| 14 'name': 'Google Chrome', | |
| 15 'description': 'Extension long format description', | |
| 16 'version': '1.0.231', | |
| 17 'mayDisable': 'true', | |
| 18 'enabled': 'true', | |
| 19 'terminated': 'false', | |
| 20 'enabledIncognito': 'false', | |
| 21 'wantsFileAccess': 'false', | |
| 22 'allowFileAccess': 'false', | |
| 23 'allow_reload': true, | |
| 24 'is_hosted_app': false, | |
| 25 'order': 1, | |
| 26 'options_url': 'options.html', | |
| 27 'enable_show_button': false, | |
| 28 'icon': 'relative-path-to-icon.png', | |
| 29 // TODO(aa): It would be nice to also render what type of view each one | |
| 30 // is, like 'toolstrip', 'background', etc. Eventually, if we can also | |
| 31 // debug and inspect content scripts, maybe we don't need to list the | |
| 32 // components, just the views. | |
| 33 'views': [ | |
| 34 { | |
| 35 'path': 'toolstrip.html', | |
| 36 'renderViewId': 1, | |
| 37 'renderProcessId': 1, | |
| 38 'incognito': false | |
| 39 }, | |
| 40 { | |
| 41 'path': 'background.html', | |
| 42 'renderViewId': 2, | |
| 43 'renderProcessId': 1, | |
| 44 'incognito': false | |
| 45 } | |
| 46 ] | |
| 47 }, | |
| 48 { | |
| 49 'id': '0000000000000000000000000000000000000001', | |
| 50 'name': 'RSS Subscriber', | |
| 51 'description': 'Extension long format description', | |
| 52 'version': '1.0.231', | |
| 53 'mayDisable': 'true', | |
| 54 'enabled': 'true', | |
| 55 'terminated': 'false', | |
| 56 'enabledIncognito': 'false', | |
| 57 'wantsFileAccess': 'false', | |
| 58 'allowFileAccess': 'false', | |
| 59 'allow_reload': false, | |
| 60 'is_hosted_app': false, | |
| 61 'order': 2, | |
| 62 'icon': '', | |
| 63 "hasPopupAction": false | |
| 64 } | |
| 65 ] | |
| 66 }; | |
| 67 | |
| 68 // Keeps track of whether the developer mode subsection has been made visible | |
| 69 // (expanded) or not. | |
| 70 var devModeExpanded = false; | |
| 71 | |
| 72 /** | |
| 73 * Toggles the devModeExpanded, and notifies the c++ WebUI to toggle the | |
| 74 * extensions.ui.developer_mode which saved in the preferences. | |
| 75 */ | |
| 76 function toggleDevModeExpanded() { | |
| 77 devModeExpanded = !devModeExpanded; | |
| 78 chrome.send('toggleDeveloperMode', []); | |
| 79 } | |
| 80 | |
| 81 /** | |
| 82 * Takes the |extensionsData| input argument which represents data about the | |
| 83 * currently installed/running extensions and populates the html jstemplate with | |
| 84 * that data. It expects an object structure like the above. | |
| 85 * @param {Object} extensionsData Detailed info about installed extensions | |
| 86 */ | |
| 87 function renderTemplate(extensionsData) { | |
| 88 // Sort by order specified in the data or (if equal) then sort by | |
| 89 // extension name. | |
| 90 var locale = new v8Locale(window.navigator.language); | |
| 91 var coll = locale.createCollator(); | |
| 92 extensionsData.extensions.sort(function(a, b) { | |
| 93 if (a.order == b.order) { | |
| 94 return coll.compare(a.name, b.name); | |
| 95 } | |
| 96 return a.order < b.order ? -1 : 1; | |
| 97 }); | |
| 98 | |
| 99 // This is the javascript code that processes the template: | |
| 100 var input = new JsEvalContext(extensionsData); | |
| 101 var output = $('extensionTemplate'); | |
| 102 jstProcess(input, output); | |
| 103 | |
| 104 // Hook up the handlers now that the template is processed. | |
| 105 $('load-extension').addEventListener('click', loadExtension); | |
| 106 $('show-pack-dialog').addEventListener('click', showPackDialog); | |
| 107 $('auto-update').addEventListener('click', autoUpdate); | |
| 108 | |
| 109 addHandlerByClass('developer-mode-image', 'click', toggleDeveloperMode); | |
| 110 addHandlerByClass('developer-mode-link', 'click', toggleDeveloperMode); | |
| 111 addHandlerByClass('extension-path', 'click', selectExtensionPath); | |
| 112 addHandlerByClass('private-key-path', 'click', selectPrivateKeyPath); | |
| 113 addHandlerByClass('pack-extension', 'click', packExtension); | |
| 114 addHandlerByClass('hide-pack-dialog', 'click', hidePackDialog); | |
| 115 addHandlerByClass('options-url', 'click', handleOptions); | |
| 116 addHandlerByClass('reload-extension', 'click', handleReloadExtension); | |
| 117 addHandlerByClass('disable-extension', 'click', handleDisableExtension); | |
| 118 addHandlerByClass('enable-extension', 'click', handleEnableExtension); | |
| 119 addHandlerByClass('uninstall-extension', 'click', handleUninstallExtension); | |
| 120 addHandlerByClass('show-button', 'click', handleShowButton); | |
| 121 addHandlerByClass('inspect-message', 'click', handleInspectMessage); | |
| 122 addHandlerByClass('toggle-incognito', 'change', | |
| 123 handleToggleExtensionIncognito); | |
| 124 addHandlerByClass('file-access', 'change', | |
| 125 handleToggleAllowFileAccess); | |
| 126 } | |
| 127 | |
| 128 /** | |
| 129 * Asks the C++ ExtensionDOMHandler to get details about the installed | |
| 130 * extensions and return detailed data about the configuration. The | |
| 131 * ExtensionDOMHandler should reply to returnExtensionsData() (below). | |
| 132 */ | |
| 133 function requestExtensionsData() { | |
| 134 chrome.send('requestExtensionsData', []); | |
| 135 } | |
| 136 | |
| 137 // Used for observing function of the backend datasource for this page by | |
| 138 // tests. | |
| 139 var webui_responded_ = false; | |
| 140 | |
| 141 // Used to only do some work the first time we render. | |
| 142 var rendered_once_ = false; | |
| 143 | |
| 144 /** | |
| 145 * Called by the web_ui_ to re-populate the page with data representing | |
| 146 * the current state of installed extensions. | |
| 147 */ | |
| 148 function returnExtensionsData(extensionsData){ | |
| 149 webui_responded_ = true; | |
| 150 devModeExpanded = extensionsData.developerMode; | |
| 151 | |
| 152 var bodyContainer = $('body-container'); | |
| 153 var body = document.body; | |
| 154 | |
| 155 // Set all page content to be visible so we can measure heights. | |
| 156 bodyContainer.style.visibility = 'hidden'; | |
| 157 body.className = ''; | |
| 158 var slidables = document.getElementsByClassName('showInDevMode'); | |
| 159 for (var i = 0; i < slidables.length; i++) | |
| 160 slidables[i].style.height = 'auto'; | |
| 161 | |
| 162 renderTemplate(extensionsData); | |
| 163 | |
| 164 // Explicitly set the height for each element that wants to be 'slid' in and | |
| 165 // out when the devModeExpanded is toggled. | |
| 166 var slidables = document.getElementsByClassName('showInDevMode'); | |
| 167 for (var i = 0; i < slidables.length; i++) | |
| 168 slidables[i].style.height = slidables[i].offsetHeight + 'px'; | |
| 169 | |
| 170 // Hide all the incognito warnings that are attached to the wrong extension | |
| 171 // ID, which can happen when an extension is added or removed. | |
| 172 var warnings = document.getElementsByClassName('incognitoWarning'); | |
| 173 for (var i = 0; i < warnings.length; i++) { | |
| 174 var extension = warnings[i]; | |
| 175 while (extension.className != "extension") | |
| 176 extension = extension.parentNode; | |
| 177 | |
| 178 if (extension.extensionId != warnings[i].attachedExtensionId) { | |
| 179 warnings[i].style.display = "none"; | |
| 180 warnings[i].style.opacity = "0"; | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 // Reset visibility of page based on the current dev mode. | |
| 185 $('collapse').style.display = devModeExpanded ? 'inline' : 'none'; | |
| 186 $('expand').style.display = devModeExpanded ? 'none' : 'inline'; | |
| 187 bodyContainer.style.visibility = 'visible'; | |
| 188 body.className = devModeExpanded ? | |
| 189 'showDevModeInitial' : 'hideDevModeInitial'; | |
| 190 | |
| 191 if (rendered_once_) | |
| 192 return; | |
| 193 | |
| 194 // Blech, JSTemplate always inserts the strings as text, which is usually a | |
| 195 // feature, but in this case it contains HTML that we want to be rendered. | |
| 196 var elm = $('try-gallery'); | |
| 197 if (elm) | |
| 198 elm.innerHTML = elm.textContent; | |
| 199 | |
| 200 elm = $('get-moar-extensions'); | |
| 201 if (elm) | |
| 202 elm.innerHTML = elm.textContent; | |
| 203 | |
| 204 rendered_once_ = true; | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Tell the C++ ExtensionDOMHandler to inspect the page detailed in |viewData|. | |
| 209 */ | |
| 210 function handleInspectMessage(event) { | |
| 211 // TODO(aa): This is ghetto, but WebUIBindings doesn't support sending | |
| 212 // anything other than arrays of strings, and this is all going to get | |
| 213 // replaced with V8 extensions soon anyway. | |
| 214 chrome.send('inspect', [ | |
| 215 String(this.extensionView.renderProcessId), | |
| 216 String(this.extensionView.renderViewId) | |
| 217 ]); | |
| 218 event.preventDefault(); | |
| 219 } | |
| 220 | |
| 221 /** | |
| 222 * Handles a 'reload' link getting clicked. | |
| 223 */ | |
| 224 function handleReloadExtension(event) { | |
| 225 // Tell the C++ ExtensionDOMHandler to reload the extension. | |
| 226 chrome.send('reload', [this.extensionId]); | |
| 227 event.preventDefault(); | |
| 228 } | |
| 229 | |
| 230 /** | |
| 231 * Handles a 'disable' link getting clicked. | |
| 232 */ | |
| 233 function handleDisableExtension(event) { | |
| 234 sendEnableExtension(this, false); | |
| 235 event.preventDefault(); | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 * Handles an 'enable' link getting clicked. | |
| 240 */ | |
| 241 function handleEnableExtension(event) { | |
| 242 sendEnableExtension(this, true); | |
| 243 event.preventDefault(); | |
| 244 } | |
| 245 | |
| 246 /** | |
| 247 * Peforms the actual work to enable or disable an extension. | |
| 248 */ | |
| 249 function sendEnableExtension(node, enable) { | |
| 250 // Tell the C++ ExtensionDOMHandler to reload the extension. | |
| 251 chrome.send('enable', [node.extensionId, String(enable)]); | |
| 252 requestExtensionsData(); | |
| 253 } | |
| 254 | |
| 255 /** | |
| 256 * Handles the 'enableIncognito' checkbox getting changed. | |
| 257 */ | |
| 258 function handleToggleExtensionIncognito(event) { | |
| 259 var warning = this; | |
| 260 | |
| 261 while (warning.className != "extension") | |
| 262 warning = warning.parentNode; | |
| 263 | |
| 264 warning = warning.getElementsByClassName("incognitoWarning")[0]; | |
| 265 if (!this.checked) { | |
| 266 warning.style.display = "none"; | |
| 267 warning.style.opacity = "0"; | |
| 268 } else { | |
| 269 warning.attachedExtensionId = this.extensionId; | |
| 270 warning.style.display = "block"; | |
| 271 | |
| 272 // Must set the opacity later. Otherwise, the fact that the display is | |
| 273 // changing causes the animation to not happen. | |
| 274 window.setTimeout(function() { | |
| 275 warning.style.opacity = "1"; | |
| 276 }, 0); | |
| 277 } | |
| 278 | |
| 279 chrome.send('enableIncognito', [this.extensionId, String(this.checked)]); | |
| 280 event.preventDefault(); | |
| 281 } | |
| 282 | |
| 283 /** | |
| 284 * Handles the 'allowFileAccess' checkbox getting changed. | |
| 285 */ | |
| 286 function handleToggleAllowFileAccess(event) { | |
| 287 chrome.send('allowFileAccess', [this.extensionId, String(this.checked)]); | |
| 288 event.preventDefault(); | |
| 289 } | |
| 290 | |
| 291 /** | |
| 292 * Handles an 'uninstall' link getting clicked. | |
| 293 */ | |
| 294 function handleUninstallExtension(event) { | |
| 295 chrome.send('uninstall', [this.extensionId]); | |
| 296 event.preventDefault(); | |
| 297 } | |
| 298 | |
| 299 /** | |
| 300 * Handles an 'options' link getting clicked. | |
| 301 */ | |
| 302 function handleOptions(event) { | |
| 303 chrome.send('options', [this.extensionId]); | |
| 304 event.preventDefault(); | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * Handles a 'show button' link getting clicked. | |
| 309 */ | |
| 310 function handleShowButton(event) { | |
| 311 chrome.send('showButton', [this.extensionId]); | |
| 312 event.preventDefault(); | |
| 313 } | |
| 314 | |
| 315 /** | |
| 316 * Utility function which asks the C++ to show a platform-specific file select | |
| 317 * dialog, and fire |callback| with the |filePath| that resulted. |selectType| | |
| 318 * can be either 'file' or 'folder'. |operation| can be 'load', 'packRoot', | |
| 319 * or 'pem' which are signals to the C++ to do some operation-specific | |
| 320 * configuration. | |
| 321 */ | |
| 322 function showFileDialog(selectType, operation, callback) { | |
| 323 handleFilePathSelected = function(filePath) { | |
| 324 callback(filePath); | |
| 325 handleFilePathSelected = function() {}; | |
| 326 }; | |
| 327 | |
| 328 chrome.send('selectFilePath', [selectType, operation]); | |
| 329 } | |
| 330 | |
| 331 /** | |
| 332 * Handles the "Load extension..." button being pressed. | |
| 333 */ | |
| 334 function loadExtension() { | |
| 335 showFileDialog('folder', 'load', function(filePath) { | |
| 336 chrome.send('load', [String(filePath)]); | |
| 337 }); | |
| 338 } | |
| 339 | |
| 340 /** | |
| 341 * Handles the "Pack extension..." button being pressed. | |
| 342 */ | |
| 343 function packExtension() { | |
| 344 var extensionPath = $('extensionPathText').value; | |
| 345 var privateKeyPath = $('privateKeyPath').value; | |
| 346 chrome.send('pack', [extensionPath, privateKeyPath]); | |
| 347 } | |
| 348 | |
| 349 /** | |
| 350 * Shows to modal HTML pack dialog. | |
| 351 */ | |
| 352 function showPackDialog() { | |
| 353 $('dialogBackground').style.display = '-webkit-box'; | |
| 354 } | |
| 355 | |
| 356 /** | |
| 357 * Hides the pack dialog. | |
| 358 */ | |
| 359 function hidePackDialog() { | |
| 360 $('dialogBackground').style.display = 'none' | |
| 361 } | |
| 362 | |
| 363 /* | |
| 364 * Toggles visibility of the developer mode. | |
| 365 */ | |
| 366 function toggleDeveloperMode() { | |
| 367 toggleDevModeExpanded(); | |
| 368 | |
| 369 $('collapse').style.display = devModeExpanded ? 'inline' : 'none'; | |
| 370 $('expand').style.display = devModeExpanded ? 'none' : 'inline'; | |
| 371 | |
| 372 document.body.className = | |
| 373 devModeExpanded ? 'showDevMode' : 'hideDevMode'; | |
| 374 } | |
| 375 | |
| 376 /** | |
| 377 * Pop up a select dialog to capture the extension path. | |
| 378 */ | |
| 379 function selectExtensionPath() { | |
| 380 showFileDialog('folder', 'packRoot', function(filePath) { | |
| 381 $('extensionPathText').value = filePath; | |
| 382 }); | |
| 383 } | |
| 384 | |
| 385 /** | |
| 386 * Pop up a select dialog to capture the private key path. | |
| 387 */ | |
| 388 function selectPrivateKeyPath() { | |
| 389 showFileDialog('file', 'pem', function(filePath) { | |
| 390 $('privateKeyPath').value = filePath; | |
| 391 }); | |
| 392 } | |
| 393 | |
| 394 /** | |
| 395 * Handles the "Update extensions now" button being pressed. | |
| 396 */ | |
| 397 function autoUpdate() { | |
| 398 chrome.send('autoupdate', []); | |
| 399 } | |
| 400 | |
| 401 /** | |
| 402 * Convenience routine to hook up nodes duplicated by jstemplate to | |
| 403 * event handlers using the class attribute to identify the set of nodes. | |
| 404 */ | |
| 405 function addHandlerByClass(name, event, handler) { | |
| 406 var elements = document.getElementsByClassName(name); | |
| 407 for (var i = 0; i < elements.length; ++i) { | |
| 408 elements[i].addEventListener(event, handler); | |
| 409 } | |
| 410 } | |
| 411 | |
| 412 document.addEventListener('DOMContentLoaded', requestExtensionsData); | |
| OLD | NEW |