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 |