Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: chrome/browser/resources/options/extension_list.js

Issue 7794023: Convert chrome://extensions to a settings page within the options pages. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Someone modified .grd between uploads :) Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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 cr.define('options', function() {
6 /**
7 * Creates a new list of extensions.
8 * @param {Object=} opt_propertyBag Optional properties.
9 * @constructor
10 * @extends {cr.ui.div}
11 */
12 var ExtensionsList = cr.ui.define('div');
13
14 var handleInstalled = false;
15
16 ExtensionsList.prototype = {
17 __proto__: HTMLDivElement.prototype,
18
19 /** @inheritDoc */
20 decorate: function() {
21 // Make sure developer mode section is set correctly as per saved setting.
Aaron Boodman 2011/08/31 18:22:44 This is a ridiculously long function. Maybe break
Finnur 2011/08/31 20:22:01 Done.
22 var toggleButton = $('toggle-dev-on');
23 var toggleSection = $('dev');
24 if (this.data_.developerMode) {
25 toggleSection.classList.add('dev-open');
26 toggleSection.classList.remove('dev-closed');
27 toggleButton.setAttribute("checked", "");
28 } else {
29 toggleSection.classList.remove('dev-open');
30 toggleSection.classList.add('dev-closed');
31 }
32
33 // Install handler for key presses.
34 if (!handleInstalled) {
35 document.addEventListener('keyup', this.upEventHandler_.bind(this));
36 document.addEventListener('mouseup', this.upEventHandler_.bind(this));
37 handleInstalled = true;
38 }
39
40 // Delete all child nodes before adding them back and while we are at it
41 // make a note of which ones were in expanded state (and which showing
42 // the warning) so we can restore those to the same state afterwards.
43 var showingDetails = [];
44 var showingWarning = [];
45 while (this.hasChildNodes()){
46 var child = this.firstChild;
47
48 // See if the item is expanded.
49 if (child.className.indexOf('extensionListItemExpanded') >= 0) {
50 showingDetails.push(child.id);
51 }
52 // See if the butterbar is showing.
53 var butterBar = document.getElementById(child.id + "_incognitoWarning");
54 if (!(butterBar === null) && butterBar.style.display == "block") {
55 showingWarning.push(child.id);
56 }
57
58 // Now we can delete it.
59 this.removeChild(child);
60 }
61
62 var minCheckboxWidth = 999999;
63 var maxCheckboxWidth = 0;
64
65 // Iterate over the extension data and add each item to the list.
66 for (var i = 0; i < this.data_.extensions.length; ++i) {
67 var extension = this.data_.extensions[i];
68 var id = extension.id;
69
70 var wrapper = this.ownerDocument.createElement('div');
71
72 // Figure out if the item should open expanded or not based on the state
73 // of things before we deleted the items.
74 var iter = showingDetails.length;
75 var expanded = false;
76 while (iter--) {
77 if (showingDetails[iter] == id) {
78 expanded = true;
79 break;
80 }
81 }
82 // Figure out if the butterbar should be showing.
83 iter = showingWarning.length;
84 var butterbar = false;
85 while (iter--) {
86 if (showingWarning[iter] == id) {
87 butterbar = true;
88 break;
89 }
90 }
91
92 wrapper.classList.add(expanded ? 'extensionListItemExpanded' :
93 'extensionListItemCollapsed');
94 if (!extension.enabled) {
95 wrapper.classList.add('disabled');
96 }
97 wrapper.id = id;
98 this.appendChild(wrapper);
99
100 var vbox_outer = this.ownerDocument.createElement('div');
101 vbox_outer.classList.add('vbox');
102 vbox_outer.classList.add('extensionListItem');
103 wrapper.appendChild(vbox_outer);
104
105 var hbox = this.ownerDocument.createElement('div');
106 hbox.classList.add('hbox');
107 vbox_outer.appendChild(hbox);
108
109 // Add a container div for the zippy, so we can extend the hit area.
110 var container = this.ownerDocument.createElement('div');
111 // Clicking anywhere on the div expands/collapses the details.
112 container.classList.add('extension-zippy-container');
113 container.addEventListener('click', this.handleZippyClick_.bind(this));
114 hbox.appendChild(container);
115
116 // On the far left we have the zippy icon.
117 div = this.ownerDocument.createElement('div');
118 div.id = id + '_zippy';
119 div.classList.add('extension-zippy-default');
120 div.classList.add(expanded ? 'extension-zippy-expanded' :
121 'extension-zippy-collapsed');
122 container.appendChild(div);
123
124 // Next to it, we have the extension icon.
125 icon = this.ownerDocument.createElement('img');
126 icon.classList.add('extension-icon');
127 icon.src = extension.icon;
128 hbox.appendChild(icon);
129
130 // Start a vertical box for showing the details.
131 var vbox = this.ownerDocument.createElement('div');
132 vbox.classList.add('vbox');
133 vbox.classList.add('stretch');
134 hbox.appendChild(vbox);
135
136 div = this.ownerDocument.createElement('div');
137 vbox.appendChild(div);
138
139 // Title comes next.
140 var title = this.ownerDocument.createElement('span');
141 title.classList.add('extension-title');
142 title.textContent = extension.name;
143 vbox.appendChild(title);
144
145 // Followed by version.
146 var version = this.ownerDocument.createElement('span');
147 version.classList.add('extension-version');
148 version.textContent = extension.version;
149 vbox.appendChild(version);
150
151 div = this.ownerDocument.createElement('div');
152 vbox.appendChild(div);
153
154 // And below that we have description (if provided).
155 if (extension.description.length > 0) {
156 var description = this.ownerDocument.createElement('span');
157 description.classList.add('extension-description');
158 description.textContent = extension.description;
159 vbox.appendChild(description);
160 }
161
162 // Immediately following the description, we have the
163 // Options link (optional).
164 if (extension.options_url) {
165 var link = this.ownerDocument.createElement('a');
166 link.classList.add('extension-links-trailing');
167 link.textContent = localStrings.getString('options');
168 link.href = "#";
169 link.addEventListener('click', this.handleOptions_.bind(this));
170 vbox.appendChild(link);
171 }
172
173 // Then the optional Visit Website link.
174 if (extension.homepageUrl) {
175 var link = this.ownerDocument.createElement('a');
176 link.classList.add('extension-links-trailing');
177 link.textContent = localStrings.getString("visitWebsite");
178 link.href = "#";
179 link.addEventListener('click', this.handleVisitWebsite_.bind(this));
180 vbox.appendChild(link);
181 }
182
183 // And now the details section that is normally hidden.
184 var details = this.ownerDocument.createElement('div');
185 details.classList.add('vbox');
186 vbox.appendChild(details);
187
188 this.decorateDetailsSection_(details, extension, expanded, butterbar);
189
190 // And on the right of the details we have the Enable/Enabled checkbox.
191 div = this.ownerDocument.createElement('div');
192 hbox.appendChild(div);
193
194 var section = this.ownerDocument.createElement('section');
195 section.classList.add('extension-enabling');
196 div.appendChild(section);
197
198 // The Enable checkbox.
199 var input = this.ownerDocument.createElement('input');
200 input.addEventListener('click', this.handleEnable_.bind(this));
201 input.type = "checkbox";
202 input.name = "toggle-" + id;
203 if (!extension.mayDisable) {
204 input.setAttribute("disabled", "");
205 }
206 if (extension.enabled) {
207 input.setAttribute("checked", "");
208 }
209 input.id = "toggle-" + id;
210 section.appendChild(input);
211 var label = this.ownerDocument.createElement('label');
212 label.classList.add('extension-enabling-label');
213 if (extension.enabled) {
214 label.classList.add('extension-enabling-label-bold');
215 }
216 label.setAttribute("for", "toggle-" + id);
217 label.id = "toggle-" + id + "-label";
218 if (extension.enabled) {
219 label.textContent = localStrings.getString("enabled");
220 } else {
221 label.textContent = localStrings.getString("enable");
222 }
223 section.appendChild(label);
224
225 if (label.offsetWidth > maxCheckboxWidth)
226 maxCheckboxWidth = label.offsetWidth;
227 if (label.offsetWidth < minCheckboxWidth)
228 minCheckboxWidth = label.offsetWidth;
229
230 // And, on the far right we have the uninstall button.
231 var button = this.ownerDocument.createElement('button');
232 button.classList.add('extension-delete');
233 button.id = id;
234 if (!extension.mayDisable) {
235 button.setAttribute("disabled", "");
236 }
237 button.textContent = localStrings.getString("remove");
238 button.addEventListener('click', this.handleUninstall_.bind(this));
239 hbox.appendChild(button);
240 }
241
242 // Do another pass, making sure checkboxes line up.
243 var difference = maxCheckboxWidth - minCheckboxWidth;
244 for (var i = 0; i < this.data_.extensions.length; ++i) {
245 var extension = this.data_.extensions[i];
246 var id = extension.id;
247 var label = $("toggle-" + id + "-label");
248 if (label.offsetWidth < maxCheckboxWidth) {
249 label.setAttribute("style", "margin-right: " +
250 difference.toString() + "px;");
251 }
252 }
253 },
254
255 /**
256 * Handles decorating the details section.
257 * @param {Element} details The div that the details should be attached to.
258 * @param {Object} extension The extension we are shoting the details for.
259 * @param {boolean} expanded Whether to show the details expanded or not.
260 * @param {boolean} showButterbar Whether to show the incognito warning or
261 * not.
262 * @private
263 */
264 decorateDetailsSection_: function(details, extension,
265 expanded, showButterbar) {
266 // This container div is needed because vbox display
267 // overrides display:hidden.
268 var details_contents = this.ownerDocument.createElement('div');
269 details_contents.classList.add(expanded ? 'extension-details-visible' :
270 'extension-details-hidden');
271 details_contents.id = extension.id + '_details';
272 details.appendChild(details_contents);
273
274 var div = this.ownerDocument.createElement('div');
275 div.classList.add('gray-text');
276 details_contents.appendChild(div);
277
278 // Keep track of how many items we'll show in the details section.
279 var itemsShown = 0;
280
281 if (this.data_.developerMode) {
282 // First we have the id.
283 var content = this.ownerDocument.createElement('div');
284 content.textContent = localStrings.getString("extensionId") +
285 " " + extension.id;
286 div.appendChild(content);
287 itemsShown++;
288
289 // Then, the path, if provided by unpacked extension.
290 if (extension.isUnpacked) {
291 content = this.ownerDocument.createElement('div');
292 content.textContent = localStrings.getString("extensionPath") +
293 " " + extension.path;
294 div.appendChild(content);
295 itemsShown++;
296 }
297
298 // Then, the 'managed, cannot uninstall/disable' message.
299 if (!extension.mayDisable) {
300 content = this.ownerDocument.createElement('div');
301 content.textContent = localStrings.getString("policyControlled");
302 div.appendChild(content);
303 itemsShown++;
304 }
305
306 // Then active views:
307 if (extension.views.length > 0) {
308 var table = this.ownerDocument.createElement('table');
309 table.classList.add('extension-inspect-table');
310 div.appendChild(table);
311 var tr = this.ownerDocument.createElement('tr');
312 table.appendChild(tr);
313 var td = this.ownerDocument.createElement('td');
314 td.classList.add('extension-inspect-left-column');
315 tr.appendChild(td);
316 var span = this.ownerDocument.createElement('span');
317 td.appendChild(span);
318 span.textContent = localStrings.getString("inspectViews");
319
320 td = this.ownerDocument.createElement('td');
321 for (var i = 0; i < extension.views.length; ++i) {
322 // Then active views:
323 content = this.ownerDocument.createElement('div');
324 var link = this.ownerDocument.createElement('a');
325 link.classList.add('extension-links-view');
326 link.textContent = extension.views[i].path;
327 link.id = extension.id;
328 link.href = "#";
329 link.addEventListener('click', this.sendInspectMessage_.bind(this));
330 content.appendChild(link);
331 td.appendChild(content);
332 tr.appendChild(td);
333
334 itemsShown++;
335 }
336 }
337 }
338
339 content = this.ownerDocument.createElement('div');
340 details_contents.appendChild(content);
341
342 // Then Reload:
343 if (extension.enabled && extension.allow_reload) {
344 var link = this.ownerDocument.createElement('a');
345 link.classList.add('extension-links-trailing');
346 link.textContent = localStrings.getString('reload');
347 link.id = extension.id;
348 link.href = "#";
349 link.addEventListener('click', this.handleReload_.bind(this));
350 content.appendChild(link);
351 itemsShown++;
352 }
353
354 // Then Show (Browser Action) Button:
355 if (extension.enabled && extension.enable_show_button) {
356 link = this.ownerDocument.createElement('a');
357 link.classList.add('extension-links-trailing');
358 link.textContent = localStrings.getString('showButton');
359 link.id = extension.id;
360 link.href = "#";
361 link.addEventListener('click', this.handleShowButton_.bind(this));
362 content.appendChild(link);
363 itemsShown++;
364 }
365
366 if (extension.enabled && !extension.wantsFileAccess) {
367 // The 'allow in incognito' checkbox.
368 var label = this.ownerDocument.createElement('label');
369 label.classList.add('extension-checkbox-label');
370 content.appendChild(label);
371 var input = this.ownerDocument.createElement('input');
372 input.addEventListener('click',
373 this.handleToggleEnableIncognito_.bind(this));
374 input.id = extension.id;
375 input.type = "checkbox";
376 if (extension.enabledIncognito) {
377 input.setAttribute("checked", "");
378 }
379 label.appendChild(input);
380 var span = this.ownerDocument.createElement('span');
381 span.classList.add('extension-checkbox-span');
382 span.textContent = localStrings.getString('enableIncognito');
383 label.appendChild(span);
384 itemsShown++;
385 }
386
387 if (extension.enabled && !extension.wantsFileAccess) {
388 // The 'allow access to file URLs' checkbox.
389 label = this.ownerDocument.createElement('label');
390 label.classList.add('extension-checkbox-label');
391 content.appendChild(label);
392 var input = this.ownerDocument.createElement('input');
393 input.addEventListener('click',
394 this.handleToggleAllowFileUrls_.bind(this));
395 input.id = extension.id;
396 input.type = "checkbox";
397 if (extension.allowFileAccess) {
398 input.setAttribute("checked", "");
399 }
400 label.appendChild(input);
401 var span = this.ownerDocument.createElement('span');
402 span.classList.add('extension-checkbox-span');
403 span.textContent = localStrings.getString('allowFileAccess');
404 label.appendChild(span);
405 itemsShown++;
406 }
407
408 if (extension.enabled && !extension.is_hosted_app) {
409 // And add a hidden warning message for allowInIncognito.
410 content = this.ownerDocument.createElement('div');
411 content.id = extension.id + "_incognitoWarning";
412 content.classList.add('butter-bar');
413 content.style.display = showButterbar ? "block" : "none";
414 details_contents.appendChild(content);
415
416 var span = this.ownerDocument.createElement('span');
417 span.innerHTML = localStrings.getString('incognitoWarning');
418 content.appendChild(span);
419 itemsShown++;
420 }
421
422 var zippy = extension.id + "_zippy";
423 $(zippy).style.display = (itemsShown > 0) ? "block" : "none";
424 },
425
426 /**
427 * A lookup helper function to find an extension based on an id.
428 * @param {string} id The |id| of the extension to look up.
429 * @private
430 */
431 getExtensionWithId_: function(id) {
432 for (var i = 0; i < this.data_.extensions.length; ++i) {
433 if (this.data_.extensions[i].id == id)
434 return this.data_.extensions[i];
435 }
436 return null;
437 },
438
439 /**
440 * A lookup helper function to find the first node that has an id (starting
441 * at |node| and going up the parent chain.
442 * @param {Element} node The node to start looking at.
443 * @private
444 */
445 findIdNode_: function(node) {
446 while (node.id.length == 0) {
447 node = node.parentNode;
448 if (!node)
449 return null;
450 }
451 return node;
452 },
453
454 /**
455 * Handles the mouseclick on the zippy icon (that expands and collapses the
456 * details section).
457 * @param {Event} e Change event.
458 * @private
459 */
460 handleZippyClick_: function(e) {
461 var node = this.findIdNode_(e.target.parentNode);
462 var iter = this.firstChild;
463 while (iter) {
464 var zippy = this.ownerDocument.getElementById(iter.id + '_zippy');
465 var details = this.ownerDocument.getElementById(iter.id + '_details');
466 if (iter.id == node.id) {
467 // Toggle visibility.
468 if (iter.classList.contains('extensionListItemExpanded')) {
469 // Hide yo kids! Hide yo wife!
470 zippy.classList.remove('extension-zippy-expanded');
471 zippy.classList.add('extension-zippy-collapsed');
472 details.classList.remove('extension-details-visible');
473 details.classList.add('extension-details-hidden');
474 iter.classList.remove('extensionListItemExpanded');
475 iter.classList.add('extensionListItemCollapsed');
476
477 // Hide yo incognito warning.
478 var butterBar = this.ownerDocument.getElementById(
479 iter.id + '_incognitoWarning');
480 if (!(butterBar === null)) {
481 butterBar.style.display = "none";
482 }
483 } else {
484 // Show the contents.
485 zippy.classList.remove('extension-zippy-collapsed');
486 zippy.classList.add('extension-zippy-expanded');
487 details.classList.remove('extension-details-hidden');
488 details.classList.add('extension-details-visible');
489 iter.classList.remove('extensionListItemCollapsed');
490 iter.classList.add('extensionListItemExpanded');
491 }
492 }
493 iter = iter.nextSibling;
494 }
495 },
496
497 /**
498 * Handles the mouse-up and keyboard-up events. This is used to limit the
499 * number of items to show in the list, when the user is searching for items
500 * with the search box. Otherwise, if one match is found, the whole list of
501 * extensions would be shown when we only want the matching items to be
502 * found.
503 * @param {Event} e Change event.
504 * @private
505 */
506 upEventHandler_: function(e) {
507 var searchString =
508 document.getElementById('search-field').value.toLowerCase();
509 var child = this.firstChild;
510 while (child){
511 var extension = this.getExtensionWithId_(child.id);
512 if (searchString.length == 0) {
513 // Show all.
514 child.classList.remove('search-suppress');
515 } else {
516 // If the search string does not appear within the text of the
517 // extension, then hide it.
518 if ((extension.name.toLowerCase().indexOf(searchString) < 0) &&
519 (extension.version.toLowerCase().indexOf(searchString) < 0) &&
520 (extension.description.toLowerCase().indexOf(searchString) < 0)) {
521 // Hide yo extension!
522 child.classList.add('search-suppress');
523 } else {
524 // Show yourself!
525 child.classList.remove('search-suppress');
526 }
527 }
528 child = child.nextSibling;
529 }
530 },
531
532 /**
533 * Handles the Reload Extension functionality.
534 * @param {Event} e Change event.
535 * @private
536 */
537 handleReload_: function(e) {
538 var node = this.findIdNode_(e.target);
539 chrome.send('reload', [node.id]);
540 },
541
542 /**
543 * Handles the Show (Browser Action) Button functionality.
544 * @param {Event} e Change event.
545 * @private
546 */
547 handleShowButton_: function(e) {
548 var node = this.findIdNode_(e.target);
549 chrome.send('showButton', [node.id]);
550 },
551
552 /**
553 * Handles the Enable/Disable Extension functionality.
554 * @param {Event} e Change event.
555 * @private
556 */
557 handleEnable_: function(e) {
558 var node = this.findIdNode_(e.target.parentNode);
559 var extension = this.getExtensionWithId_(node.id);
560 chrome.send('enable',
561 [node.id, extension.enabled ? "false" : "true"]);
562 chrome.send('requestExtensionsData');
563 },
564
565 /**
566 * Handles the Uninstall Extension functionality.
567 * @param {Event} e Change event.
568 * @private
569 */
570 handleUninstall_: function(e) {
571 var node = this.findIdNode_(e.target.parentNode);
572 chrome.send('uninstall', [node.id]);
573 chrome.send('requestExtensionsData');
574 },
575
576 /**
577 * Handles the View Options link.
578 * @param {Event} e Change event.
579 * @private
580 */
581 handleOptions_: function(e) {
582 var node = this.findIdNode_(e.target.parentNode);
583 var extension = this.getExtensionWithId_(node.id);
584 chrome.send('options', [extension.id]);
585 },
586
587 /**
588 * Handles the Visit Extension Website link.
589 * @param {Event} e Change event.
590 * @private
591 */
592 handleVisitWebsite_: function(e) {
593 var node = this.findIdNode_(e.target.parentNode);
594 var extension = this.getExtensionWithId_(node.id);
595 document.location = extension.homepageUrl;
596 },
597
598 /**
599 * Handles the Enable Extension In Incognito functionality.
600 * @param {Event} e Change event.
601 * @private
602 */
603 handleToggleEnableIncognito_: function(e) {
604 var node = this.findIdNode_(e.target);
605 var butterBar = document.getElementById(node.id + "_incognitoWarning");
606 if (e.target.checked) {
607 if (butterBar.style.display == "none") {
608 butterBar.style.display = "block";
609 }
610 } else {
611 if (butterBar.style.display == "block") {
612 butterBar.style.display = "none";
613 }
614 }
615
616 chrome.send('enableIncognito', [node.id, String(e.target.checked)]);
617 },
618
619 /**
620 * Handles the Allow On File URLs functionality.
621 * @param {Event} e Change event.
622 * @private
623 */
624 handleToggleAllowFileUrls_: function(e) {
625 var node = this.findIdNode_(e.target);
626 chrome.send('allowFileAccess', [node.id, String(e.target.checked)]);
627 },
628
629 /**
630 * Tell the C++ ExtensionDOMHandler to inspect the page detailed in
631 * |viewData|.
632 * @param {Event} e Change event.
633 * @private
634 */
635 sendInspectMessage_: function(e) {
636 var extension = this.getExtensionWithId_(e.srcElement.id);
637 for (var i = 0; i < extension.views.length; ++i) {
638 if (extension.views[i].path == e.srcElement.innerText) {
639 // TODO(aa): This is ghetto, but WebUIBindings doesn't support sending
640 // anything other than arrays of strings, and this is all going to get
641 // replaced with V8 extensions soon anyway.
642 chrome.send('inspect', [
643 String(extension.views[i].renderProcessId),
644 String(extension.views[i].renderViewId)
645 ]);
646 }
647 }
648 },
649 };
650
651 return {
652 ExtensionsList: ExtensionsList
653 };
654 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698