Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / extensions / extensions.js
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 <include src="../uber/uber_utils.js"></include>
6 <include src="extension_commands_overlay.js"></include>
7 <include src="extension_focus_manager.js"></include>
8 <include src="extension_list.js"></include>
9 <include src="pack_extension_overlay.js"></include>
10 <include src="extension_error_overlay.js"></include>
11
12 <if expr="pp_ifdef('chromeos')">
13 <include src="chromeos/kiosk_apps.js"></include>
14 </if>
15
16 // Used for observing function of the backend datasource for this page by
17 // tests.
18 var webuiResponded = false;
19
20 cr.define('extensions', function() {
21   var ExtensionsList = options.ExtensionsList;
22
23   // Implements the DragWrapper handler interface.
24   var dragWrapperHandler = {
25     /** @override */
26     shouldAcceptDrag: function(e) {
27       // We can't access filenames during the 'dragenter' event, so we have to
28       // wait until 'drop' to decide whether to do something with the file or
29       // not.
30       // See: http://www.w3.org/TR/2011/WD-html5-20110113/dnd.html#concept-dnd-p
31       return (e.dataTransfer.types &&
32               e.dataTransfer.types.indexOf('Files') > -1);
33     },
34     /** @override */
35     doDragEnter: function() {
36       chrome.send('startDrag');
37       ExtensionSettings.showOverlay(null);
38       ExtensionSettings.showOverlay($('drop-target-overlay'));
39     },
40     /** @override */
41     doDragLeave: function() {
42       ExtensionSettings.showOverlay(null);
43       chrome.send('stopDrag');
44     },
45     /** @override */
46     doDragOver: function(e) {
47       e.preventDefault();
48     },
49     /** @override */
50     doDrop: function(e) {
51       ExtensionSettings.showOverlay(null);
52       if (e.dataTransfer.files.length != 1)
53         return;
54
55       var toSend = null;
56       // Files lack a check if they're a directory, but we can find out through
57       // its item entry.
58       for (var i = 0; i < e.dataTransfer.items.length; ++i) {
59         if (e.dataTransfer.items[i].kind == 'file' &&
60             e.dataTransfer.items[i].webkitGetAsEntry().isDirectory) {
61           toSend = 'installDroppedDirectory';
62           break;
63         }
64       }
65       // Only process files that look like extensions. Other files should
66       // navigate the browser normally.
67       if (!toSend && /\.(crx|user\.js)$/i.test(e.dataTransfer.files[0].name))
68         toSend = 'installDroppedFile';
69
70       if (toSend) {
71         e.preventDefault();
72         chrome.send(toSend);
73       }
74     }
75   };
76
77   /**
78    * ExtensionSettings class
79    * @class
80    */
81   function ExtensionSettings() {}
82
83   cr.addSingletonGetter(ExtensionSettings);
84
85   ExtensionSettings.prototype = {
86     __proto__: HTMLDivElement.prototype,
87
88     /**
89      * Perform initial setup.
90      */
91     initialize: function() {
92       uber.onContentFrameLoaded();
93       cr.ui.FocusOutlineManager.forDocument(document);
94       measureCheckboxStrings();
95
96       // Set the title.
97       var title = loadTimeData.getString('extensionSettings');
98       uber.invokeMethodOnParent('setTitle', {title: title});
99
100       // This will request the data to show on the page and will get a response
101       // back in returnExtensionsData.
102       chrome.send('extensionSettingsRequestExtensionsData');
103
104       $('toggle-dev-on').addEventListener('change',
105           this.handleToggleDevMode_.bind(this));
106       $('dev-controls').addEventListener('webkitTransitionEnd',
107           this.handleDevControlsTransitionEnd_.bind(this));
108
109       // Set up the three dev mode buttons (load unpacked, pack and update).
110       $('load-unpacked').addEventListener('click',
111           this.handleLoadUnpackedExtension_.bind(this));
112       $('pack-extension').addEventListener('click',
113           this.handlePackExtension_.bind(this));
114       $('update-extensions-now').addEventListener('click',
115           this.handleUpdateExtensionNow_.bind(this));
116
117       if (!loadTimeData.getBoolean('offStoreInstallEnabled')) {
118         this.dragWrapper_ = new cr.ui.DragWrapper(document.documentElement,
119                                                   dragWrapperHandler);
120       }
121
122       extensions.PackExtensionOverlay.getInstance().initializePage();
123
124       // Hook up the configure commands link to the overlay.
125       var link = document.querySelector('.extension-commands-config');
126       link.addEventListener('click',
127           this.handleExtensionCommandsConfig_.bind(this));
128
129       // Initialize the Commands overlay.
130       extensions.ExtensionCommandsOverlay.getInstance().initializePage();
131
132       extensions.ExtensionErrorOverlay.getInstance().initializePage(
133           extensions.ExtensionSettings.showOverlay);
134
135       // Initialize the kiosk overlay.
136       if (cr.isChromeOS) {
137         var kioskOverlay = extensions.KioskAppsOverlay.getInstance();
138         kioskOverlay.initialize();
139
140         $('add-kiosk-app').addEventListener('click', function() {
141           ExtensionSettings.showOverlay($('kiosk-apps-page'));
142           kioskOverlay.didShowPage();
143         });
144
145         extensions.KioskDisableBailoutConfirm.getInstance().initialize();
146       }
147
148       cr.ui.overlay.setupOverlay($('drop-target-overlay'));
149       cr.ui.overlay.globalInitialization();
150
151       extensions.ExtensionFocusManager.getInstance().initialize();
152
153       var path = document.location.pathname;
154       if (path.length > 1) {
155         // Skip starting slash and remove trailing slash (if any).
156         var overlayName = path.slice(1).replace(/\/$/, '');
157         if (overlayName == 'configureCommands')
158           this.showExtensionCommandsConfigUi_();
159       }
160
161       preventDefaultOnPoundLinkClicks();  // From webui/js/util.js.
162     },
163
164     /**
165      * Handles the Load Unpacked Extension button.
166      * @param {Event} e Change event.
167      * @private
168      */
169     handleLoadUnpackedExtension_: function(e) {
170       chrome.send('extensionSettingsLoadUnpackedExtension');
171     },
172
173     /**
174      * Handles the Pack Extension button.
175      * @param {Event} e Change event.
176      * @private
177      */
178     handlePackExtension_: function(e) {
179       ExtensionSettings.showOverlay($('pack-extension-overlay'));
180       chrome.send('metricsHandler:recordAction', ['Options_PackExtension']);
181     },
182
183     /**
184      * Shows the Extension Commands configuration UI.
185      * @param {Event} e Change event.
186      * @private
187      */
188     showExtensionCommandsConfigUi_: function(e) {
189       ExtensionSettings.showOverlay($('extension-commands-overlay'));
190       chrome.send('metricsHandler:recordAction',
191                   ['Options_ExtensionCommands']);
192     },
193
194     /**
195      * Handles the Configure (Extension) Commands link.
196      * @param {Event} e Change event.
197      * @private
198      */
199     handleExtensionCommandsConfig_: function(e) {
200       this.showExtensionCommandsConfigUi_();
201     },
202
203     /**
204      * Handles the Update Extension Now button.
205      * @param {Event} e Change event.
206      * @private
207      */
208     handleUpdateExtensionNow_: function(e) {
209       chrome.send('extensionSettingsAutoupdate');
210     },
211
212     /**
213      * Handles the Toggle Dev Mode button.
214      * @param {Event} e Change event.
215      * @private
216      */
217     handleToggleDevMode_: function(e) {
218       if ($('toggle-dev-on').checked) {
219         $('dev-controls').hidden = false;
220         window.setTimeout(function() {
221           $('extension-settings').classList.add('dev-mode');
222         }, 0);
223       } else {
224         $('extension-settings').classList.remove('dev-mode');
225       }
226
227       chrome.send('extensionSettingsToggleDeveloperMode');
228     },
229
230     /**
231      * Called when a transition has ended for #dev-controls.
232      * @param {Event} e webkitTransitionEnd event.
233      * @private
234      */
235     handleDevControlsTransitionEnd_: function(e) {
236       if (e.propertyName == 'height' &&
237           !$('extension-settings').classList.contains('dev-mode')) {
238         $('dev-controls').hidden = true;
239       }
240     },
241   };
242
243   /**
244    * Called by the dom_ui_ to re-populate the page with data representing
245    * the current state of installed extensions.
246    */
247   ExtensionSettings.returnExtensionsData = function(extensionsData) {
248     // We can get called many times in short order, thus we need to
249     // be careful to remove the 'finished loading' timeout.
250     if (this.loadingTimeout_)
251       window.clearTimeout(this.loadingTimeout_);
252     document.documentElement.classList.add('loading');
253     this.loadingTimeout_ = window.setTimeout(function() {
254       document.documentElement.classList.remove('loading');
255     }, 0);
256
257     webuiResponded = true;
258
259     if (extensionsData.extensions.length > 0) {
260       // Enforce order specified in the data or (if equal) then sort by
261       // extension name (case-insensitive) followed by their ID (in the case
262       // where extensions have the same name).
263       extensionsData.extensions.sort(function(a, b) {
264         function compare(x, y) {
265           return x < y ? -1 : (x > y ? 1 : 0);
266         }
267         return compare(a.order, b.order) ||
268                compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
269                compare(a.id, b.id);
270       });
271     }
272
273     var pageDiv = $('extension-settings');
274     var marginTop = 0;
275     if (extensionsData.profileIsManaged) {
276       pageDiv.classList.add('profile-is-managed');
277     } else {
278       pageDiv.classList.remove('profile-is-managed');
279     }
280     if (extensionsData.profileIsManaged) {
281       pageDiv.classList.add('showing-banner');
282       $('toggle-dev-on').disabled = true;
283       marginTop += 45;
284     } else {
285       pageDiv.classList.remove('showing-banner');
286       $('toggle-dev-on').disabled = false;
287     }
288
289     pageDiv.style.marginTop = marginTop + 'px';
290
291     if (extensionsData.developerMode) {
292       pageDiv.classList.add('dev-mode');
293       $('toggle-dev-on').checked = true;
294       $('dev-controls').hidden = false;
295     } else {
296       pageDiv.classList.remove('dev-mode');
297       $('toggle-dev-on').checked = false;
298     }
299
300     $('load-unpacked').disabled = extensionsData.loadUnpackedDisabled;
301
302     ExtensionsList.prototype.data_ = extensionsData;
303     var extensionList = $('extension-settings-list');
304     ExtensionsList.decorate(extensionList);
305   }
306
307   // Indicate that warning |message| has occured for pack of |crx_path| and
308   // |pem_path| files.  Ask if user wants override the warning.  Send
309   // |overrideFlags| to repeated 'pack' call to accomplish the override.
310   ExtensionSettings.askToOverrideWarning =
311       function(message, crx_path, pem_path, overrideFlags) {
312     var closeAlert = function() {
313       ExtensionSettings.showOverlay(null);
314     };
315
316     alertOverlay.setValues(
317         loadTimeData.getString('packExtensionWarningTitle'),
318         message,
319         loadTimeData.getString('packExtensionProceedAnyway'),
320         loadTimeData.getString('cancel'),
321         function() {
322           chrome.send('pack', [crx_path, pem_path, overrideFlags]);
323           closeAlert();
324         },
325         closeAlert);
326     ExtensionSettings.showOverlay($('alertOverlay'));
327   }
328
329   /**
330    * Returns the current overlay or null if one does not exist.
331    * @return {Element} The overlay element.
332    */
333   ExtensionSettings.getCurrentOverlay = function() {
334     return document.querySelector('#overlay .page.showing');
335   }
336
337   /**
338    * Sets the given overlay to show. This hides whatever overlay is currently
339    * showing, if any.
340    * @param {HTMLElement} node The overlay page to show. If falsey, all overlays
341    *     are hidden.
342    */
343   ExtensionSettings.showOverlay = function(node) {
344     var pageDiv = $('extension-settings');
345     if (node) {
346       pageDiv.style.width = window.getComputedStyle(pageDiv).width;
347       document.body.classList.add('no-scroll');
348     } else {
349       document.body.classList.remove('no-scroll');
350       pageDiv.style.width = '';
351     }
352
353     var currentlyShowingOverlay = ExtensionSettings.getCurrentOverlay();
354     if (currentlyShowingOverlay)
355       currentlyShowingOverlay.classList.remove('showing');
356
357     if (node)
358       node.classList.add('showing');
359
360     var pages = document.querySelectorAll('.page');
361     for (var i = 0; i < pages.length; i++) {
362       pages[i].setAttribute('aria-hidden', node ? 'true' : 'false');
363     }
364
365     overlay.hidden = !node;
366     uber.invokeMethodOnParent(node ? 'beginInterceptingEvents' :
367                                      'stopInterceptingEvents');
368   }
369
370   /**
371    * Utility function to find the width of various UI strings and synchronize
372    * the width of relevant spans. This is crucial for making sure the
373    * Enable/Enabled checkboxes align, as well as the Developer Mode checkbox.
374    */
375   function measureCheckboxStrings() {
376     var trashWidth = 30;
377     var measuringDiv = $('font-measuring-div');
378     measuringDiv.textContent =
379         loadTimeData.getString('extensionSettingsEnabled');
380     var pxWidth = measuringDiv.clientWidth + trashWidth;
381     measuringDiv.textContent =
382         loadTimeData.getString('extensionSettingsEnable');
383     pxWidth = Math.max(measuringDiv.clientWidth + trashWidth, pxWidth);
384     measuringDiv.textContent =
385         loadTimeData.getString('extensionSettingsDeveloperMode');
386     pxWidth = Math.max(measuringDiv.clientWidth, pxWidth);
387
388     var style = document.createElement('style');
389     style.type = 'text/css';
390     style.textContent =
391         '.enable-checkbox-text {' +
392         '  min-width: ' + (pxWidth - trashWidth) + 'px;' +
393         '}' +
394         '#dev-toggle span {' +
395         '  min-width: ' + pxWidth + 'px;' +
396         '}';
397     document.querySelector('head').appendChild(style);
398   }
399
400   // Export
401   return {
402     ExtensionSettings: ExtensionSettings
403   };
404 });
405
406 window.addEventListener('load', function(e) {
407   extensions.ExtensionSettings.getInstance().initialize();
408 });