diff options
author | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 18:12:34 +0000 |
---|---|---|
committer | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 18:12:34 +0000 |
commit | f94d3119c96ed2ef9a48475f3963a2d75441a991 (patch) | |
tree | 18d24d3dc91725553996af50d4f91e49112a17e1 /chrome | |
parent | f671d792836ac7781f19d3bf605db25472a7e4a4 (diff) | |
download | chromium_src-f94d3119c96ed2ef9a48475f3963a2d75441a991.zip chromium_src-f94d3119c96ed2ef9a48475f3963a2d75441a991.tar.gz chromium_src-f94d3119c96ed2ef9a48475f3963a2d75441a991.tar.bz2 |
Convert chrome://extensions to a settings page within the options pages.
BUG=87378
TEST=The new settings page (chrome://settings/extensionSettings) should work the same as the old one (chrome://extensions).
Review URL: http://codereview.chromium.org/7794023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99402 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
19 files changed, 1782 insertions, 422 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 999f766..cc64bd4 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4007,12 +4007,21 @@ are declared in build/common.gypi. <message name="IDS_EXTENSIONS_ENABLE" desc="The link for enabling extensions."> Enable </message> + <message name="IDS_EXTENSIONS_ENABLED" desc="The label for have been enabled extensions."> + Enabled + </message> + <message name="IDS_EXTENSIONS_REMOVE" desc="The label for uninstalling an extension."> + Remove + </message> <message name="IDS_EXTENSIONS_ENABLE_INCOGNITO" desc="The checkbox for enabling extensions in incognito."> Allow in incognito </message> <message name="IDS_EXTENSIONS_ALLOW_FILE_ACCESS" desc="The checkbox for allowing an extension access to run scripts on file URLs."> Allow access to file URLs </message> + <message name="IDS_EXTENSIONS_VISIT_WEBSITE" desc="The link for visiting the extension's gallery page."> + Visit website + </message> <message name="IDS_EXTENSIONS_INCOGNITO_WARNING" desc="Warns the user that Chrome cannot prevent extensions from recording history in incognito mode. Displayed in extensions management UI after an extension is selected to be run in incognito mode."> <ph name="BEGIN_BOLD"><b></ph>Warning:<ph name="END_BOLD"></b></ph> <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> cannot prevent extensions from recording your browsing history. To disable this extension in incognito mode, unselect this option. </message> @@ -4038,7 +4047,7 @@ are declared in build/common.gypi. (this extension is managed and can not be uninstalled or disabled) </message> <message name="IDS_GET_MORE_EXTENSIONS" desc="The link for getting more extensions. Displayed at bottom of extension management page when there is at least one extension installed."> - Get more extensions >> + Get more extensions </message> <message name="IDS_EXTENSION_LOAD_FROM_DIRECTORY" desc="Title of directory browse dialog when user wants to load an extension from a directory."> Select the extension directory. @@ -4046,6 +4055,9 @@ are declared in build/common.gypi. <message name="IDS_EXTENSION_PACK_DIALOG_TITLE" desc="Title of pack extension dialog"> Pack Extension </message> + <message name="IDS_EXTENSION_PACK_BUTTON" desc="Text of the pack extension button"> + Pack Extension + </message> <message name="IDS_EXTENSION_PACK_DIALOG_HEADING" desc="The heading of the pack extension dialog."> Select the root directory of the extension to pack. To update an extension, also select the private key file to reuse. </message> @@ -7183,6 +7195,11 @@ Keep your key file in a safe place. You will need it to create new versions of y Unlock </message> + <!-- Extension settings --> + <message name="IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE" desc="Title that appears in the dialogue title bar for manage extensions settings"> + Extensions + </message> + <!-- Font settings --> <message name="IDS_FONT_LANGUAGE_SETTING_FONT_TAB_TITLE" desc="Title that appears in the Fonts and Encoding subpage"> Fonts and Encoding diff --git a/chrome/browser/resources/options/extension_list.js b/chrome/browser/resources/options/extension_list.js new file mode 100644 index 0000000..3314a32 --- /dev/null +++ b/chrome/browser/resources/options/extension_list.js @@ -0,0 +1,684 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** + * Creates a new list of extensions. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {cr.ui.div} + */ + var ExtensionsList = cr.ui.define('div'); + + var handleInstalled = false; + + ExtensionsList.prototype = { + __proto__: HTMLDivElement.prototype, + + /** @inheritDoc */ + decorate: function() { + this.initControlsAndHandlers_(); + + var showingDetails = []; + var showingWarning = []; + this.deleteExistingExtensionNodes_(showingDetails, showingWarning); + + this.showExtensionNodes_(showingDetails, showingWarning); + }, + + /** + * Initializes the controls (toggle section and button) and installs + * handlers. + * @private + */ + initControlsAndHandlers_: function() { + // Make sure developer mode section is set correctly as per saved setting. + var toggleButton = $('toggle-dev-on'); + var toggleSection = $('dev'); + if (this.data_.developerMode) { + toggleSection.classList.add('dev-open'); + toggleSection.classList.remove('dev-closed'); + toggleButton.checked = true; + } else { + toggleSection.classList.remove('dev-open'); + toggleSection.classList.add('dev-closed'); + } + + // Install handler for key presses. + if (!handleInstalled) { + document.addEventListener('keyup', this.upEventHandler_.bind(this)); + document.addEventListener('mouseup', this.upEventHandler_.bind(this)); + handleInstalled = true; + } + }, + + /** + * Deletes the existing Extension nodes from the page to make room for new + * ones. It also keeps track of who was showing details so when the + * extension list gets recreated we can recreate that state. + * @param {Array} showingDetails An array that will contain the list of id's + * of extension that had the details section expanded. + * @param {Array} showingWarning An array that will contain the list of id's + * of extension that were showing a warning. + * @private + */ + deleteExistingExtensionNodes_: function(showingDetails, showingWarning) { + // Delete all child nodes before adding them back and while we are at it + // make a note of which ones were in expanded state (and which showing + // the warning) so we can restore those to the same state afterwards. + while (this.hasChildNodes()){ + var child = this.firstChild; + + // See if the item is expanded. + if (child.classList.contains('extension-list-item-expanded')) + showingDetails.push(child.id); + + // See if the butterbar is showing. + var butterBar = document.getElementById(child.id + '_incognitoWarning'); + if (!(butterBar === null) && !butterBar.hidden) + showingWarning.push(child.id); + + // Now we can delete it. + this.removeChild(child); + } + }, + + /** + * Handles decorating the details section. + * @param {Element} details The div that the details should be attached to. + * @param {Object} extension The extension we are shoting the details for. + * @param {boolean} expanded Whether to show the details expanded or not. + * @param {boolean} showButterbar Whether to show the incognito warning or + * not. + * @private + */ + showExtensionNodes_: function(showingDetails, showingWarning) { + // Keeps track of differences in checkbox width. + var minCheckboxWidth = 999999; + var maxCheckboxWidth = 0; + + // Iterate over the extension data and add each item to the list. + for (var i = 0; i < this.data_.extensions.length; ++i) { + var extension = this.data_.extensions[i]; + var id = extension.id; + + var wrapper = this.ownerDocument.createElement('div'); + + // Figure out if the item should open expanded or not based on the state + // of things before we deleted the items. + var iter = showingDetails.length; + var expanded = false; + while (iter--) { + if (showingDetails[iter] == id) { + expanded = true; + break; + } + } + // Figure out if the butterbar should be showing. + iter = showingWarning.length; + var butterbar = false; + while (iter--) { + if (showingWarning[iter] == id) { + butterbar = true; + break; + } + } + + wrapper.classList.add(expanded ? 'extension-list-item-expanded' : + 'extension-list-item-collaped'); + if (!extension.enabled) + wrapper.classList.add('disabled'); + wrapper.id = id; + this.appendChild(wrapper); + + var vbox_outer = this.ownerDocument.createElement('div'); + vbox_outer.classList.add('vbox'); + vbox_outer.classList.add('extension-list-item'); + wrapper.appendChild(vbox_outer); + + var hbox = this.ownerDocument.createElement('div'); + hbox.classList.add('hbox'); + vbox_outer.appendChild(hbox); + + // Add a container div for the zippy, so we can extend the hit area. + var container = this.ownerDocument.createElement('div'); + // Clicking anywhere on the div expands/collapses the details. + container.classList.add('extension-zippy-container'); + container.addEventListener('click', this.handleZippyClick_.bind(this)); + hbox.appendChild(container); + + // On the far left we have the zippy icon. + div = this.ownerDocument.createElement('div'); + div.id = id + '_zippy'; + div.classList.add('extension-zippy-default'); + div.classList.add(expanded ? 'extension-zippy-expanded' : + 'extension-zippy-collapsed'); + container.appendChild(div); + + // Next to it, we have the extension icon. + icon = this.ownerDocument.createElement('img'); + icon.classList.add('extension-icon'); + icon.src = extension.icon; + hbox.appendChild(icon); + + // Start a vertical box for showing the details. + var vbox = this.ownerDocument.createElement('div'); + vbox.classList.add('vbox'); + vbox.classList.add('stretch'); + hbox.appendChild(vbox); + + div = this.ownerDocument.createElement('div'); + vbox.appendChild(div); + + // Title comes next. + var title = this.ownerDocument.createElement('span'); + title.classList.add('extension-title'); + title.textContent = extension.name; + vbox.appendChild(title); + + // Followed by version. + var version = this.ownerDocument.createElement('span'); + version.classList.add('extension-version'); + version.textContent = extension.version; + vbox.appendChild(version); + + div = this.ownerDocument.createElement('div'); + vbox.appendChild(div); + + // And below that we have description (if provided). + if (extension.description.length > 0) { + var description = this.ownerDocument.createElement('span'); + description.classList.add('extension-description'); + description.textContent = extension.description; + vbox.appendChild(description); + } + + // Immediately following the description, we have the + // Options link (optional). + if (extension.options_url) { + var link = this.ownerDocument.createElement('a'); + link.classList.add('extension-links-trailing'); + link.textContent = localStrings.getString('extensionSettingsOptions'); + link.href = '#'; + link.addEventListener('click', this.handleOptions_.bind(this)); + vbox.appendChild(link); + } + + // Then the optional Visit Website link. + if (extension.homepageUrl) { + var link = this.ownerDocument.createElement('a'); + link.classList.add('extension-links-trailing'); + link.textContent = + localStrings.getString('extensionSettingsVisitWebsite'); + link.href = '#'; + link.addEventListener('click', this.handleVisitWebsite_.bind(this)); + vbox.appendChild(link); + } + + // And now the details section that is normally hidden. + var details = this.ownerDocument.createElement('div'); + details.classList.add('vbox'); + vbox.appendChild(details); + + this.decorateDetailsSection_(details, extension, expanded, butterbar); + + // And on the right of the details we have the Enable/Enabled checkbox. + div = this.ownerDocument.createElement('div'); + hbox.appendChild(div); + + var section = this.ownerDocument.createElement('section'); + section.classList.add('extension-enabling'); + div.appendChild(section); + + // The Enable checkbox. + var input = this.ownerDocument.createElement('input'); + input.addEventListener('click', this.handleEnable_.bind(this)); + input.type = 'checkbox'; + input.name = 'toggle-' + id; + if (!extension.mayDisable) + input.disabled = true; + if (extension.enabled) + input.checked = true; + input.id = 'toggle-' + id; + section.appendChild(input); + var label = this.ownerDocument.createElement('label'); + label.classList.add('extension-enabling-label'); + if (extension.enabled) + label.classList.add('extension-enabling-label-bold'); + label.setAttribute('for', 'toggle-' + id); + label.id = 'toggle-' + id + '-label'; + if (extension.enabled) { + // Enabled (with a d). + label.textContent = + localStrings.getString('extensionSettingsEnabled'); + } else { + // Enable (no d). + label.textContent = localStrings.getString('extensionSettingsEnable'); + } + section.appendChild(label); + + if (label.offsetWidth > maxCheckboxWidth) + maxCheckboxWidth = label.offsetWidth; + if (label.offsetWidth < minCheckboxWidth) + minCheckboxWidth = label.offsetWidth; + + // And, on the far right we have the uninstall button. + var button = this.ownerDocument.createElement('button'); + button.classList.add('extension-delete'); + button.id = id; + if (!extension.mayDisable) + button.disabled = true; + button.textContent = localStrings.getString('extensionSettingsRemove'); + button.addEventListener('click', this.handleUninstall_.bind(this)); + hbox.appendChild(button); + } + + // Do another pass, making sure checkboxes line up. + var difference = maxCheckboxWidth - minCheckboxWidth; + for (var i = 0; i < this.data_.extensions.length; ++i) { + var extension = this.data_.extensions[i]; + var id = extension.id; + var label = $('toggle-' + id + '-label'); + if (label.offsetWidth < maxCheckboxWidth) + label.style.marginRight = difference.toString() + 'px'; + } + }, + + /** + * Handles decorating the details section. + * @param {Element} details The div that the details should be attached to. + * @param {Object} extension The extension we are shoting the details for. + * @param {boolean} expanded Whether to show the details expanded or not. + * @param {boolean} showButterbar Whether to show the incognito warning or + * not. + * @private + */ + decorateDetailsSection_: function(details, extension, + expanded, showButterbar) { + // This container div is needed because vbox display + // overrides display:hidden. + var details_contents = this.ownerDocument.createElement('div'); + details_contents.classList.add(expanded ? 'extension-details-visible' : + 'extension-details-hidden'); + details_contents.id = extension.id + '_details'; + details.appendChild(details_contents); + + var div = this.ownerDocument.createElement('div'); + div.classList.add('informative-text'); + details_contents.appendChild(div); + + // Keep track of how many items we'll show in the details section. + var itemsShown = 0; + + if (this.data_.developerMode) { + // First we have the id. + var content = this.ownerDocument.createElement('div'); + content.textContent = + localStrings.getString('extensionSettingsExtensionId') + + ' ' + extension.id; + div.appendChild(content); + itemsShown++; + + // Then, the path, if provided by unpacked extension. + if (extension.isUnpacked) { + content = this.ownerDocument.createElement('div'); + content.textContent = + localStrings.getString('extensionSettingsExtensionPath') + + ' ' + extension.path; + div.appendChild(content); + itemsShown++; + } + + // Then, the 'managed, cannot uninstall/disable' message. + if (!extension.mayDisable) { + content = this.ownerDocument.createElement('div'); + content.textContent = + localStrings.getString('extensionSettingsPolicyControlled'); + div.appendChild(content); + itemsShown++; + } + + // Then active views: + if (extension.views.length > 0) { + var table = this.ownerDocument.createElement('table'); + table.classList.add('extension-inspect-table'); + div.appendChild(table); + var tr = this.ownerDocument.createElement('tr'); + table.appendChild(tr); + var td = this.ownerDocument.createElement('td'); + td.classList.add('extension-inspect-left-column'); + tr.appendChild(td); + var span = this.ownerDocument.createElement('span'); + td.appendChild(span); + span.textContent = + localStrings.getString('extensionSettingsInspectViews'); + + td = this.ownerDocument.createElement('td'); + for (var i = 0; i < extension.views.length; ++i) { + // Then active views: + content = this.ownerDocument.createElement('div'); + var link = this.ownerDocument.createElement('a'); + link.classList.add('extension-links-view'); + link.textContent = extension.views[i].path; + link.id = extension.id; + link.href = '#'; + link.addEventListener('click', this.sendInspectMessage_.bind(this)); + content.appendChild(link); + td.appendChild(content); + tr.appendChild(td); + + itemsShown++; + } + } + } + + var content = this.ownerDocument.createElement('div'); + details_contents.appendChild(content); + + // Then Reload: + if (extension.enabled && extension.allow_reload) { + var link = this.ownerDocument.createElement('a'); + link.classList.add('extension-links-trailing'); + link.textContent = localStrings.getString('extensionSettingsReload'); + link.id = extension.id; + link.href = '#'; + link.addEventListener('click', this.handleReload_.bind(this)); + content.appendChild(link); + itemsShown++; + } + + // Then Show (Browser Action) Button: + if (extension.enabled && extension.enable_show_button) { + link = this.ownerDocument.createElement('a'); + link.classList.add('extension-links-trailing'); + link.textContent = + localStrings.getString('extensionSettingsShowButton'); + link.id = extension.id; + link.href = '#'; + link.addEventListener('click', this.handleShowButton_.bind(this)); + content.appendChild(link); + itemsShown++; + } + + if (extension.enabled && !extension.wantsFileAccess) { + // The 'allow in incognito' checkbox. + var label = this.ownerDocument.createElement('label'); + label.classList.add('extension-checkbox-label'); + content.appendChild(label); + var input = this.ownerDocument.createElement('input'); + input.addEventListener('click', + this.handleToggleEnableIncognito_.bind(this)); + input.id = extension.id; + input.type = 'checkbox'; + if (extension.enabledIncognito) + input.checked = true; + label.appendChild(input); + var span = this.ownerDocument.createElement('span'); + span.classList.add('extension-checkbox-span'); + span.textContent = + localStrings.getString('extensionSettingsEnableIncognito'); + label.appendChild(span); + itemsShown++; + } + + if (extension.enabled && !extension.wantsFileAccess) { + // The 'allow access to file URLs' checkbox. + label = this.ownerDocument.createElement('label'); + label.classList.add('extension-checkbox-label'); + content.appendChild(label); + var input = this.ownerDocument.createElement('input'); + input.addEventListener('click', + this.handleToggleAllowFileUrls_.bind(this)); + input.id = extension.id; + input.type = 'checkbox'; + if (extension.allowFileAccess) + input.checked = true; + label.appendChild(input); + var span = this.ownerDocument.createElement('span'); + span.classList.add('extension-checkbox-span'); + span.textContent = + localStrings.getString('extensionSettingsAllowFileAccess'); + label.appendChild(span); + itemsShown++; + } + + if (extension.enabled && !extension.is_hosted_app) { + // And add a hidden warning message for allowInIncognito. + content = this.ownerDocument.createElement('div'); + content.id = extension.id + '_incognitoWarning'; + content.classList.add('butter-bar'); + content.hidden = !showButterbar; + details_contents.appendChild(content); + + var span = this.ownerDocument.createElement('span'); + span.innerHTML = + localStrings.getString('extensionSettingsIncognitoWarning'); + content.appendChild(span); + itemsShown++; + } + + var zippy = extension.id + '_zippy'; + $(zippy).style.display = (itemsShown > 0) ? 'block' : 'none'; + }, + + /** + * A lookup helper function to find an extension based on an id. + * @param {string} id The |id| of the extension to look up. + * @private + */ + getExtensionWithId_: function(id) { + for (var i = 0; i < this.data_.extensions.length; ++i) { + if (this.data_.extensions[i].id == id) + return this.data_.extensions[i]; + } + return null; + }, + + /** + * A lookup helper function to find the first node that has an id (starting + * at |node| and going up the parent chain. + * @param {Element} node The node to start looking at. + * @private + */ + findIdNode_: function(node) { + while (node.id.length == 0) { + node = node.parentNode; + if (!node) + return null; + } + return node; + }, + + /** + * Handles the mouseclick on the zippy icon (that expands and collapses the + * details section). + * @param {Event} e Change event. + * @private + */ + handleZippyClick_: function(e) { + var node = this.findIdNode_(e.target.parentNode); + var iter = this.firstChild; + while (iter) { + var zippy = $(iter.id + '_zippy'); + var details = $(iter.id + '_details'); + if (iter.id == node.id) { + // Toggle visibility. + if (iter.classList.contains('extension-list-item-expanded')) { + // Hide yo kids! Hide yo wife! + zippy.classList.remove('extension-zippy-expanded'); + zippy.classList.add('extension-zippy-collapsed'); + details.classList.remove('extension-details-visible'); + details.classList.add('extension-details-hidden'); + iter.classList.remove('extension-list-item-expanded'); + iter.classList.add('extension-list-item-collaped'); + + // Hide yo incognito warning. + var butterBar = this.ownerDocument.getElementById( + iter.id + '_incognitoWarning'); + if (!(butterBar === null)) + butterBar.hidden = true; + } else { + // Show the contents. + zippy.classList.remove('extension-zippy-collapsed'); + zippy.classList.add('extension-zippy-expanded'); + details.classList.remove('extension-details-hidden'); + details.classList.add('extension-details-visible'); + iter.classList.remove('extension-list-item-collaped'); + iter.classList.add('extension-list-item-expanded'); + } + } + iter = iter.nextSibling; + } + }, + + /** + * Handles the mouse-up and keyboard-up events. This is used to limit the + * number of items to show in the list, when the user is searching for items + * with the search box. Otherwise, if one match is found, the whole list of + * extensions would be shown when we only want the matching items to be + * found. + * @param {Event} e Change event. + * @private + */ + upEventHandler_: function(e) { + var searchString = $('search-field').value.toLowerCase(); + var child = this.firstChild; + while (child){ + var extension = this.getExtensionWithId_(child.id); + if (searchString.length == 0) { + // Show all. + child.classList.remove('search-suppress'); + } else { + // If the search string does not appear within the text of the + // extension, then hide it. + if ((extension.name.toLowerCase().indexOf(searchString) < 0) && + (extension.version.toLowerCase().indexOf(searchString) < 0) && + (extension.description.toLowerCase().indexOf(searchString) < 0)) { + // Hide yo extension! + child.classList.add('search-suppress'); + } else { + // Show yourself! + child.classList.remove('search-suppress'); + } + } + child = child.nextSibling; + } + }, + + /** + * Handles the Reload Extension functionality. + * @param {Event} e Change event. + * @private + */ + handleReload_: function(e) { + var node = this.findIdNode_(e.target); + chrome.send('extensionSettingsReload', [node.id]); + }, + + /** + * Handles the Show (Browser Action) Button functionality. + * @param {Event} e Change event. + * @private + */ + handleShowButton_: function(e) { + var node = this.findIdNode_(e.target); + chrome.send('extensionSettingsShowButton', [node.id]); + }, + + /** + * Handles the Enable/Disable Extension functionality. + * @param {Event} e Change event. + * @private + */ + handleEnable_: function(e) { + var node = this.findIdNode_(e.target.parentNode); + var extension = this.getExtensionWithId_(node.id); + chrome.send('extensionSettingsEnable', + [node.id, extension.enabled ? 'false' : 'true']); + chrome.send('extensionSettingsRequestExtensionsData'); + }, + + /** + * Handles the Uninstall Extension functionality. + * @param {Event} e Change event. + * @private + */ + handleUninstall_: function(e) { + var node = this.findIdNode_(e.target.parentNode); + chrome.send('extensionSettingsUninstall', [node.id]); + chrome.send('extensionSettingsRequestExtensionsData'); + }, + + /** + * Handles the View Options link. + * @param {Event} e Change event. + * @private + */ + handleOptions_: function(e) { + var node = this.findIdNode_(e.target.parentNode); + var extension = this.getExtensionWithId_(node.id); + chrome.send('extensionSettingsOptions', [extension.id]); + }, + + /** + * Handles the Visit Extension Website link. + * @param {Event} e Change event. + * @private + */ + handleVisitWebsite_: function(e) { + var node = this.findIdNode_(e.target.parentNode); + var extension = this.getExtensionWithId_(node.id); + document.location = extension.homepageUrl; + }, + + /** + * Handles the Enable Extension In Incognito functionality. + * @param {Event} e Change event. + * @private + */ + handleToggleEnableIncognito_: function(e) { + var node = this.findIdNode_(e.target); + var butterBar = document.getElementById(node.id + '_incognitoWarning'); + butterBar.hidden = !e.target.checked; + chrome.send('extensionSettingsEnableIncognito', + [node.id, String(e.target.checked)]); + }, + + /** + * Handles the Allow On File URLs functionality. + * @param {Event} e Change event. + * @private + */ + handleToggleAllowFileUrls_: function(e) { + var node = this.findIdNode_(e.target); + chrome.send('extensionSettingsAllowFileAccess', + [node.id, String(e.target.checked)]); + }, + + /** + * Tell the C++ ExtensionDOMHandler to inspect the page detailed in + * |viewData|. + * @param {Event} e Change event. + * @private + */ + sendInspectMessage_: function(e) { + var extension = this.getExtensionWithId_(e.srcElement.id); + for (var i = 0; i < extension.views.length; ++i) { + if (extension.views[i].path == e.srcElement.innerText) { + // TODO(aa): This is ghetto, but WebUIBindings doesn't support sending + // anything other than arrays of strings, and this is all going to get + // replaced with V8 extensions soon anyway. + chrome.send('extensionSettingsInspect', [ + String(extension.views[i].renderProcessId), + String(extension.views[i].renderViewId) + ]); + } + } + }, + }; + + return { + ExtensionsList: ExtensionsList + }; +});
\ No newline at end of file diff --git a/chrome/browser/resources/options/extension_settings.css b/chrome/browser/resources/options/extension_settings.css new file mode 100644 index 0000000..c7c4a25 --- /dev/null +++ b/chrome/browser/resources/options/extension_settings.css @@ -0,0 +1,261 @@ +/* +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +*/ + +/* TODO(finnur): Move these into a central location, either options_page.css or + somewhere in the shared directory */ +.hbox { + display: -webkit-box; + -webkit-box-orient: horizontal; +} + +.vbox { + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.stretch { + padding-right: 10px; + -webkit-box-flex: 1; +} + +.extension-list-item { + padding-bottom: 7px; + padding-top: 7px; + width: 100%; + -webkit-user-select: auto; +} + +/* Get rid of display: table, which causes width issues. */ +.displaytable { + display: block; +} +/* Get rid of display: table row, which causes width issues. */ +.displaytable > section { + display: block; +} +/* Get rid of display: table cell, which causes width issues. */ +.displaytable > section > * { + display: block; +} + +.extension-settings-content { + border-bottom : 0px solid #eee; + margin-top: 3px; +} + +.big-list { + overflow-y: hidden; +} + +/* Get rid of the light-blue background on list item hover. */ +.big-list:not([disabled]) > :hover { + background-color: white; + border-color: #CDCDCD; +} + +.butter-bar { + background: #FFF299; + padding: 2px 5px; + border-radius: 3px; + white-space: normal; +} + +.search-suppress { + display: none; + height: 0; +} + +.extension-list-item-collaped { + height: auto; + margin-bottom: 16px; + -webkit-transition: padding 300ms, overflow 300ms, opacity 700ms; +} + +.extension-list-item-expanded { + height: auto; + margin-bottom: 16px; + overflow: visible; + -webkit-transition: padding 300ms, overflow 300ms, opacity 700ms; +} + +.extension-settings { + overflow-x: hidden; +} + +.extension-icon { + height: 48px; + padding-left: 15px; + padding-right: 15px; + vertical-align: text-top; + width: 48px; + -webkit-user-select: none; +} + +.extension-title { + font-size: 16px; + font-weight: 500; + padding-right: 20px; +} + +.extension-version { + font-size: 13px; + font-weight: 400; +} + +.extension-description { + font-size: 13px; + white-space: normal; + padding-right: 5px; +} + +.extension-checkbox-span { + margin-left: 7px; +} + +.extension-checkbox-label { + margin-right: 10px; +} + +.extension-delete { + margin-left: 5px; +} + +.extension-details-hidden { + opacity: 0; + max-height: 0; + -webkit-transition: max-height 400ms, opacity 200ms; +} + +.extension-details-visible { + opacity: 1; + max-height: 100px; + -webkit-transition: max-height 200ms, opacity 300ms; +} + +.extension-links-view { + padding-left: 15px; +} + +.extension-links-trailing { + padding-right: 7px; +} + +.extension-zippy-container { + cursor: pointer; + width: 20px; + -webkit-user-select: none; +} + +.informative-text { + color: gray; +} + +.extension-zippy-default { + background-image: url('zippy.png'); + background-repeat: no-repeat; + background-position: center top; + position: absolute; + left: 12px; + top: 25px; + width: 6px; + height: 16px; + opacity: .25; +} + +.extension-zippy-collapsed { + -webkit-transition: -webkit-transform .1s; + -webkit-transform: rotate(0deg); +} + +.extension-zippy-collapsed:hover { + -webkit-transition: -webkit-transform .1s, opacity .1s; + opacity: .5; + -webkit-transform: rotate(5deg); +} + +.extension-zippy-expanded { + -webkit-transition: -webkit-transform .1s; + -webkit-transform: rotate(90deg); +} + +.extension-zippy-expanded:hover { + -webkit-transition: -webkit-transform .1s; + -webkit-transform: rotate(85deg); +} + +.extension-enabling { + position: relative; + top: 3px; +} + +.extension-enabling-label { + padding-left: 3px; + padding-right: 9px; + color: black; +} + +.extension-enabling-label-bold { + font-weight: bold; +} + +.extension-inspect-table { + padding: 0; + border-spacing: 0; +} + +.extension-inspect-left-column { + vertical-align: text-top; +} + +/* Dev */ + +.dev-open { + border-bottom: 1px solid rgb(205, 205, 205); + height: 32px; + padding-bottom: 7px; + padding-left: 4px; + padding-right: 3px; + padding-top: 4px; + -webkit-transition: padding 300ms, height 300ms, opacity 700ms; +} +.dev-closed { + height: 0; + opacity: 0; + padding-left: 4px; + padding-right: 3px; + -webkit-transition: padding 300ms, height 700ms, opacity 200ms; +} + +.dev-button-visible { + display: inherit; + opacity: 1; + -webkit-transition: opacity 200ms; +} + +.dev-button-hidden { + display: none; +} + +#suggest-gallery { + padding-left: 10px; +} + +#dev-toggle { + display: block; + float: right; + margin-top: -28px; + margin-right: 8px; +} + +#get-more-extensions-container { + display: -webkit-box; +} + +#get-more-extensions { + padding-left: 10px; + padding-top: 5px; + font-size: 15px; +} diff --git a/chrome/browser/resources/options/extension_settings.html b/chrome/browser/resources/options/extension_settings.html new file mode 100644 index 0000000..5a0b210 --- /dev/null +++ b/chrome/browser/resources/options/extension_settings.html @@ -0,0 +1,40 @@ +<div id="extension-settings" class="page" hidden> + <h1 i18n-content="extensionSettingsTitle"></h1> + <div id="dev-toggle"> + <input id="toggle-dev-on" type="checkbox" value="off"></input> + <label for="toggle-dev-on" i18n-content="extensionSettingsDeveloperMode" /> + </div> + <div class="displaytable"> + <div id="dev" class="dev-closed"> + <table id="dev-table" width="100%"> + <tr> + <td> + <button id="load-unpacked" + i18n-content="extensionSettingsLoadUnpackedButton"></button> + <button id="pack-extension" + i18n-content="extensionSettingsPackButton"></button> + </td> + <td align="right"> + <button id="update-extensions-now" + i18n-content="extensionSettingsUpdateButton"></button> + </td> + </tr> + </table> + </div> + <section class="extension-settings-content"> + <div class="extension-settings"> + <list class="big-list" id="extension-settings-list"></list> + </div> + </section> + </div> + <section> + <div><strong id="no-extensions" + i18n-content="extensionSettingsNoExtensions" + hidden="true"></strong></div> + <div id="suggest-gallery" hidden="true"></div> + <div id="get-more-extensions-container"> + <image src="chrome://theme/IDR_WEBSTORE_ICON_32"> + <div id="get-more-extensions"></div> + </div> + </section> +</div> diff --git a/chrome/browser/resources/options/extension_settings.js b/chrome/browser/resources/options/extension_settings.js new file mode 100644 index 0000000..d58d482 --- /dev/null +++ b/chrome/browser/resources/options/extension_settings.js @@ -0,0 +1,179 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + var ExtensionsList = options.ExtensionsList; + + /** + * ExtensionSettings class + * Encapsulated handling of the 'Manage Extensions' page. + * @class + */ + function ExtensionSettings() { + OptionsPage.call(this, + 'extensionSettings', + templateData.extensionSettingsTitle, + 'extension-settings'); + } + + cr.addSingletonGetter(ExtensionSettings); + + ExtensionSettings.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + // This will request the data to show on the page and will get a response + // back in returnExtensionsData. + chrome.send('extensionSettingsRequestExtensionsData'); + + // Set up the developer mode button. + var toggleDevMode = $('toggle-dev-on'); + toggleDevMode.addEventListener('click', + this.handleToggleDevMode_.bind(this)); + + // Setup the gallery related links and text. + $('suggest-gallery').innerHTML = + localStrings.getString('extensionSettingsSuggestGallery'); + $('get-more-extensions').innerHTML = + localStrings.getString('extensionSettingsGetMoreExtensions'); + + // Set up the three dev mode buttons (load unpacked, pack and update). + $('load-unpacked').addEventListener('click', + this.handleLoadUnpackedExtension_.bind(this)); + $('pack-extension').addEventListener('click', + this.handlePackExtension_.bind(this)); + $('update-extensions-now').addEventListener('click', + this.handleUpdateExtensionNow_.bind(this)); + }, + + /** + * Utility function which asks the C++ to show a platform-specific file + * select dialog, and fire |callback| with the |filePath| that resulted. + * |selectType| can be either 'file' or 'folder'. |operation| can be 'load', + * 'packRoot', or 'pem' which are signals to the C++ to do some + * operation-specific configuration. + * @private + */ + showFileDialog_: function(selectType, operation, callback) { + handleFilePathSelected = function(filePath) { + callback(filePath); + handleFilePathSelected = function() {}; + }; + + chrome.send('extensionSettingsSelectFilePath', [selectType, operation]); + }, + + /** + * Handles the Load Unpacked Extension button. + * @param {Event} e Change event. + * @private + */ + handleLoadUnpackedExtension_: function(e) { + this.showFileDialog_('folder', 'load', function(filePath) { + chrome.send('extensionSettingsLoad', [String(filePath)]); + }); + + chrome.send('coreOptionsUserMetricsAction', + ['Options_LoadUnpackedExtension']); + }, + + /** + * Handles the Pack Extension button. + * @param {Event} e Change event. + * @private + */ + handlePackExtension_: function(e) { + OptionsPage.navigateToPage('packExtensionOverlay'); + chrome.send('coreOptionsUserMetricsAction', ['Options_PackExtension']); + }, + + /** + * Handles the Update Extension Now button. + * @param {Event} e Change event. + * @private + */ + handleUpdateExtensionNow_: function(e) { + chrome.send('extensionSettingsAutoupdate', []); + }, + + /** + * Handles the Toggle Dev Mode button. + * @param {Event} e Change event. + * @private + */ + handleToggleDevMode_: function(e) { + var dev = $('dev'); + if (!dev.classList.contains('dev-open')) { + // Make the Dev section visible. + dev.classList.add('dev-open'); + dev.classList.remove('dev-closed'); + + $('load-unpacked').classList.add('dev-button-visible'); + $('load-unpacked').classList.remove('dev-button-hidden'); + $('pack-extension').classList.add('dev-button-visible'); + $('pack-extension').classList.remove('dev-button-hidden'); + $('update-extensions-now').classList.add('dev-button-visible'); + $('update-extensions-now').classList.remove('dev-button-hidden'); + } else { + // Hide the Dev section. + dev.classList.add('dev-closed'); + dev.classList.remove('dev-open'); + + $('load-unpacked').classList.add('dev-button-hidden'); + $('load-unpacked').classList.remove('dev-button-visible'); + $('pack-extension').classList.add('dev-button-hidden'); + $('pack-extension').classList.remove('dev-button-visible'); + $('update-extensions-now').classList.add('dev-button-hidden'); + $('update-extensions-now').classList.remove('dev-button-visible'); + } + + chrome.send('extensionSettingsToggleDeveloperMode', []); + }, + }; + + /** + * Called by the dom_ui_ to re-populate the page with data representing + * the current state of installed extensions. + */ + ExtensionSettings.returnExtensionsData = function(extensionsData) { + $('no-extensions').hidden = true; + $('suggest-gallery').hidden = true; + $('get-more-extensions-container').hidden = true; + + if (extensionsData.extensions.length > 0) { + // Enforce order specified in the data or (if equal) then sort by + // extension name (case-insensitive). + extensionsData.extensions.sort(function(a, b) { + if (a.order == b.order) { + a = a.name.toLowerCase(); + b = b.name.toLowerCase(); + return a < b ? -1 : (a > b ? 1 : 0); + } else { + return a.order < b.order ? -1 : 1; + } + }); + + $('get-more-extensions-container').hidden = false; + } else { + $('no-extensions').hidden = false; + $('suggest-gallery').hidden = false; + } + + ExtensionsList.prototype.data_ = extensionsData; + + var extensionList = $('extension-settings-list'); + ExtensionsList.decorate(extensionList); + } + + // Export + return { + ExtensionSettings: ExtensionSettings + }; +}); diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html index de3754c..0b14ab8 100644 --- a/chrome/browser/resources/options/options.html +++ b/chrome/browser/resources/options/options.html @@ -24,6 +24,7 @@ <link rel="stylesheet" href="clear_browser_data_overlay.css"> <link rel="stylesheet" href="content_settings.css"> <link rel="stylesheet" href="cookies_view.css"> +<link rel="stylesheet" href="extension_settings.css"> <link rel="stylesheet" href="font_settings.css"> <if expr="pp_ifdef('enable_register_protocol_handler')"> <link rel="stylesheet" href="handler_options.css"> @@ -34,6 +35,7 @@ </if> <link rel="stylesheet" href="language_options.css"> <link rel="stylesheet" href="manage_profile_overlay.css"> +<link rel="stylesheet" href="pack_extension_overlay.css"> <link rel="stylesheet" href="password_manager.css"> <link rel="stylesheet" href="password_manager_list.css"> <link rel="stylesheet" href="personal_options.css"> @@ -88,6 +90,7 @@ <include src="instant_confirm_overlay.html"> <include src="language_add_language_overlay.html"> <include src="manage_profile_overlay.html"> + <include src="pack_extension_overlay.html"> <include src="../sync_setup_overlay.html"> <if expr="pp_ifdef('chromeos')"> <include @@ -126,6 +129,7 @@ <include src="chromeos/accounts_options.html"> </if> <include src="advanced_options.html"> + <include src="extension_settings.html"> </div> <div id="subpage-sheet-container-1" class="subpage-sheet-container transparent" hidden> diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js index 3e5c41b..bb0f3d2 100644 --- a/chrome/browser/resources/options/options.js +++ b/chrome/browser/resources/options/options.js @@ -14,6 +14,7 @@ var ContentSettings = options.ContentSettings; var ContentSettingsExceptionsArea = options.contentSettings.ContentSettingsExceptionsArea; var CookiesView = options.CookiesView; +var ExtensionSettings = options.ExtensionSettings; var FontSettings = options.FontSettings; var HandlerOptions = options.HandlerOptions; var ImportDataOverlay = options.ImportDataOverlay; @@ -21,6 +22,7 @@ var IntentsView = options.IntentsView; var InstantConfirmOverlay = options.InstantConfirmOverlay; var LanguageOptions = options.LanguageOptions; var OptionsPage = options.OptionsPage; +var PackExtensionOverlay = options.PackExtensionOverlay; var PasswordManager = options.PasswordManager; var PersonalOptions = options.PersonalOptions; var Preferences = options.Preferences; @@ -166,6 +168,10 @@ function load() { OptionsPage.registerOverlay(ManageProfileOverlay.getInstance(), PersonalOptions.getInstance()); + OptionsPage.register(ExtensionSettings.getInstance()); + OptionsPage.registerOverlay(PackExtensionOverlay.getInstance(), + ExtensionSettings.getInstance()); + if (cr.isChromeOS) { OptionsPage.register(AccountsOptions.getInstance()); OptionsPage.registerSubPage(ProxyOptions.getInstance(), diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js index 0b58a74..3701f1a 100644 --- a/chrome/browser/resources/options/options_bundle.js +++ b/chrome/browser/resources/options/options_bundle.js @@ -61,6 +61,8 @@ <include src="content_settings_ui.js"></include> <include src="cookies_list.js"></include> <include src="cookies_view.js"></include> +<include src="extension_list.js"></include> +<include src="extension_settings.js"></include> <include src="font_settings.js"></include> <if expr="pp_ifdef('enable_register_protocol_handler')"> <include src="handler_options.js"></script> @@ -76,6 +78,7 @@ <include src="language_list.js"></include> <include src="language_options.js"></include> <include src="manage_profile_overlay.js"></include> +<include src="pack_extension_overlay.js"></include> <include src="password_manager.js"></include> <include src="password_manager_list.js"></include> <include src="personal_options.js"></include> diff --git a/chrome/browser/resources/options/pack_extension_overlay.css b/chrome/browser/resources/options/pack_extension_overlay.css new file mode 100644 index 0000000..169750b --- /dev/null +++ b/chrome/browser/resources/options/pack_extension_overlay.css @@ -0,0 +1,18 @@ +/* +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +*/ + +.packExtensionHeading { + width: 520px; + padding-bottom: 5px; +} + +.packExtensionTextBoxes { + text-align: right; +} + +.packExtensionTextArea { + width: 260px; +} diff --git a/chrome/browser/resources/options/pack_extension_overlay.html b/chrome/browser/resources/options/pack_extension_overlay.html new file mode 100644 index 0000000..bd77789 --- /dev/null +++ b/chrome/browser/resources/options/pack_extension_overlay.html @@ -0,0 +1,28 @@ +<div id="packExtensionOverlay" class="page" hidden> + <h1 i18n-content="packExtensionOverlay"></h1> + <div id="cbdContentArea" class="content-area"> + <div class="packExtensionHeading" i18n-content="packExtensionHeading"></div> + <div class="packExtensionTextBoxes"> + <label i18n-content="packExtensionRootDir"></label> + <input class="packExtensionTextArea" id="extensionRootDir" type="text" /> + <button id="browseExtensionDir" + i18n-content="packExtensionBrowseButton"></button> + </div> + <div class="packExtensionTextBoxes"> + <label i18n-content="packExtensionPrivateKey"></label> + <input class="packExtensionTextArea" + id="extensionPrivateKey" type="text" /> + <button id="browsePrivateKey" + i18n-content="packExtensionBrowseButton"></button> + </div> + </div> + <div class="action-area"> + <div class="action-area-right"> + <div class="button-strip"> + <button id="packExtensionDismiss" i18n-content="cancel"></button> + <button id="packExtensionCommit" + i18n-content="packExtensionCommit"></button> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/chrome/browser/resources/options/pack_extension_overlay.js b/chrome/browser/resources/options/pack_extension_overlay.js new file mode 100644 index 0000000..6e69b03 --- /dev/null +++ b/chrome/browser/resources/options/pack_extension_overlay.js @@ -0,0 +1,90 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + const OptionsPage = options.OptionsPage; + + /** + * PackExtensionOverlay class + * Encapsulated handling of the 'Pack Extension' overlay page. + * @class + */ + function PackExtensionOverlay() { + OptionsPage.call(this, 'packExtensionOverlay', + templateData.packExtensionOverlayTabTitle, + 'packExtensionOverlay'); + } + + cr.addSingletonGetter(PackExtensionOverlay); + + PackExtensionOverlay.prototype = { + // Inherit PackExtensionOverlay from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to starts preference initialization. + OptionsPage.prototype.initializePage.call(this); + + $('packExtensionDismiss').onclick = function(event) { + OptionsPage.closeOverlay(); + }; + $('packExtensionCommit').onclick = function(event) { + var extensionPath = $('extensionRootDir').value; + var privateKeyPath = $('extensionPrivateKey').value; + chrome.send('pack', [extensionPath, privateKeyPath]); + }; + $('browseExtensionDir').addEventListener('click', + this.handleBrowseExtensionDir_.bind(this)); + $('browsePrivateKey').addEventListener('click', + this.handleBrowsePrivateKey_.bind(this)); + }, + + /** + * Utility function which asks the C++ to show a platform-specific file + * select dialog, and fire |callback| with the |filePath| that resulted. + * |selectType| can be either 'file' or 'folder'. |operation| can be 'load', + * 'packRoot', or 'pem' which are signals to the C++ to do some + * operation-specific configuration. + @private + */ + showFileDialog_: function(selectType, operation, callback) { + handleFilePathSelected = function(filePath) { + callback(filePath); + handleFilePathSelected = function() {}; + }; + + chrome.send('extensionSettingsSelectFilePath', [selectType, operation]); + }, + + /** + * Handles the showing of the extension directory browser. + * @param {Event} e Change event. + * @private + */ + handleBrowseExtensionDir_: function(e) { + this.showFileDialog_('folder', 'load', function(filePath) { + $('extensionRootDir').value = filePath; + }); + }, + + /** + * Handles the showing of the extension private key file. + * @param {Event} e Change event. + * @private + */ + handleBrowsePrivateKey_: function(e) { + this.showFileDialog_('file', 'load', function(filePath) { + $('extensionPrivateKey').value = filePath; + }); + }, + }; + + // Export + return { + PackExtensionOverlay: PackExtensionOverlay + }; +}); diff --git a/chrome/browser/resources/options/zippy.png b/chrome/browser/resources/options/zippy.png Binary files differindex 4b4adff..c16f42e 100644 --- a/chrome/browser/resources/options/zippy.png +++ b/chrome/browser/resources/options/zippy.png diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.cc b/chrome/browser/ui/webui/options/extension_settings_handler.cc index b7ac296..4fb7156 100644 --- a/chrome/browser/ui/webui/options/extension_settings_handler.cc +++ b/chrome/browser/ui/webui/options/extension_settings_handler.cc @@ -2,42 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/extensions/extensions_ui.h" - -#include <algorithm> +#include "chrome/browser/ui/webui/options/extension_settings_handler.h" #include "base/base64.h" -#include "base/callback.h" #include "base/file_util.h" -#include "base/memory/singleton.h" #include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/threading/thread.h" #include "base/utf_string_conversions.h" +#include "base/values.h" #include "base/version.h" +#include "chrome/browser/debugger/devtools_window.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h" -#include "chrome/browser/extensions/extension_error_reporter.h" -#include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/google/google_util.h" -#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/background_contents.h" -#include "chrome/browser/ui/browser_list.h" #include "chrome/common/extensions/extension.h" -#include "chrome/common/extensions/extension_icon_set.h" -#include "chrome/common/extensions/url_pattern.h" -#include "chrome/common/extensions/user_script.h" -#include "chrome/common/jstemplate_builder.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" -#include "content/browser/debugger/devtools_window.h" #include "content/browser/renderer_host/render_process_host.h" #include "content/browser/renderer_host/render_view_host.h" -#include "content/browser/renderer_host/render_widget_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" #include "content/common/content_notification_types.h" @@ -46,7 +31,6 @@ #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" -#include "net/base/net_util.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/codec/png_codec.h" @@ -81,130 +65,16 @@ bool ShouldShowExtension(const Extension* extension) { //////////////////////////////////////////////////////////////////////////////// // -// ExtensionsHTMLSource -// -//////////////////////////////////////////////////////////////////////////////// - -ExtensionsUIHTMLSource::ExtensionsUIHTMLSource() - : DataSource(chrome::kChromeUIExtensionsHost, MessageLoop::current()) { -} - -void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path, - bool is_incognito, - int request_id) { - DictionaryValue localized_strings; - localized_strings.SetString("title", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_TITLE)); - localized_strings.SetString("devModeLink", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK)); - localized_strings.SetString("devModePrefix", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_PREFIX)); - localized_strings.SetString("loadUnpackedButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON)); - localized_strings.SetString("packButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON)); - localized_strings.SetString("updateButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON)); - localized_strings.SetString("noExtensions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED)); - localized_strings.SetString("suggestGallery", - l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY, - ASCIIToUTF16("<a href='") + - ASCIIToUTF16(google_util::AppendGoogleLocaleParam( - GURL(Extension::ChromeStoreLaunchURL())).spec()) + - ASCIIToUTF16("'>"), - ASCIIToUTF16("</a>"))); - localized_strings.SetString("getMoreExtensions", - ASCIIToUTF16("<a href='") + - ASCIIToUTF16(google_util::AppendGoogleLocaleParam( - GURL(Extension::ChromeStoreLaunchURL())).spec()) + - ASCIIToUTF16("'>") + - l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS) + - ASCIIToUTF16("</a>")); - localized_strings.SetString("extensionCrashed", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION)); - localized_strings.SetString("extensionDisabled", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLED_EXTENSION)); - localized_strings.SetString("inDevelopment", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT)); - localized_strings.SetString("viewIncognito", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO)); - localized_strings.SetString("extensionId", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); - localized_strings.SetString("extensionVersion", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_VERSION)); - localized_strings.SetString("inspectViews", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS)); - localized_strings.SetString("inspectPopupsInstructions", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_POPUPS_INSTRUCTIONS)); - localized_strings.SetString("disable", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE)); - localized_strings.SetString("enable", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE)); - localized_strings.SetString("enableIncognito", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO)); - localized_strings.SetString("allowFileAccess", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS)); - localized_strings.SetString("incognitoWarning", - l10n_util::GetStringFUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING, - l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); - localized_strings.SetString("reload", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD)); - localized_strings.SetString("uninstall", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL)); - localized_strings.SetString("options", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS)); - localized_strings.SetString("packDialogTitle", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE)); - localized_strings.SetString("packDialogHeading", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING)); - localized_strings.SetString("rootDirectoryLabel", - l10n_util::GetStringUTF16( - IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL)); - localized_strings.SetString("packDialogBrowse", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE)); - localized_strings.SetString("privateKeyLabel", - l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL)); - localized_strings.SetString("okButton", - l10n_util::GetStringUTF16(IDS_OK)); - localized_strings.SetString("cancelButton", - l10n_util::GetStringUTF16(IDS_CANCEL)); - localized_strings.SetString("showButton", - l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON)); - - SetFontAndTextDirection(&localized_strings); - - static const base::StringPiece extensions_html( - ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_EXTENSIONS_UI_HTML)); - std::string full_html(extensions_html.data(), extensions_html.size()); - jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); - jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); - jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); - jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); - - scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); - html_bytes->data.resize(full_html.size()); - std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); - - SendResponse(request_id, html_bytes); -} - -std::string ExtensionsUIHTMLSource::GetMimeType(const std::string&) const { - return "text/html"; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// ExtensionsDOMHandler::IconLoader +// ExtensionSettingsHandler::IconLoader // //////////////////////////////////////////////////////////////////////////////// -ExtensionsDOMHandler::IconLoader::IconLoader(ExtensionsDOMHandler* handler) +ExtensionSettingsHandler::IconLoader::IconLoader( + ExtensionSettingsHandler* handler) : handler_(handler) { } -void ExtensionsDOMHandler::IconLoader::LoadIcons( +void ExtensionSettingsHandler::IconLoader::LoadIcons( std::vector<ExtensionResource>* icons, DictionaryValue* json) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, @@ -212,11 +82,11 @@ void ExtensionsDOMHandler::IconLoader::LoadIcons( &IconLoader::LoadIconsOnFileThread, icons, json)); } -void ExtensionsDOMHandler::IconLoader::Cancel() { +void ExtensionSettingsHandler::IconLoader::Cancel() { handler_ = NULL; } -void ExtensionsDOMHandler::IconLoader::LoadIconsOnFileThread( +void ExtensionSettingsHandler::IconLoader::LoadIconsOnFileThread( std::vector<ExtensionResource>* icons, DictionaryValue* json) { scoped_ptr<std::vector<ExtensionResource> > icons_deleter(icons); scoped_ptr<DictionaryValue> json_deleter(json); @@ -279,7 +149,7 @@ void ExtensionsDOMHandler::IconLoader::LoadIconsOnFileThread( json_deleter.release())); } -void ExtensionsDOMHandler::IconLoader::ReportResultOnUIThread( +void ExtensionSettingsHandler::IconLoader::ReportResultOnUIThread( DictionaryValue* json) { if (handler_) handler_->OnIconsLoaded(json); @@ -288,48 +158,62 @@ void ExtensionsDOMHandler::IconLoader::ReportResultOnUIThread( /////////////////////////////////////////////////////////////////////////////// // -// ExtensionsDOMHandler +// ExtensionSettingsHandler // /////////////////////////////////////////////////////////////////////////////// -ExtensionsDOMHandler::ExtensionsDOMHandler(ExtensionService* extension_service) - : extension_service_(extension_service), +ExtensionSettingsHandler::ExtensionSettingsHandler() + : extension_service_(NULL), ignore_notifications_(false), deleting_rvh_(NULL) { } -void ExtensionsDOMHandler::RegisterMessages() { - web_ui_->RegisterMessageCallback("requestExtensionsData", - NewCallback(this, &ExtensionsDOMHandler::HandleRequestExtensionsData)); - web_ui_->RegisterMessageCallback("toggleDeveloperMode", - NewCallback(this, &ExtensionsDOMHandler::HandleToggleDeveloperMode)); - web_ui_->RegisterMessageCallback("inspect", - NewCallback(this, &ExtensionsDOMHandler::HandleInspectMessage)); - web_ui_->RegisterMessageCallback("reload", - NewCallback(this, &ExtensionsDOMHandler::HandleReloadMessage)); - web_ui_->RegisterMessageCallback("enable", - NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage)); - web_ui_->RegisterMessageCallback("enableIncognito", - NewCallback(this, &ExtensionsDOMHandler::HandleEnableIncognitoMessage)); - web_ui_->RegisterMessageCallback("allowFileAccess", - NewCallback(this, &ExtensionsDOMHandler::HandleAllowFileAccessMessage)); - web_ui_->RegisterMessageCallback("uninstall", - NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage)); - web_ui_->RegisterMessageCallback("options", - NewCallback(this, &ExtensionsDOMHandler::HandleOptionsMessage)); - web_ui_->RegisterMessageCallback("showButton", - NewCallback(this, &ExtensionsDOMHandler::HandleShowButtonMessage)); - web_ui_->RegisterMessageCallback("load", - NewCallback(this, &ExtensionsDOMHandler::HandleLoadMessage)); - web_ui_->RegisterMessageCallback("pack", - NewCallback(this, &ExtensionsDOMHandler::HandlePackMessage)); - web_ui_->RegisterMessageCallback("autoupdate", - NewCallback(this, &ExtensionsDOMHandler::HandleAutoUpdateMessage)); - web_ui_->RegisterMessageCallback("selectFilePath", - NewCallback(this, &ExtensionsDOMHandler::HandleSelectFilePathMessage)); -} - -void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { +ExtensionSettingsHandler::~ExtensionSettingsHandler() { + // There may be pending file dialogs, we need to tell them that we've gone + // away so they don't try and call back to us. + if (load_extension_dialog_.get()) + load_extension_dialog_->ListenerDestroyed(); + + if (icon_loader_.get()) + icon_loader_->Cancel(); +} + +void ExtensionSettingsHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("extensionSettingsRequestExtensionsData", + NewCallback(this, + &ExtensionSettingsHandler::HandleRequestExtensionsData)); + web_ui_->RegisterMessageCallback("extensionSettingsToggleDeveloperMode", + NewCallback(this, &ExtensionSettingsHandler::HandleToggleDeveloperMode)); + web_ui_->RegisterMessageCallback("extensionSettingsInspect", + NewCallback(this, &ExtensionSettingsHandler::HandleInspectMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsReload", + NewCallback(this, &ExtensionSettingsHandler::HandleReloadMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsEnable", + NewCallback(this, + &ExtensionSettingsHandler::HandleEnableMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsEnableIncognito", + NewCallback(this, + &ExtensionSettingsHandler::HandleEnableIncognitoMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsAllowFileAccess", + NewCallback(this, + &ExtensionSettingsHandler::HandleAllowFileAccessMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsUninstall", + NewCallback(this, &ExtensionSettingsHandler::HandleUninstallMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsOptions", + NewCallback(this, &ExtensionSettingsHandler::HandleOptionsMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsShowButton", + NewCallback(this, &ExtensionSettingsHandler::HandleShowButtonMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsLoad", + NewCallback(this, &ExtensionSettingsHandler::HandleLoadMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsAutoupdate", + NewCallback(this, &ExtensionSettingsHandler::HandleAutoUpdateMessage)); + web_ui_->RegisterMessageCallback("extensionSettingsSelectFilePath", + NewCallback(this, + &ExtensionSettingsHandler::HandleSelectFilePathMessage)); +} + +void ExtensionSettingsHandler::HandleRequestExtensionsData( + const ListValue* args) { DictionaryValue* results = new DictionaryValue(); // Add the extensions to the results structure. @@ -346,7 +230,7 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { extension != extensions->end(); ++extension) { if (ShouldShowExtension(*extension)) { extensions_list->Append(CreateExtensionDetailValue( - extension_service_.get(), + extension_service_, *extension, GetActivePagesForExtension(*extension), true, false)); // enabled, terminated @@ -358,7 +242,7 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { extension != extensions->end(); ++extension) { if (ShouldShowExtension(*extension)) { extensions_list->Append(CreateExtensionDetailValue( - extension_service_.get(), + extension_service_, *extension, GetActivePagesForExtension(*extension), false, false)); // enabled, terminated @@ -371,7 +255,7 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { extension != extensions->end(); ++extension) { if (ShouldShowExtension(*extension)) { extensions_list->Append(CreateExtensionDetailValue( - extension_service_.get(), + extension_service_, *extension, empty_pages, // Terminated process has no active pages. false, true)); // enabled, terminated @@ -380,8 +264,9 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { } results->Set("extensions", extensions_list); - bool developer_mode = web_ui_->GetProfile()->GetPrefs() - ->GetBoolean(prefs::kExtensionsUIDeveloperMode); + Profile* profile = Profile::FromWebUI(web_ui_); + bool developer_mode = + profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode); results->SetBoolean("developerMode", developer_mode); if (icon_loader_.get()) @@ -391,67 +276,72 @@ void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) { icon_loader_->LoadIcons(extension_icons, results); } -void ExtensionsDOMHandler::OnIconsLoaded(DictionaryValue* json) { - web_ui_->CallJavascriptFunction(L"returnExtensionsData", *json); +void ExtensionSettingsHandler::OnIconsLoaded(DictionaryValue* json) { + web_ui_->CallJavascriptFunction("ExtensionSettings.returnExtensionsData", + *json); delete json; // Register for notifications that we need to reload the page. registrar_.RemoveAll(); - registrar_.Add(this, chrome::EXTENSION_LOADED, + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, NotificationService::AllSources()); - registrar_.Add(this, chrome::EXTENSION_PROCESS_CREATED, + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED, NotificationService::AllSources()); - registrar_.Add(this, chrome::EXTENSION_UNLOADED, + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, NotificationService::AllSources()); - registrar_.Add(this, chrome::EXTENSION_UPDATE_DISABLED, + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED, NotificationService::AllSources()); registrar_.Add(this, - chrome::NAV_ENTRY_COMMITTED, + content::NOTIFICATION_NAV_ENTRY_COMMITTED, NotificationService::AllSources()); registrar_.Add(this, - chrome::RENDER_VIEW_HOST_CREATED, + content::NOTIFICATION_RENDER_VIEW_HOST_CREATED, NotificationService::AllSources()); registrar_.Add(this, - chrome::RENDER_VIEW_HOST_DELETED, + content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, NotificationService::AllSources()); registrar_.Add(this, - chrome::BACKGROUND_CONTENTS_NAVIGATED, + chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, NotificationService::AllSources()); registrar_.Add(this, - chrome::BACKGROUND_CONTENTS_DELETED, + chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, NotificationService::AllSources()); registrar_.Add(this, - chrome::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, + chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, NotificationService::AllSources()); } -ExtensionResource ExtensionsDOMHandler::PickExtensionIcon( +ExtensionResource ExtensionSettingsHandler::PickExtensionIcon( const Extension* extension) { return extension->GetIconResource(Extension::EXTENSION_ICON_MEDIUM, ExtensionIconSet::MATCH_BIGGER); } -ExtensionUninstallDialog* ExtensionsDOMHandler::GetExtensionUninstallDialog() { +ExtensionUninstallDialog* +ExtensionSettingsHandler::GetExtensionUninstallDialog() { if (!extension_uninstall_dialog_.get()) { extension_uninstall_dialog_.reset( - new ExtensionUninstallDialog(web_ui_->GetProfile())); + new ExtensionUninstallDialog(Profile::FromWebUI(web_ui_))); } return extension_uninstall_dialog_.get(); } -void ExtensionsDOMHandler::HandleToggleDeveloperMode(const ListValue* args) { - bool developer_mode = web_ui_->GetProfile()->GetPrefs() - ->GetBoolean(prefs::kExtensionsUIDeveloperMode); - web_ui_->GetProfile()->GetPrefs()->SetBoolean( +void ExtensionSettingsHandler::HandleToggleDeveloperMode( + const ListValue* args) { + Profile* profile = Profile::FromWebUI(web_ui_); + bool developer_mode = + profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode); + profile->GetPrefs()->SetBoolean( prefs::kExtensionsUIDeveloperMode, !developer_mode); + HandleRequestExtensionsData(NULL); } -void ExtensionsDOMHandler::HandleInspectMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleInspectMessage(const ListValue* args) { std::string render_process_id_str; std::string render_view_id_str; int render_process_id; int render_view_id; - CHECK(args->GetSize() == 2); + CHECK_EQ(2U, args->GetSize()); CHECK(args->GetString(0, &render_process_id_str)); CHECK(args->GetString(1, &render_view_id_str)); CHECK(base::StringToInt(render_process_id_str, &render_process_id)); @@ -466,24 +356,31 @@ void ExtensionsDOMHandler::HandleInspectMessage(const ListValue* args) { DevToolsWindow::OpenDevToolsWindow(host); } -void ExtensionsDOMHandler::HandleReloadMessage(const ListValue* args) { - std::string extension_id = WideToASCII(ExtractStringValue(args)); +void ExtensionSettingsHandler::HandleReloadMessage(const ListValue* args) { + std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); CHECK(!extension_id.empty()); extension_service_->ReloadExtension(extension_id); } -void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) { - CHECK(args->GetSize() == 2); +void ExtensionSettingsHandler::HandleEnableMessage(const ListValue* args) { + CHECK_EQ(2U, args->GetSize()); std::string extension_id, enable_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &enable_str)); + + const Extension* extension = + extension_service_->GetExtensionById(extension_id, true); + if (!Extension::UserMayDisable(extension->location())) { + LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was" + << "made. Extension id: " << extension->id(); + return; + } + if (enable_str == "true") { ExtensionPrefs* prefs = extension_service_->extension_prefs(); if (prefs->DidExtensionEscalatePermissions(extension_id)) { - const Extension* extension = - extension_service_->GetExtensionById(extension_id, true); ShowExtensionDisabledDialog(extension_service_, - web_ui_->GetProfile(), extension); + Profile::FromWebUI(web_ui_), extension); } else { extension_service_->EnableExtension(extension_id); } @@ -492,8 +389,9 @@ void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) { } } -void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) { - CHECK(args->GetSize() == 2); +void ExtensionSettingsHandler::HandleEnableIncognitoMessage( + const ListValue* args) { + CHECK_EQ(2U, args->GetSize()); std::string extension_id, enable_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &enable_str)); @@ -513,12 +411,14 @@ void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) { // // Bug: http://crbug.com/41384 ignore_notifications_ = true; - extension_service_->SetIsIncognitoEnabled(extension, enable_str == "true"); + extension_service_->SetIsIncognitoEnabled(extension->id(), + enable_str == "true"); ignore_notifications_ = false; } -void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) { - CHECK(args->GetSize() == 2); +void ExtensionSettingsHandler::HandleAllowFileAccessMessage( + const ListValue* args) { + CHECK_EQ(2U, args->GetSize()); std::string extension_id, allow_str; CHECK(args->GetString(0, &extension_id)); CHECK(args->GetString(1, &allow_str)); @@ -526,11 +426,18 @@ void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) { extension_service_->GetExtensionById(extension_id, true); DCHECK(extension); + if (!Extension::UserMayDisable(extension->location())) { + LOG(ERROR) << "Attempt to change allow file access of an extension that is " + << "non-usermanagable was made. Extension id : " + << extension->id(); + return; + } + extension_service_->SetAllowFileAccess(extension, allow_str == "true"); } -void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { - std::string extension_id = WideToASCII(ExtractStringValue(args)); +void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) { + std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); CHECK(!extension_id.empty()); const Extension* extension = extension_service_->GetExtensionById(extension_id, true); @@ -539,6 +446,12 @@ void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { if (!extension) return; + if (!Extension::UserMayDisable(extension->location())) { + LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable " + << "was made. Extension id : " << extension->id(); + return; + } + if (!extension_id_prompting_.empty()) return; // Only one prompt at a time. @@ -547,7 +460,7 @@ void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) { GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); } -void ExtensionsDOMHandler::ExtensionDialogAccepted() { +void ExtensionSettingsHandler::ExtensionDialogAccepted() { DCHECK(!extension_id_prompting_.empty()); bool was_terminated = false; @@ -565,7 +478,8 @@ void ExtensionsDOMHandler::ExtensionDialogAccepted() { return; extension_service_->UninstallExtension(extension_id_prompting_, - false /* external_uninstall */, NULL); + false, // External uninstall. + NULL); // Error. extension_id_prompting_ = ""; // There will be no EXTENSION_UNLOADED notification for terminated @@ -574,92 +488,47 @@ void ExtensionsDOMHandler::ExtensionDialogAccepted() { HandleRequestExtensionsData(NULL); } -void ExtensionsDOMHandler::ExtensionDialogCanceled() { +void ExtensionSettingsHandler::ExtensionDialogCanceled() { extension_id_prompting_ = ""; } -void ExtensionsDOMHandler::HandleOptionsMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleOptionsMessage(const ListValue* args) { const Extension* extension = GetExtension(args); if (!extension || extension->options_url().is_empty()) return; - web_ui_->GetProfile()->GetExtensionProcessManager()->OpenOptionsPage( + Profile::FromWebUI(web_ui_)->GetExtensionProcessManager()->OpenOptionsPage( extension, NULL); } -void ExtensionsDOMHandler::HandleShowButtonMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleShowButtonMessage(const ListValue* args) { const Extension* extension = GetExtension(args); extension_service_->SetBrowserActionVisibility(extension, true); } -void ExtensionsDOMHandler::HandleLoadMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleLoadMessage(const ListValue* args) { FilePath::StringType string_path; - CHECK(args->GetSize() == 1) << args->GetSize(); + CHECK_EQ(1U, args->GetSize()) << args->GetSize(); CHECK(args->GetString(0, &string_path)); extension_service_->LoadExtension(FilePath(string_path)); } -void ExtensionsDOMHandler::ShowAlert(const std::string& message) { +void ExtensionSettingsHandler::ShowAlert(const std::string& message) { ListValue arguments; arguments.Append(Value::CreateStringValue(message)); - web_ui_->CallJavascriptFunction(L"alert", arguments); -} - -void ExtensionsDOMHandler::HandlePackMessage(const ListValue* args) { - std::string extension_path; - std::string private_key_path; - CHECK(args->GetSize() == 2); - CHECK(args->GetString(0, &extension_path)); - CHECK(args->GetString(1, &private_key_path)); - - FilePath root_directory = - FilePath::FromWStringHack(UTF8ToWide(extension_path)); - FilePath key_file = FilePath::FromWStringHack(UTF8ToWide(private_key_path)); - - if (root_directory.empty()) { - if (extension_path.empty()) { - ShowAlert(l10n_util::GetStringUTF8( - IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED)); - } else { - ShowAlert(l10n_util::GetStringUTF8( - IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID)); - } - - return; - } - - if (!private_key_path.empty() && key_file.empty()) { - ShowAlert(l10n_util::GetStringUTF8( - IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID)); - return; - } - - pack_job_ = new PackExtensionJob(this, root_directory, key_file); - pack_job_->Start(); + web_ui_->CallJavascriptFunction("alert", arguments); } -void ExtensionsDOMHandler::OnPackSuccess(const FilePath& crx_file, - const FilePath& pem_file) { - ShowAlert(UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(crx_file, - pem_file))); - - ListValue results; - web_ui_->CallJavascriptFunction(L"hidePackDialog", results); -} - -void ExtensionsDOMHandler::OnPackFailure(const std::string& error) { - ShowAlert(error); -} - -void ExtensionsDOMHandler::HandleAutoUpdateMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) { ExtensionUpdater* updater = extension_service_->updater(); if (updater) updater->CheckNow(); } -void ExtensionsDOMHandler::HandleSelectFilePathMessage(const ListValue* args) { +void ExtensionSettingsHandler::HandleSelectFilePathMessage( + const ListValue* args) { std::string select_type; std::string operation; - CHECK(args->GetSize() == 2); + CHECK_EQ(2U, args->GetSize()); CHECK(args->GetString(0, &select_type)); CHECK(args->GetString(1, &operation)); @@ -697,22 +566,101 @@ void ExtensionsDOMHandler::HandleSelectFilePathMessage(const ListValue* args) { } -void ExtensionsDOMHandler::FileSelected(const FilePath& path, int index, - void* params) { +void ExtensionSettingsHandler::FileSelected(const FilePath& path, int index, + void* params) { // Add the extensions to the results structure. ListValue results; results.Append(Value::CreateStringValue(path.value())); - web_ui_->CallJavascriptFunction(L"window.handleFilePathSelected", results); + web_ui_->CallJavascriptFunction("window.handleFilePathSelected", results); } -void ExtensionsDOMHandler::MultiFilesSelected( +void ExtensionSettingsHandler::MultiFilesSelected( const std::vector<FilePath>& files, void* params) { NOTREACHED(); } -void ExtensionsDOMHandler::Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) { +void ExtensionSettingsHandler::GetLocalizedValues( + DictionaryValue* localized_strings) { + DCHECK(localized_strings); + + RegisterTitle(localized_strings, "extensionSettings", + IDS_OPTIONS_GENERAL_TAB_LABEL); + + localized_strings->SetString("extensionSettingsTitle", + l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE)); + localized_strings->SetString("extensionSettingsVisitWebsite", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE)); + + localized_strings->SetString("extensionSettingsDeveloperMode", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK)); + localized_strings->SetString("extensionSettingsNoExtensions", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED)); + localized_strings->SetString("extensionSettingsSuggestGallery", + l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY, + ASCIIToUTF16("<a href='") + + ASCIIToUTF16(google_util::AppendGoogleLocaleParam( + GURL(extension_urls::GetWebstoreLaunchURL())).spec()) + + ASCIIToUTF16("'>"), + ASCIIToUTF16("</a>"))); + localized_strings->SetString("extensionSettingsGetMoreExtensions", + ASCIIToUTF16("<a href='") + + ASCIIToUTF16(google_util::AppendGoogleLocaleParam( + GURL(extension_urls::GetWebstoreLaunchURL())).spec()) + + ASCIIToUTF16("'>") + + l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS) + + ASCIIToUTF16("</a>")); + localized_strings->SetString("extensionSettingsExtensionId", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); + localized_strings->SetString("extensionSettingsExtensionPath", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH)); + localized_strings->SetString("extensionSettingsInspectViews", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS)); + localized_strings->SetString("extensionSettingsEnable", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE)); + localized_strings->SetString("extensionSettingsEnabled", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED)); + localized_strings->SetString("extensionSettingsRemove", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE)); + localized_strings->SetString("extensionSettingsEnableIncognito", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO)); + localized_strings->SetString("extensionSettingsAllowFileAccess", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS)); + localized_strings->SetString("extensionSettingsIncognitoWarning", + l10n_util::GetStringFUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + localized_strings->SetString("extensionSettingsReload", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD)); + localized_strings->SetString("extensionSettingsOptions", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS)); + localized_strings->SetString("extensionSettingsPolicyControlled", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED)); + localized_strings->SetString("extensionSettingsShowButton", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON)); + localized_strings->SetString("extensionSettingsLoadUnpackedButton", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON)); + localized_strings->SetString("extensionSettingsPackButton", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON)); + localized_strings->SetString("extensionSettingsUpdateButton", + l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON)); +} + +void ExtensionSettingsHandler::Initialize() { +} + +WebUIMessageHandler* ExtensionSettingsHandler::Attach(WebUI* web_ui) { + // Call through to superclass. + WebUIMessageHandler* handler = OptionsPageUIHandler::Attach(web_ui); + + extension_service_ = Profile::FromWebUI(web_ui_) + ->GetOriginalProfile()->GetExtensionService(); + + // Return result from the superclass. + return handler; +} + +void ExtensionSettingsHandler::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { switch (type) { // We listen for notifications that will result in the page being // repopulated with data twice for the same event in certain cases. @@ -728,22 +676,22 @@ void ExtensionsDOMHandler::Observe(int type, // // Doing it this way gets everything but causes the page to be rendered // more than we need. It doesn't seem to result in any noticeable flicker. - case chrome::RENDER_VIEW_HOST_DELETED: - deleting_rvh_ = Details<RenderViewHost>(details).ptr(); + case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: + deleting_rvh_ = Source<RenderViewHost>(source).ptr(); MaybeUpdateAfterNotification(); break; - case chrome::BACKGROUND_CONTENTS_DELETED: + case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED: deleting_rvh_ = Details<BackgroundContents>(details)->render_view_host(); MaybeUpdateAfterNotification(); break; - case chrome::EXTENSION_LOADED: - case chrome::EXTENSION_PROCESS_CREATED: - case chrome::EXTENSION_UNLOADED: - case chrome::EXTENSION_UPDATE_DISABLED: - case chrome::RENDER_VIEW_HOST_CREATED: - case chrome::NAV_ENTRY_COMMITTED: - case chrome::BACKGROUND_CONTENTS_NAVIGATED: - case chrome::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED: + case chrome::NOTIFICATION_EXTENSION_LOADED: + case chrome::NOTIFICATION_EXTENSION_PROCESS_CREATED: + case chrome::NOTIFICATION_EXTENSION_UNLOADED: + case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED: + case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED: + case content::NOTIFICATION_NAV_ENTRY_COMMITTED: + case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED: + case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED: MaybeUpdateAfterNotification(); break; default: @@ -751,20 +699,20 @@ void ExtensionsDOMHandler::Observe(int type, } } -const Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) { - std::string extension_id = WideToASCII(ExtractStringValue(args)); +const Extension* ExtensionSettingsHandler::GetExtension(const ListValue* args) { + std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); CHECK(!extension_id.empty()); return extension_service_->GetExtensionById(extension_id, true); } -void ExtensionsDOMHandler::MaybeUpdateAfterNotification() { +void ExtensionSettingsHandler::MaybeUpdateAfterNotification() { if (!ignore_notifications_ && web_ui_->tab_contents()) HandleRequestExtensionsData(NULL); deleting_rvh_ = NULL; } // Static -DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( +DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue( ExtensionService* service, const Extension* extension, const std::vector<ExtensionPage>& pages, bool enabled, bool terminated) { DictionaryValue* extension_data = new DictionaryValue(); @@ -772,11 +720,16 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( extension_data->SetString("id", extension->id()); extension_data->SetString("name", extension->name()); extension_data->SetString("description", extension->description()); + extension_data->SetString("path", extension->path().value()); extension_data->SetString("version", extension->version()->GetString()); + extension_data->SetBoolean("isUnpacked", + extension->location() == Extension::LOAD); + extension_data->SetBoolean("mayDisable", + Extension::UserMayDisable(extension->location())); extension_data->SetBoolean("enabled", enabled); extension_data->SetBoolean("terminated", terminated); extension_data->SetBoolean("enabledIncognito", - service ? service->IsIncognitoEnabled(extension) : false); + service ? service->IsIncognitoEnabled(extension->id()) : false); extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access()); extension_data->SetBoolean("allowFileAccess", service ? service->AllowFileAccess(extension) : false); @@ -791,7 +744,7 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( else extension_data->SetInteger("order", 2); - if (!extension->options_url().is_empty()) + if (!extension->options_url().is_empty() && enabled) extension_data->SetString("options_url", extension->options_url().spec()); if (service && !service->GetBrowserActionVisibility(extension)) @@ -822,7 +775,7 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( return extension_data; } -std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( +std::vector<ExtensionPage> ExtensionSettingsHandler::GetActivePagesForExtension( const Extension* extension) { std::vector<ExtensionPage> result; @@ -847,7 +800,7 @@ std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension( return result; } -void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( +void ExtensionSettingsHandler::GetActivePagesForExtensionProcess( RenderProcessHost* process, const Extension* extension, std::vector<ExtensionPage> *result) { @@ -875,49 +828,8 @@ void ExtensionsDOMHandler::GetActivePagesForExtensionProcess( continue; } - result->push_back(ExtensionPage(url, process->id(), host->routing_id(), - process->profile()->IsOffTheRecord())); + result->push_back( + ExtensionPage(url, process->id(), host->routing_id(), + process->browser_context()->IsOffTheRecord())); } } - -ExtensionsDOMHandler::~ExtensionsDOMHandler() { - // There may be pending file dialogs, we need to tell them that we've gone - // away so they don't try and call back to us. - if (load_extension_dialog_.get()) - load_extension_dialog_->ListenerDestroyed(); - - if (pack_job_.get()) - pack_job_->ClearClient(); - - if (icon_loader_.get()) - icon_loader_->Cancel(); -} - -// ExtensionsDOMHandler, public: ----------------------------------------------- - -ExtensionsUI::ExtensionsUI(TabContents* contents) : ChromeWebUI(contents) { - ExtensionService *exstension_service = - GetProfile()->GetOriginalProfile()->GetExtensionService(); - - ExtensionsDOMHandler* handler = new ExtensionsDOMHandler(exstension_service); - AddMessageHandler(handler); - handler->Attach(this); - - ExtensionsUIHTMLSource* html_source = new ExtensionsUIHTMLSource(); - - // Set up the chrome://extensions/ source. - contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); -} - -// static -RefCountedMemory* ExtensionsUI::GetFaviconResourceBytes() { - return ResourceBundle::GetSharedInstance(). - LoadDataResourceBytes(IDR_PLUGIN); -} - -// static -void ExtensionsUI::RegisterUserPrefs(PrefService* prefs) { - prefs->RegisterBooleanPref(prefs::kExtensionsUIDeveloperMode, - false, - PrefService::SYNCABLE_PREF); -} diff --git a/chrome/browser/ui/webui/options/extension_settings_handler.h b/chrome/browser/ui/webui/options/extension_settings_handler.h index 5ec4a14..2b5d6c0 100644 --- a/chrome/browser/ui/webui/options/extension_settings_handler.h +++ b/chrome/browser/ui/webui/options/extension_settings_handler.h @@ -9,11 +9,10 @@ #include <string> #include <vector> -#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_uninstall_dialog.h" -#include "chrome/browser/extensions/pack_extension_job.h" #include "chrome/browser/ui/shell_dialogs.h" -#include "chrome/browser/ui/webui/chrome_url_data_manager.h" +#include "chrome/browser/ui/webui/options/options_ui.h" #include "chrome/browser/ui/webui/chrome_web_ui.h" #include "chrome/common/extensions/extension_resource.h" #include "content/common/notification_observer.h" @@ -47,31 +46,11 @@ struct ExtensionPage { bool incognito; }; -class ExtensionsUIHTMLSource : public ChromeURLDataManager::DataSource { +// Extension Settings UI handler. +class ExtensionSettingsHandler : public OptionsPageUIHandler, + public SelectFileDialog::Listener, + public ExtensionUninstallDialog::Delegate { public: - ExtensionsUIHTMLSource(); - - // Called when the network layer has requested a resource underneath - // the path we registered. - virtual void StartDataRequest(const std::string& path, - bool is_incognito, - int request_id); - virtual std::string GetMimeType(const std::string&) const; - - private: - ~ExtensionsUIHTMLSource() {} - - DISALLOW_COPY_AND_ASSIGN(ExtensionsUIHTMLSource); -}; - -// The handler for JavaScript messages related to the "extensions" view. -class ExtensionsDOMHandler : public WebUIMessageHandler, - public NotificationObserver, - public PackExtensionJob::Client, - public SelectFileDialog::Listener, - public ExtensionUninstallDialog::Delegate { - public: - // Helper class that loads the icons for the extensions in the management UI. // We do this with native code instead of just using chrome-extension:// URLs // for two reasons: @@ -82,7 +61,7 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, // look disabled. class IconLoader : public base::RefCountedThreadSafe<IconLoader> { public: - explicit IconLoader(ExtensionsDOMHandler* handler); + explicit IconLoader(ExtensionSettingsHandler* handler); // Load |icons|. Will call handler->OnIconsLoaded when complete. IconLoader // takes ownership of both arguments. @@ -103,14 +82,11 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, void ReportResultOnUIThread(base::DictionaryValue* json); // The handler we will report back to. - ExtensionsDOMHandler* handler_; + ExtensionSettingsHandler* handler_; }; - explicit ExtensionsDOMHandler(ExtensionService* extension_service); - virtual ~ExtensionsDOMHandler(); - - // WebUIMessageHandler implementation. - virtual void RegisterMessages(); + ExtensionSettingsHandler(); + virtual ~ExtensionSettingsHandler(); // Extension Detail JSON Struct for page. (static for ease of testing). // Note: service can be NULL in unit tests. @@ -126,17 +102,6 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, const UserScript& script, const FilePath& extension_path); - // ExtensionPackJob::Client - virtual void OnPackSuccess(const FilePath& crx_file, - const FilePath& key_file); - - virtual void OnPackFailure(const std::string& error); - - // ExtensionUninstallDialog::Delegate: - virtual void ExtensionDialogAccepted(); - virtual void ExtensionDialogCanceled(); - - private: // Callback for "requestExtensionsData" message. void HandleRequestExtensionsData(const base::ListValue* args); @@ -190,16 +155,31 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, // SelectFileDialog::Listener virtual void FileSelected(const FilePath& path, - int index, void* params); + int index, void* params) OVERRIDE; virtual void MultiFilesSelected( - const std::vector<FilePath>& files, void* params); - virtual void FileSelectionCanceled(void* params) {} + const std::vector<FilePath>& files, void* params) OVERRIDE; + virtual void FileSelectionCanceled(void* params) OVERRIDE {} - // NotificationObserver + // WebUIMessageHandler implementation. + virtual void RegisterMessages() OVERRIDE; + virtual WebUIMessageHandler* Attach(WebUI* web_ui) OVERRIDE; + + // OptionsUIHandler implementation. + virtual void GetLocalizedValues( + base::DictionaryValue* localized_strings) OVERRIDE; + virtual void Initialize() OVERRIDE; + + // NotificationObserver implementation. virtual void Observe(int type, const NotificationSource& source, - const NotificationDetails& details); + const NotificationDetails& details) OVERRIDE; + + // ExtensionUninstallDialog::Delegate implementation, used for receiving + // notification about uninstall confirmation dialog selections. + virtual void ExtensionDialogAccepted() OVERRIDE; + virtual void ExtensionDialogCanceled() OVERRIDE; + private: // Helper that lists the current active html pages for an extension. std::vector<ExtensionPage> GetActivePagesForExtension( const Extension* extension); @@ -227,14 +207,11 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, ExtensionUninstallDialog* GetExtensionUninstallDialog(); // Our model. Outlives us since it's owned by our containing profile. - ExtensionService* const extension_service_; + ExtensionService* extension_service_; // Used to pick the directory when loading an extension. scoped_refptr<SelectFileDialog> load_extension_dialog_; - // Used to package the extension. - scoped_refptr<PackExtensionJob> pack_job_; - // Used to load icons asynchronously on the file thread. scoped_refptr<IconLoader> icon_loader_; @@ -244,10 +221,6 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, // The id of the extension we are prompting the user about. std::string extension_id_prompting_; - // We monitor changes to the extension system so that we can reload when - // necessary. - NotificationRegistrar registrar_; - // If true, we will ignore notifications in ::Observe(). This is needed // to prevent reloading the page when we were the cause of the // notification. @@ -260,19 +233,7 @@ class ExtensionsDOMHandler : public WebUIMessageHandler, // it from the active views. RenderViewHost* deleting_rvh_; - DISALLOW_COPY_AND_ASSIGN(ExtensionsDOMHandler); -}; - -class ExtensionsUI : public ChromeWebUI { - public: - explicit ExtensionsUI(TabContents* contents); - - static RefCountedMemory* GetFaviconResourceBytes(); - - static void RegisterUserPrefs(PrefService* prefs); - - private: - DISALLOW_COPY_AND_ASSIGN(ExtensionsUI); + DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsHandler); }; #endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_EXTENSION_SETTINGS_HANDLER_H_ diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc index 98e7d8b..5eb1865 100644 --- a/chrome/browser/ui/webui/options/options_ui.cc +++ b/chrome/browser/ui/webui/options/options_ui.cc @@ -26,12 +26,14 @@ #include "chrome/browser/ui/webui/options/handler_options_handler.h" #include "chrome/browser/ui/webui/options/cookies_view_handler.h" #include "chrome/browser/ui/webui/options/core_options_handler.h" +#include "chrome/browser/ui/webui/options/extension_settings_handler.h" #include "chrome/browser/ui/webui/options/font_settings_handler.h" #include "chrome/browser/ui/webui/options/import_data_handler.h" #include "chrome/browser/ui/webui/options/intents_settings_handler.h" #include "chrome/browser/ui/webui/options/language_options_handler.h" #include "chrome/browser/ui/webui/options/manage_profile_handler.h" #include "chrome/browser/ui/webui/options/options_sync_setup_handler.h" +#include "chrome/browser/ui/webui/options/pack_extension_handler.h" #include "chrome/browser/ui/webui/options/password_manager_handler.h" #include "chrome/browser/ui/webui/options/personal_options_handler.h" #include "chrome/browser/ui/webui/options/search_engine_manager_handler.h" @@ -210,6 +212,7 @@ OptionsUI::OptionsUI(TabContents* contents) AddOptionsPageUIHandler(localized_strings, new ClearBrowserDataHandler()); AddOptionsPageUIHandler(localized_strings, new ContentSettingsHandler()); AddOptionsPageUIHandler(localized_strings, new CookiesViewHandler()); + AddOptionsPageUIHandler(localized_strings, new ExtensionSettingsHandler()); AddOptionsPageUIHandler(localized_strings, new FontSettingsHandler()); AddOptionsPageUIHandler(localized_strings, new IntentsSettingsHandler()); #if defined(OS_CHROMEOS) @@ -219,6 +222,7 @@ OptionsUI::OptionsUI(TabContents* contents) AddOptionsPageUIHandler(localized_strings, new LanguageOptionsHandler()); #endif AddOptionsPageUIHandler(localized_strings, new ManageProfileHandler()); + AddOptionsPageUIHandler(localized_strings, new PackExtensionHandler()); AddOptionsPageUIHandler(localized_strings, new PasswordManagerHandler()); AddOptionsPageUIHandler(localized_strings, new PersonalOptionsHandler()); AddOptionsPageUIHandler(localized_strings, new SearchEngineManagerHandler()); diff --git a/chrome/browser/ui/webui/options/options_ui_uitest.cc b/chrome/browser/ui/webui/options/options_ui_uitest.cc index 0330062..a1de2ea 100644 --- a/chrome/browser/ui/webui/options/options_ui_uitest.cc +++ b/chrome/browser/ui/webui/options/options_ui_uitest.cc @@ -149,14 +149,15 @@ TEST_F(OptionsUITest, NavBarCheck) { ASSERT_EQ(true, navbar_exist); // Check section headers in navbar. - // For ChromeOS, there should be 1 + 6: - // search, basics, personal, systerm, internet, under the hood and users - // For other platforms, there should 1 + 3: - // search, basics, personal and under the hood. + // For ChromeOS, there should be 1 + 7: + // Search, Basics, Personal, System, Internet, Under the Hood, + // Users and Extensions. + // For other platforms, there should 1 + 4: + // Search, Basics, Personal, Under the Hood and Extensions. #if defined(OS_CHROMEOS) - const int kExpectedSections = 1 + 6; + const int kExpectedSections = 1 + 7; #else - const int kExpectedSections = 1 + 3; + const int kExpectedSections = 1 + 4; #endif int num_of_sections = 0; ASSERT_TRUE(tab->ExecuteAndExtractInt(L"", diff --git a/chrome/browser/ui/webui/options/pack_extension_handler.cc b/chrome/browser/ui/webui/options/pack_extension_handler.cc new file mode 100644 index 0000000..40a656d6 --- /dev/null +++ b/chrome/browser/ui/webui/options/pack_extension_handler.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/options/pack_extension_handler.h" + +#include "base/utf_string_conversions.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +PackExtensionHandler::PackExtensionHandler() { +} + +PackExtensionHandler::~PackExtensionHandler() { + if (pack_job_.get()) + pack_job_->ClearClient(); +} + +void PackExtensionHandler::Initialize() { +} + +void PackExtensionHandler::GetLocalizedValues( + DictionaryValue* localized_strings) { + DCHECK(localized_strings); + RegisterTitle(localized_strings, "clearBrowserDataOverlay", + IDS_CLEAR_BROWSING_DATA_TITLE); + + localized_strings->SetString("packExtensionOverlay", + l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE)); + localized_strings->SetString("packExtensionHeading", + l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING)); + localized_strings->SetString("packExtensionCommit", + l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_BUTTON)); + localized_strings->SetString("packExtensionRootDir", + l10n_util::GetStringUTF16( + IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL)); + localized_strings->SetString("packExtensionPrivateKey", + l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL)); + localized_strings->SetString("packExtensionBrowseButton", + l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE)); +} + +void PackExtensionHandler::RegisterMessages() { + // Setup handlers specific to this panel. + web_ui_->RegisterMessageCallback("pack", + NewCallback(this, &PackExtensionHandler::HandlePackMessage)); +} + +void PackExtensionHandler::OnPackSuccess(const FilePath& crx_file, + const FilePath& pem_file) { + ListValue results; + web_ui_->CallJavascriptFunction("OptionsPage.closeOverlay", results); + + ShowAlert(UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(crx_file, + pem_file))); +} + +void PackExtensionHandler::OnPackFailure(const std::string& error) { + ShowAlert(error); +} + +void PackExtensionHandler::HandlePackMessage(const ListValue* args) { + std::string extension_path; + std::string private_key_path; + CHECK_EQ(2U, args->GetSize()); + CHECK(args->GetString(0, &extension_path)); + CHECK(args->GetString(1, &private_key_path)); + + FilePath root_directory = + FilePath::FromWStringHack(UTF8ToWide(extension_path)); + FilePath key_file = FilePath::FromWStringHack(UTF8ToWide(private_key_path)); + + if (root_directory.empty()) { + if (extension_path.empty()) { + ShowAlert(l10n_util::GetStringUTF8( + IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED)); + } else { + ShowAlert(l10n_util::GetStringUTF8( + IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID)); + } + + return; + } + + if (!private_key_path.empty() && key_file.empty()) { + ShowAlert(l10n_util::GetStringUTF8( + IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID)); + return; + } + + pack_job_ = new PackExtensionJob(this, root_directory, key_file); + pack_job_->Start(); +} + +void PackExtensionHandler::ShowAlert(const std::string& message) { + ListValue arguments; + arguments.Append(Value::CreateStringValue(message)); + web_ui_->CallJavascriptFunction("alert", arguments); +} diff --git a/chrome/browser/ui/webui/options/pack_extension_handler.h b/chrome/browser/ui/webui/options/pack_extension_handler.h new file mode 100644 index 0000000..1ef2d67 --- /dev/null +++ b/chrome/browser/ui/webui/options/pack_extension_handler.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_PACK_EXTENSION_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_OPTIONS_PACK_EXTENSION_HANDLER_H_ +#pragma once + +#include <string> + +#include "chrome/browser/browsing_data_remover.h" +#include "chrome/browser/extensions/pack_extension_job.h" +#include "chrome/browser/plugin_data_remover_helper.h" +#include "chrome/browser/ui/webui/options/options_ui.h" + +// Clear browser data handler page UI handler. +class PackExtensionHandler : public OptionsPageUIHandler, + public PackExtensionJob::Client { + public: + PackExtensionHandler(); + virtual ~PackExtensionHandler(); + + // OptionsPageUIHandler implementation. + virtual void Initialize() OVERRIDE; + virtual void GetLocalizedValues(DictionaryValue* localized_strings) OVERRIDE; + + // WebUIMessageHandler implementation. + virtual void RegisterMessages() OVERRIDE; + + // ExtensionPackJob::Client + virtual void OnPackSuccess(const FilePath& crx_file, + const FilePath& key_file) OVERRIDE; + + virtual void OnPackFailure(const std::string& error) OVERRIDE; + + private: + // Javascript callback to start packing an extension. + void HandlePackMessage(const ListValue* args); + + // A function to ask the webpage to show an alert. + void ShowAlert(const std::string& message); + + // Used to package the extension. + scoped_refptr<PackExtensionJob> pack_job_; + + DISALLOW_COPY_AND_ASSIGN(PackExtensionHandler); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_PACK_EXTENSION_HANDLER_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 15c04cd..f090a78 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3647,6 +3647,8 @@ 'browser/ui/webui/options/cookies_view_handler.h', 'browser/ui/webui/options/core_options_handler.cc', 'browser/ui/webui/options/core_options_handler.h', + 'browser/ui/webui/options/extension_settings_handler.h', + 'browser/ui/webui/options/extension_settings_handler.cc', 'browser/ui/webui/options/font_settings_handler.cc', 'browser/ui/webui/options/font_settings_handler.h', 'browser/ui/webui/options/font_settings_utils.h', @@ -3671,6 +3673,8 @@ 'browser/ui/webui/options/options_ui.h', 'browser/ui/webui/options/password_manager_handler.cc', 'browser/ui/webui/options/password_manager_handler.h', + 'browser/ui/webui/options/pack_extension_handler.h', + 'browser/ui/webui/options/pack_extension_handler.cc', 'browser/ui/webui/options/personal_options_handler.cc', 'browser/ui/webui/options/personal_options_handler.h', 'browser/ui/webui/options/search_engine_manager_handler.cc', |