Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / resources / extensions / extensions.js
blobec2d60c512de251f8811306d257df7c32ea07e1f
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 <include src="../uber/uber_utils.js"></include>
6 <include src="extension_code.js"></include>
7 <include src="extension_commands_overlay.js"></include>
8 <include src="extension_focus_manager.js"></include>
9 <include src="extension_list.js"></include>
10 <include src="pack_extension_overlay.js"></include>
11 <include src="extension_error_overlay.js"></include>
12 <include src="extension_loader.js"></include>
14 <if expr="chromeos">
15 <include src="chromeos/kiosk_apps.js"></include>
16 </if>
18 // Used for observing function of the backend datasource for this page by
19 // tests.
20 var webuiResponded = false;
22 cr.define('extensions', function() {
23 var ExtensionsList = options.ExtensionsList;
25 // Implements the DragWrapper handler interface.
26 var dragWrapperHandler = {
27 /** @override */
28 shouldAcceptDrag: function(e) {
29 // We can't access filenames during the 'dragenter' event, so we have to
30 // wait until 'drop' to decide whether to do something with the file or
31 // not.
32 // See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p
33 return (e.dataTransfer.types &&
34 e.dataTransfer.types.indexOf('Files') > -1);
36 /** @override */
37 doDragEnter: function() {
38 chrome.send('startDrag');
39 ExtensionSettings.showOverlay(null);
40 ExtensionSettings.showOverlay($('drop-target-overlay'));
42 /** @override */
43 doDragLeave: function() {
44 ExtensionSettings.showOverlay(null);
45 chrome.send('stopDrag');
47 /** @override */
48 doDragOver: function(e) {
49 e.preventDefault();
51 /** @override */
52 doDrop: function(e) {
53 ExtensionSettings.showOverlay(null);
54 if (e.dataTransfer.files.length != 1)
55 return;
57 var toSend = null;
58 // Files lack a check if they're a directory, but we can find out through
59 // its item entry.
60 for (var i = 0; i < e.dataTransfer.items.length; ++i) {
61 if (e.dataTransfer.items[i].kind == 'file' &&
62 e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) {
63 toSend = 'installDroppedDirectory';
64 break;
67 // Only process files that look like extensions. Other files should
68 // navigate the browser normally.
69 if (!toSend && /\.(crx|user\.js)$/i.test(e.dataTransfer.files[0].name))
70 toSend = 'installDroppedFile';
72 if (toSend) {
73 e.preventDefault();
74 chrome.send(toSend);
79 /**
80 * ExtensionSettings class
81 * @class
83 function ExtensionSettings() {}
85 cr.addSingletonGetter(ExtensionSettings);
87 ExtensionSettings.prototype = {
88 __proto__: HTMLDivElement.prototype,
90 /**
91 * Whether or not to try to display the Apps Developer Tools promotion.
92 * @type {boolean}
93 * @private
95 displayPromo_: false,
97 /**
98 * Perform initial setup.
100 initialize: function() {
101 uber.onContentFrameLoaded();
102 cr.ui.FocusOutlineManager.forDocument(document);
103 measureCheckboxStrings();
105 // Set the title.
106 var title = loadTimeData.getString('extensionSettings');
107 uber.invokeMethodOnParent('setTitle', {title: title});
109 // This will request the data to show on the page and will get a response
110 // back in returnExtensionsData.
111 chrome.send('extensionSettingsRequestExtensionsData');
113 var extensionLoader = extensions.ExtensionLoader.getInstance();
115 $('toggle-dev-on').addEventListener('change',
116 this.handleToggleDevMode_.bind(this));
117 $('dev-controls').addEventListener('webkitTransitionEnd',
118 this.handleDevControlsTransitionEnd_.bind(this));
120 // Set up the three dev mode buttons (load unpacked, pack and update).
121 $('load-unpacked').addEventListener('click', function(e) {
122 extensionLoader.loadUnpacked();
124 $('pack-extension').addEventListener('click',
125 this.handlePackExtension_.bind(this));
126 $('update-extensions-now').addEventListener('click',
127 this.handleUpdateExtensionNow_.bind(this));
129 // Set up the close dialog for the apps developer tools promo.
130 $('apps-developer-tools-promo').querySelector('.close-button').
131 addEventListener('click', function(e) {
132 this.displayPromo_ = false;
133 this.updatePromoVisibility_();
134 chrome.send('extensionSettingsDismissADTPromo');
135 }.bind(this));
137 if (!loadTimeData.getBoolean('offStoreInstallEnabled')) {
138 this.dragWrapper_ = new cr.ui.DragWrapper(document.documentElement,
139 dragWrapperHandler);
142 extensions.PackExtensionOverlay.getInstance().initializePage();
144 // Hook up the configure commands link to the overlay.
145 var link = document.querySelector('.extension-commands-config');
146 link.addEventListener('click',
147 this.handleExtensionCommandsConfig_.bind(this));
149 // Initialize the Commands overlay.
150 extensions.ExtensionCommandsOverlay.getInstance().initializePage();
152 extensions.ExtensionErrorOverlay.getInstance().initializePage(
153 extensions.ExtensionSettings.showOverlay);
155 // Initialize the kiosk overlay.
156 if (cr.isChromeOS) {
157 var kioskOverlay = extensions.KioskAppsOverlay.getInstance();
158 kioskOverlay.initialize();
160 $('add-kiosk-app').addEventListener('click', function() {
161 ExtensionSettings.showOverlay($('kiosk-apps-page'));
162 kioskOverlay.didShowPage();
165 extensions.KioskDisableBailoutConfirm.getInstance().initialize();
168 cr.ui.overlay.setupOverlay($('drop-target-overlay'));
169 cr.ui.overlay.globalInitialization();
171 extensions.ExtensionFocusManager.getInstance().initialize();
173 var path = document.location.pathname;
174 if (path.length > 1) {
175 // Skip starting slash and remove trailing slash (if any).
176 var overlayName = path.slice(1).replace(/\/$/, '');
177 if (overlayName == 'configureCommands')
178 this.showExtensionCommandsConfigUi_();
181 preventDefaultOnPoundLinkClicks(); // From webui/js/util.js.
185 * Updates the Chrome Apps and Extensions Developer Tools promotion's
186 * visibility.
187 * @private
189 updatePromoVisibility_: function() {
190 var extensionSettings = $('extension-settings');
191 var visible = extensionSettings.classList.contains('dev-mode') &&
192 this.displayPromo_;
194 var adtPromo = $('apps-developer-tools-promo');
195 var controls = adtPromo.querySelectorAll('a, button');
196 Array.prototype.forEach.call(controls, function(control) {
197 control[visible ? 'removeAttribute' : 'setAttribute']('tabindex', '-1');
200 adtPromo.setAttribute('aria-hidden', !visible);
201 extensionSettings.classList.toggle('adt-promo', visible);
205 * Handles the Pack Extension button.
206 * @param {Event} e Change event.
207 * @private
209 handlePackExtension_: function(e) {
210 ExtensionSettings.showOverlay($('pack-extension-overlay'));
211 chrome.send('metricsHandler:recordAction', ['Options_PackExtension']);
215 * Shows the Extension Commands configuration UI.
216 * @param {Event} e Change event.
217 * @private
219 showExtensionCommandsConfigUi_: function(e) {
220 ExtensionSettings.showOverlay($('extension-commands-overlay'));
221 chrome.send('metricsHandler:recordAction',
222 ['Options_ExtensionCommands']);
226 * Handles the Configure (Extension) Commands link.
227 * @param {Event} e Change event.
228 * @private
230 handleExtensionCommandsConfig_: function(e) {
231 this.showExtensionCommandsConfigUi_();
235 * Handles the Update Extension Now button.
236 * @param {Event} e Change event.
237 * @private
239 handleUpdateExtensionNow_: function(e) {
240 chrome.send('extensionSettingsAutoupdate');
244 * Handles the Toggle Dev Mode button.
245 * @param {Event} e Change event.
246 * @private
248 handleToggleDevMode_: function(e) {
249 if ($('toggle-dev-on').checked) {
250 $('dev-controls').hidden = false;
251 window.setTimeout(function() {
252 $('extension-settings').classList.add('dev-mode');
253 }, 0);
254 } else {
255 $('extension-settings').classList.remove('dev-mode');
257 window.setTimeout(this.updatePromoVisibility_.bind(this));
259 chrome.send('extensionSettingsToggleDeveloperMode');
263 * Called when a transition has ended for #dev-controls.
264 * @param {Event} e webkitTransitionEnd event.
265 * @private
267 handleDevControlsTransitionEnd_: function(e) {
268 if (e.propertyName == 'height' &&
269 !$('extension-settings').classList.contains('dev-mode')) {
270 $('dev-controls').hidden = true;
276 * Called by the dom_ui_ to re-populate the page with data representing
277 * the current state of installed extensions.
279 ExtensionSettings.returnExtensionsData = function(extensionsData) {
280 // We can get called many times in short order, thus we need to
281 // be careful to remove the 'finished loading' timeout.
282 if (this.loadingTimeout_)
283 window.clearTimeout(this.loadingTimeout_);
284 document.documentElement.classList.add('loading');
285 this.loadingTimeout_ = window.setTimeout(function() {
286 document.documentElement.classList.remove('loading');
287 }, 0);
289 webuiResponded = true;
291 if (extensionsData.extensions.length > 0) {
292 // Enforce order specified in the data or (if equal) then sort by
293 // extension name (case-insensitive) followed by their ID (in the case
294 // where extensions have the same name).
295 extensionsData.extensions.sort(function(a, b) {
296 function compare(x, y) {
297 return x < y ? -1 : (x > y ? 1 : 0);
299 return compare(a.order, b.order) ||
300 compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
301 compare(a.id, b.id);
305 var pageDiv = $('extension-settings');
306 var marginTop = 0;
307 if (extensionsData.profileIsManaged) {
308 pageDiv.classList.add('profile-is-managed');
309 } else {
310 pageDiv.classList.remove('profile-is-managed');
312 if (extensionsData.profileIsManaged) {
313 pageDiv.classList.add('showing-banner');
314 $('toggle-dev-on').disabled = true;
315 marginTop += 45;
316 } else {
317 pageDiv.classList.remove('showing-banner');
318 $('toggle-dev-on').disabled = false;
321 pageDiv.style.marginTop = marginTop + 'px';
323 if (extensionsData.developerMode) {
324 pageDiv.classList.add('dev-mode');
325 $('toggle-dev-on').checked = true;
326 $('dev-controls').hidden = false;
327 } else {
328 pageDiv.classList.remove('dev-mode');
329 $('toggle-dev-on').checked = false;
332 ExtensionSettings.getInstance().displayPromo_ =
333 extensionsData.promoteAppsDevTools;
334 ExtensionSettings.getInstance().updatePromoVisibility_();
336 $('load-unpacked').disabled = extensionsData.loadUnpackedDisabled;
338 ExtensionsList.prototype.data_ = extensionsData;
339 var extensionList = $('extension-settings-list');
340 ExtensionsList.decorate(extensionList);
343 // Indicate that warning |message| has occured for pack of |crx_path| and
344 // |pem_path| files. Ask if user wants override the warning. Send
345 // |overrideFlags| to repeated 'pack' call to accomplish the override.
346 ExtensionSettings.askToOverrideWarning =
347 function(message, crx_path, pem_path, overrideFlags) {
348 var closeAlert = function() {
349 ExtensionSettings.showOverlay(null);
352 alertOverlay.setValues(
353 loadTimeData.getString('packExtensionWarningTitle'),
354 message,
355 loadTimeData.getString('packExtensionProceedAnyway'),
356 loadTimeData.getString('cancel'),
357 function() {
358 chrome.send('pack', [crx_path, pem_path, overrideFlags]);
359 closeAlert();
361 closeAlert);
362 ExtensionSettings.showOverlay($('alertOverlay'));
366 * Returns the current overlay or null if one does not exist.
367 * @return {Element} The overlay element.
369 ExtensionSettings.getCurrentOverlay = function() {
370 return document.querySelector('#overlay .page.showing');
374 * Sets the given overlay to show. This hides whatever overlay is currently
375 * showing, if any.
376 * @param {HTMLElement} node The overlay page to show. If falsey, all overlays
377 * are hidden.
379 ExtensionSettings.showOverlay = function(node) {
380 var pageDiv = $('extension-settings');
381 if (node) {
382 pageDiv.style.width = window.getComputedStyle(pageDiv).width;
383 document.body.classList.add('no-scroll');
384 } else {
385 document.body.classList.remove('no-scroll');
386 pageDiv.style.width = '';
389 var currentlyShowingOverlay = ExtensionSettings.getCurrentOverlay();
390 if (currentlyShowingOverlay)
391 currentlyShowingOverlay.classList.remove('showing');
393 if (node)
394 node.classList.add('showing');
396 var pages = document.querySelectorAll('.page');
397 for (var i = 0; i < pages.length; i++) {
398 pages[i].setAttribute('aria-hidden', node ? 'true' : 'false');
401 overlay.hidden = !node;
402 uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
403 'stopInterceptingEvents');
407 * Utility function to find the width of various UI strings and synchronize
408 * the width of relevant spans. This is crucial for making sure the
409 * Enable/Enabled checkboxes align, as well as the Developer Mode checkbox.
411 function measureCheckboxStrings() {
412 var trashWidth = 30;
413 var measuringDiv = $('font-measuring-div');
414 measuringDiv.textContent =
415 loadTimeData.getString('extensionSettingsEnabled');
416 var pxWidth = measuringDiv.clientWidth + trashWidth;
417 measuringDiv.textContent =
418 loadTimeData.getString('extensionSettingsEnable');
419 pxWidth = Math.max(measuringDiv.clientWidth + trashWidth, pxWidth);
420 measuringDiv.textContent =
421 loadTimeData.getString('extensionSettingsDeveloperMode');
422 pxWidth = Math.max(measuringDiv.clientWidth, pxWidth);
424 var style = document.createElement('style');
425 style.type = 'text/css';
426 style.textContent =
427 '.enable-checkbox-text {' +
428 ' min-width: ' + (pxWidth - trashWidth) + 'px;' +
429 '}' +
430 '#dev-toggle span {' +
431 ' min-width: ' + pxWidth + 'px;' +
432 '}';
433 document.querySelector('head').appendChild(style);
436 // Export
437 return {
438 ExtensionSettings: ExtensionSettings
442 window.addEventListener('load', function(e) {
443 extensions.ExtensionSettings.getInstance().initialize();