diff options
Diffstat (limited to 'chromium/chrome/browser/resources/md_extensions')
18 files changed, 1091 insertions, 35 deletions
diff --git a/chromium/chrome/browser/resources/md_extensions/compiled_resources.gyp b/chromium/chrome/browser/resources/md_extensions/compiled_resources.gyp index 2b001ed20fb..868cae39a5e 100644 --- a/chromium/chrome/browser/resources/md_extensions/compiled_resources.gyp +++ b/chromium/chrome/browser/resources/md_extensions/compiled_resources.gyp @@ -7,12 +7,22 @@ 'target_name': 'manager', 'variables': { 'depends': [ - '../../../../ui/webui/resources/cr_elements/v1_0/cr_search_field/cr_search_field.js', + '../../../../ui/webui/resources/cr_elements/cr_search_field/cr_search_field.js', '../../../../ui/webui/resources/js/assert.js', + '../../../../ui/webui/resources/js/compiled_resources.gyp:load_time_data', '../../../../ui/webui/resources/js/cr.js', + '../../../../ui/webui/resources/js/i18n_behavior.js', + 'item.js', + 'item_list.js', + 'manager.js', + 'service.js', + 'sidebar.js', + 'toolbar.js', ], 'externs': [ '<(EXTERNS_DIR)/chrome_send.js', + '<(EXTERNS_DIR)/developer_private.js', + '<(EXTERNS_DIR)/management.js', ], }, 'includes': ['../../../../third_party/closure_compiler/compile_js.gypi'], diff --git a/chromium/chrome/browser/resources/md_extensions/icons.html b/chromium/chrome/browser/resources/md_extensions/icons.html new file mode 100644 index 00000000000..72b51e217d4 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/icons.html @@ -0,0 +1,21 @@ +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html"> + +<iron-iconset-svg name="extensions-icons" size="24"> + <svg> + <defs> + <g id="pack"> + <path d="M0 0h24v24H0z" fill="none"></path> + <path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2v2zm0 8h-2v-2h-2v-2h2v2h2v2z"></path> + </g> + <g id="update"> + <defs> + <path id="a" d="M0 0h24v24H0V0z"></path> + </defs> + <clipPath id="b"> + <use xlink:href="#a" overflow="visible"></use> + </clipPath> + <path d="M7.3 5.3c-1.3 1.3-2 3-2 4.7 0 1.7.7 3.4 2 4.7l1.2-1.2c-1-1-1.5-2.3-1.5-3.5 0-1.3.5-2.6 1.5-3.5L7.3 5.3zm11.8-2.4l-1.2 1.2c1.6 1.6 2.4 3.8 2.4 5.9s-.8 4.3-2.4 5.9l1.2 1.2c2-2 2.9-4.5 2.9-7.1s-1-5.1-2.9-7.1zm-13 1.2L4.9 2.9C3 4.9 2 7.4 2 10s1 5.1 2.9 7.1l1.2-1.2c-1.6-1.6-2.4-3.8-2.4-5.9s.8-4.3 2.4-5.9zm10.6 1.2l-1.2 1.2c1 1 1.5 2.3 1.5 3.5 0 1.3-.5 2.6-1.5 3.5l1.2 1.2c1.3-1.3 2-3 2-4.7-.1-1.7-.7-3.4-2-4.7zM12 7.5c-1.4 0-2.5 1.1-2.5 2.5 0 1 .6 1.9 1.5 2.3V21h2v-8.7c.9-.4 1.5-1.3 1.5-2.3 0-1.4-1.1-2.5-2.5-2.5z" clip-path="url(#b)"></path> + </g> + </svg> +</iron-icon-set> diff --git a/chromium/chrome/browser/resources/md_extensions/item.css b/chromium/chrome/browser/resources/md_extensions/item.css new file mode 100644 index 00000000000..447cd33833b --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item.css @@ -0,0 +1,113 @@ +/* Copyright 2015 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. */ + +.content-section { + padding: 20px; +} + +#icon-wrapper { + align-self: center; + display: flex; + justify-content: center; + margin: 0 24px; +} + +#icon { + height: 32px; + width: 32px; +} + +/* TODO(devlin): Combine this with downloads style. */ +#card-wrapper { + background: white; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .05), 0 1px 4px 0 rgba(0, 0, 0, .08), + 0 3px 1px -2px rgba(0, 0, 0, .2); + color: #5A5A5A; + min-height: 103px; + width: 622px; +} + +#card { + display: flex; +} + +#card.disabled { + opacity: 0.6; +} + +#content { + -webkit-border-start: 1px solid #C5C5C5; + display: flex; + flex: 1; + flex-direction: column; +} + +#name { + color: #222; + font-weight: 500; + margin-bottom: 8px; +} + +#controls { + padding-top: 20px; +} + +#version { + -webkit-margin-start: 50px; + flex: 1; +} + +.checkbox { + --paper-checkbox-checked-color: rgb(66, 133, 244); + --primary-text-color: #5A5A5A; +} + +#details-container { + border-top: 1px solid #C5C5C5; + color: #5A5A5A; +} + +#extension-id { + padding-bottom: 10px; +} + +#inspect-views { + padding-bottom: 20px; +} + +#inspect-views paper-button { + color: rgb(51, 103, 214); +} + +paper-button { + /* TODO(devlin): Decide whether all webui should do this. */ + cursor: pointer; +} + +#warnings-container { + border-top: 1px solid #C5C5C5; + display: flex; + padding: 10px; +} + +#warnings-container div { + color: #333; +} + +#warnings-container iron-icon { + -webkit-margin-end: 10px; + flex-shrink: 0; +} + +#warnings-container.severe iron-icon { + color: rgb(219, 68, 55); +} + +#warnings-container.mild iron-icon { + color: rgb(240, 147, 0); +} + +#blacklisted-warning:empty { + display: none; +} diff --git a/chromium/chrome/browser/resources/md_extensions/item.html b/chromium/chrome/browser/resources/md_extensions/item.html new file mode 100644 index 00000000000..69d8cbcf173 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item.html @@ -0,0 +1,86 @@ +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/load_time_data.html"> +<link rel="import" href="chrome://extensions/strings.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> + +<dom-module id="extensions-item"> + <template> + <div id="card-wrapper" class$="[[computeClasses_(data.state)]]"> + <div id="card"> + <div id="icon-wrapper"> + <img alt="" id="icon" src="[[data.iconUrl]]"> + </div> + <div id="content"> + <div class="content-section"> + <div class="layout horizontal justified center"> + <div id="name">[[data.name]]</div> + <paper-icon-button icon="[[computeExpandIcon_(showingDetails_)]]" + id="show-details" on-tap="onShowDetailsTap_"> + </paper-icon-button> + </div> + <div id="description">[[data.description]]</div> + <div id="controls" class="layout horizontal center"> + <paper-checkbox id="enabled" class="checkbox" + checked="[[isEnabled_(data.state)]]" + on-change="onEnableChange_"> + [[computeEnableCheckboxLabel_(data.state)]] + </paper-checkbox> + <span id="version">[[data.version]]</span> + <paper-icon-button icon="delete" id="delete-button" + on-tap="onDeleteTap_"></paper-icon-button> + </div> + </div> + <template is="dom-if" if="[[showingDetails_]]"> + <div id="details-container" class="content-section"> + <template is="dom-if" if="[[inDevMode]]"> + <div id="extension-id"></div> + <div id="inspect-views"> + <span>[[i18n('itemInspectViews')]]</span> + <template is="dom-repeat" items="[[data.views]]"> + <paper-button on-tap="onInspectTap_"> + [[computeInspectLabel_(item)]] + </paper-button> + </template> + </div> + </template> + <div class="layout horizontal justified center"> + <paper-checkbox id="allow-incognito" class="checkbox" + checked="[[data.incognitoAccess.isActive]]" + on-change="onAllowIncognitoChange_"> + [[i18n('itemAllowIncognito')]] + </paper-checkbox> + <paper-button id="details-button" on-tap="onDetailsTap_"> + [[i18n('itemDetails')]] + </paper-button> + </div> + </div> + </template> + </div> + </div> + <template is="dom-if" if="[[hasWarnings_(data.*)]]"> + <div id="warnings-container" + class$="[[computeWarningsClasses_(data.blacklistText)]]"> + <iron-icon icon="warning"></iron-icon> + <div> + <div id="corrupted-warning" + hidden="[[!data.disableReasons.corruptInstall]]"> + [[i18n('itemCorruptInstall')]] + </div> + <div id="suspicious-warning" + hidden="[[!data.disableReasons.suspiciousInstall]]"> + [[i18n('itemSuspiciousInstall')]] + </div> + <div id="blacklisted-warning">[[data.blacklistText]]</div> + </div> + </div> + </template> + </div> + </template> + <link rel="import" type="css" href="chrome://extensions/item.css"> + <script src="chrome://extensions/item.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/item.js b/chromium/chrome/browser/resources/md_extensions/item.js new file mode 100644 index 00000000000..9c0ead59b2e --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item.js @@ -0,0 +1,194 @@ +// Copyright 2015 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('extensions', function() { + /** @interface */ + var ItemDelegate = function() {}; + + ItemDelegate.prototype = { + /** @param {string} id */ + deleteItem: assertNotReached, + + /** + * @param {string} id + * @param {boolean} isEnabled + */ + setItemEnabled: assertNotReached, + + /** @param {string} id */ + showItemDetails: assertNotReached, + + /** + * @param {string} id + * @param {boolean} isAllowedIncognito + */ + setItemAllowedIncognito: assertNotReached, + + /** + * @param {string} id, + * @param {chrome.developerPrivate.ExtensionView} view + */ + inspectItemView: assertNotReached, + }; + + var Item = Polymer({ + is: 'extensions-item', + + properties: { + // The item's delegate, or null. + delegate: { + type: Object, + }, + + // Whether or not dev mode is enabled. + inDevMode: { + type: Boolean, + value: false, + }, + + // Whether or not the expanded view of the item is shown. + showingDetails_: { + type: Boolean, + value: false, + }, + + // The underlying ExtensionInfo itself. Public for use in declarative + // bindings. + /** @type {chrome.developerPrivate.ExtensionInfo} */ + data: { + type: Object, + }, + }, + + behaviors: [ + I18nBehavior, + ], + + observers: [ + 'observeIdVisibility_(inDevMode, showingDetails_, data.id)', + ], + + /** @private */ + observeIdVisibility_: function(inDevMode, showingDetails, id) { + Polymer.dom.flush(); + var idElement = this.$$('#extension-id'); + if (idElement) { + assert(this.data); + idElement.innerHTML = this.i18n('itemId', this.data.id); + } + this.fire('extension-item-size-changed', {item: this.data}); + }, + + /** @private */ + onShowDetailsTap_: function() { + this.showingDetails_ = !this.showingDetails_; + }, + + /** @private */ + onDeleteTap_: function() { + this.delegate.deleteItem(this.data.id); + }, + + /** @private */ + onEnableChange_: function() { + this.delegate.setItemEnabled(this.data.id, this.$.enabled.checked); + }, + + /** @private */ + onDetailsTap_: function() { + this.delegate.showItemDetails(this.data.id); + }, + + /** @private */ + onAllowIncognitoChange_: function() { + this.delegate.setItemAllowedIncognito( + this.data.id, this.$$('#allow-incognito').checked); + }, + + /** + * @param {!{model: !{item: !chrome.developerPrivate.ExtensionView}}} e + * @private + */ + onInspectTap_: function(e) { + this.delegate.inspectItemView(this.data.id, e.model.item); + }, + + /** + * Returns true if the extension is enabled, including terminated + * extensions. + * @return {boolean} + * @private + */ + isEnabled_: function() { + switch (this.data.state) { + case chrome.developerPrivate.ExtensionState.ENABLED: + case chrome.developerPrivate.ExtensionState.TERMINATED: + return true; + case chrome.developerPrivate.ExtensionState.DISABLED: + return false; + } + assertNotReached(); // FileNotFound. + }, + + /** @private */ + computeClasses_: function() { + return this.isEnabled_() ? 'enabled' : 'disabled'; + }, + + /** @private */ + computeExpandIcon_: function() { + return this.showingDetails_ ? 'expand-less' : 'expand-more'; + }, + + /** @private */ + computeEnableCheckboxLabel_: function() { + return this.i18n(this.isEnabled_() ? 'itemEnabled' : 'itemDisabled'); + }, + + /** + * @param {chrome.developerPrivate.ExtensionView} view + * @suppress {checkTypes} Needed for URL externs. :( + * @private + */ + computeInspectLabel_: function(view) { + // Trim the "chrome-extension://<id>/". + var url = new URL(view.url); + var label = view.url; + if (url.protocol == 'chrome-extension:') + label = url.pathname.substring(1); + if (label == '_generated_background_page.html') + label = this.i18n('viewBackgroundPage'); + // Add any qualifiers. + label += (view.incognito ? ' ' + this.i18n('viewIncognito') : '') + + (view.renderProcessId == -1 ? + ' ' + this.i18n('viewInactive') : '') + + (view.isIframe ? ' ' + this.i18n('viewIframe') : ''); + return label; + }, + + /** + * @return {boolean} + * @private + */ + hasWarnings_: function() { + return this.data.disableReasons.corruptInstall || + this.data.disableReasons.suspiciousInstall || + !!this.data.blacklistText; + }, + + /** + * @return {string} + * @private + */ + computeWarningsClasses_: function() { + return this.data.blacklistText ? 'severe' : 'mild'; + }, + }); + + return { + Item: Item, + ItemDelegate: ItemDelegate, + }; +}); + diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.css b/chromium/chrome/browser/resources/md_extensions/item_list.css new file mode 100644 index 00000000000..9163291f92b --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item_list.css @@ -0,0 +1,15 @@ +/* Copyright 2015 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. */ + +h2 { + color: #5a5a5a; + font-size: 1.1em; + font-weight: normal; + margin-bottom: 0; + margin-top: 30px; +} + +.wrapper { + padding: 10px 0; +} diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.html b/chromium/chrome/browser/resources/md_extensions/item_list.html new file mode 100644 index 00000000000..18a2b35efcc --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item_list.html @@ -0,0 +1,22 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html"> +<link rel="import" href="chrome://extensions/item.html"> + +<dom-module id="extensions-item-list"> + <template> + <h2>[[header]]</h2> + <iron-list id="list" items="[[computeShownItems_(items.*, filter)]]" + as="item"> + <template> + <div class="wrapper"> + <extensions-item data="[[item]]" delegate="[[delegate]]" + id="[[item.id]]" in-dev-mode="[[inDevMode]]"> + </extensions-item> + </div> + </template> + </iron-list> + </template> + <link rel="import" type="css" href="chrome://extensions/item_list.css"> + <script src="chrome://extensions/item_list.js"></script> +</dom-module> diff --git a/chromium/chrome/browser/resources/md_extensions/item_list.js b/chromium/chrome/browser/resources/md_extensions/item_list.js new file mode 100644 index 00000000000..2e6d9c3257d --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/item_list.js @@ -0,0 +1,57 @@ +// Copyright 2015 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('extensions', function() { + var ItemList = Polymer({ + is: 'extensions-item-list', + + properties: { + /** @type {Array<!chrome.developerPrivate.ExtensionInfo>} */ + items: Array, + + /** @type {extensions.ItemDelegate} */ + delegate: Object, + + header: String, + + inDevMode: { + type: Boolean, + value: false, + }, + + filter: String, + }, + + listeners: { + 'list.extension-item-size-changed': 'itemSizeChanged_', + }, + + /** + * Updates the size for a given item. + * @param {CustomEvent} e + * @private + * @suppress {checkTypes} Closure doesn't know $.list is an IronList. + */ + itemSizeChanged_: function(e) { + this.$.list.updateSizeForItem(e.detail.item); + }, + + /** + * Computes the list of items to be shown. + * @param {Object} changeRecord The changeRecord for |items|. + * @param {string} filter The updated filter string. + * @return {Array<!chrome.developerPrivate.ExtensionInfo>} + * @private + */ + computeShownItems_: function(changeRecord, filter) { + return this.items.filter(function(item) { + return item.name.toLowerCase().includes(this.filter.toLowerCase()); + }, this); + }, + }); + + return { + ItemList: ItemList, + }; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/manager.css b/chromium/chrome/browser/resources/md_extensions/manager.css index 999634de029..cef4d23721d 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.css +++ b/chromium/chrome/browser/resources/md_extensions/manager.css @@ -11,3 +11,14 @@ display: flex; }; } + +#items { + -webkit-margin-start: 30px; + overflow-y: auto; + padding-bottom: 30px; + width: 100%; +} + +extensions-item { + display: inline-block; +} diff --git a/chromium/chrome/browser/resources/md_extensions/manager.html b/chromium/chrome/browser/resources/md_extensions/manager.html index 504de93f902..e5d30a16f98 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.html +++ b/chromium/chrome/browser/resources/md_extensions/manager.html @@ -1,6 +1,9 @@ <link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-header-panel/paper-header-panel.html"> +<link rel="import" href="chrome://extensions/item_list.html"> +<link rel="import" href="chrome://extensions/service.html"> <link rel="import" href="chrome://extensions/sidebar.html"> <link rel="import" href="chrome://extensions/toolbar.html"> @@ -9,7 +12,28 @@ <paper-header-panel id="panel"> <extensions-toolbar class="paper-header" id="toolbar"> </extensions-toolbar> - <extensions-sidebar></extensions-sidebar> + <extensions-sidebar in-dev-mode="[[inDevMode]]" + hide-extensions-button="[[computeListHidden_(extensions, extensions.splices)]]" + hide-apps-button="[[computeListHidden_(apps, apps.splices)]]" + hide-websites-button="[[computeListHidden_(websites, websites.splices)]]"> + </extensions-sidebar> + <div id="items"> + <extensions-item-list id="extensions-list" items="[[extensions]]" + delegate="[[service]]" header="[[i18n('sidebarExtensions')]]" + in-dev-mode="[[inDevMode]]" filter="[[filter]]" + hidden$="[[computeListHidden_(extensions, extensions.splices)]]"> + </extensions-item-list> + <extensions-item-list id="apps-list" items="[[apps]]" + delegate="[[service]]" header="[[i18n('sidebarApps')]]" + in-dev-mode="[[inDevMode]]" filter="[[filter]]" + hidden$="[[computeListHidden_(apps, apps.splices)]]"> + </extensions-item-list> + <extensions-item-list id="websites-list" items="[[websites]]" + delegate="[[service]]" header="[[i18n('sidebarWebsites')]]" + in-dev-mode="[[inDevMode]]" filter="[[filter]]" + hidden$="[[computeListHidden_(websites, websites.splices)]]"> + </extensions-item-list> + </div> </paper-header-panel> </template> <link rel="import" type="css" href="chrome://extensions/manager.css"> diff --git a/chromium/chrome/browser/resources/md_extensions/manager.js b/chromium/chrome/browser/resources/md_extensions/manager.js index 729ee73338e..60c33b48dc5 100644 --- a/chromium/chrome/browser/resources/md_extensions/manager.js +++ b/chromium/chrome/browser/resources/md_extensions/manager.js @@ -3,9 +3,222 @@ // found in the LICENSE file. cr.define('extensions', function() { + 'use strict'; + + /** + * Compares two extensions to determine which should come first in the list. + * @param {chrome.developerPrivate.ExtensionInfo} a + * @param {chrome.developerPrivate.ExtensionInfo} b + * @return {number} + */ + var compareExtensions = function(a, b) { + function compare(x, y) { + return x < y ? -1 : (x > y ? 1 : 0); + } + function compareLocation(x, y) { + if (x.location == y.location) + return 0; + if (x.location == chrome.developerPrivate.Location.UNPACKED) + return -1; + if (y.location == chrome.developerPrivate.Location.UNPACKED) + return 1; + return 0; + } + return compareLocation(a, b) || + compare(a.name.toLowerCase(), b.name.toLowerCase()) || + compare(a.id, b.id); + }; + var Manager = Polymer({ - is: 'extensions-manager' + is: 'extensions-manager', + + properties: { + /** @type {extensions.Sidebar} */ + sidebar: Object, + + /** @type {extensions.Service} */ + service: Object, + + inDevMode: { + type: Boolean, + value: false, + }, + + filter: { + type: String, + value: '', + }, + + /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + extensions: { + type: Array, + value: function() { return []; }, + }, + + /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + apps: { + type: Array, + value: function() { return []; }, + }, + + /** @type {!Array<!chrome.developerPrivate.ExtensionInfo>} */ + websites: { + type: Array, + value: function() { return []; }, + }, + }, + + behaviors: [ + I18nBehavior, + ], + + ready: function() { + /** @type {extensions.Sidebar} */ + this.sidebar = + /** @type {extensions.Sidebar} */(this.$$('extensions-sidebar')); + this.service = extensions.Service.getInstance(); + this.service.managerReady(this); + this.scrollHelper_ = new ScrollHelper(this); + this.sidebar.setScrollDelegate(this.scrollHelper_); + this.$.toolbar.setSearchDelegate(new SearchHelper(this)); + }, + + /** + * @param {chrome.developerPrivate.ExtensionType} type The type of item. + * @return {string} The ID of the list that the item belongs in. + * @private + */ + getListId_: function(type) { + var listId; + var ExtensionType = chrome.developerPrivate.ExtensionType; + switch (type) { + case ExtensionType.HOSTED_APP: + case ExtensionType.LEGACY_PACKAGED_APP: + listId = 'websites'; + break; + case ExtensionType.PLATFORM_APP: + listId = 'apps'; + break; + case ExtensionType.EXTENSION: + case ExtensionType.SHARED_MODULE: + listId = 'extensions'; + break; + case ExtensionType.THEME: + assertNotReached( + 'Don\'t send themes to the chrome://extensions page'); + break; + } + assert(listId); + return listId; + }, + + /** + * @param {string} listId The list to look for the item in. + * @param {string} itemId The id of the item to look for. + * @return {number} The index of the item in the list, or -1 if not found. + * @private + */ + getIndexInList_: function(listId, itemId) { + return this[listId].findIndex(function(item) { + return item.id == itemId; + }); + }, + + /** + * @param {!Array<!chrome.developerPrivate.ExtensionInfo>} list + * @return {boolean} Whether the list should be visible. + */ + computeListHidden_: function(list) { + return list.length == 0; + }, + + /** + * Creates and adds a new extensions-item element to the list, inserting it + * into its sorted position in the relevant section. + * @param {!chrome.developerPrivate.ExtensionInfo} item The extension + * the new element is representing. + */ + addItem: function(item) { + var listId = this.getListId_(item.type); + // We should never try and add an existing item. + assert(this.getIndexInList_(listId, item.id) == -1); + var insertBeforeChild = this[listId].findIndex(function(listEl) { + return compareExtensions(listEl, item) > 0; + }); + if (insertBeforeChild == -1) + insertBeforeChild = this[listId].length; + this.splice(listId, insertBeforeChild, 0, item); + }, + + /** + * @param {!chrome.developerPrivate.ExtensionInfo} item The data for the + * item to update. + */ + updateItem: function(item) { + var listId = this.getListId_(item.type); + var index = this.getIndexInList_(listId, item.id); + // We should never try and update a non-existent item. + assert(index >= 0); + this.set([listId, index], item); + }, + + /** + * @param {!chrome.developerPrivate.ExtensionInfo} item The data for the + * item to remove. + */ + removeItem: function(item) { + var listId = this.getListId_(item.type); + var index = this.getIndexInList_(listId, item.id); + // We should never try and remove a non-existent item. + assert(index >= 0); + this.splice(listId, index, 1); + }, }); + /** + * @param {extensions.Manager} manager + * @constructor + * @implements {extensions.SidebarScrollDelegate} + */ + function ScrollHelper(manager) { + this.items_ = manager.$.items; + } + + ScrollHelper.prototype = { + /** @override */ + scrollToExtensions: function() { + this.items_.scrollTop = + this.items_.querySelector('#extensions-list').offsetTop; + }, + + /** @override */ + scrollToApps: function() { + this.items_.scrollTop = + this.items_.querySelector('#apps-list').offsetTop; + }, + + /** @override */ + scrollToWebsites: function() { + this.items_.scrollTop = + this.items_.querySelector('#websites-list').offsetTop; + }, + }; + + /** + * @param {extensions.Manager} manager + * @constructor + * @implements {SearchFieldDelegate} + */ + function SearchHelper(manager) { + this.manager_ = manager; + } + + SearchHelper.prototype = { + /** @override */ + onSearchTermSearch: function(searchTerm) { + this.manager_.filter = searchTerm; + }, + }; + return {Manager: Manager}; }); diff --git a/chromium/chrome/browser/resources/md_extensions/service.html b/chromium/chrome/browser/resources/md_extensions/service.html new file mode 100644 index 00000000000..fe1be3facc3 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/service.html @@ -0,0 +1,5 @@ +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://extensions/service.html"> +<link rel="import" href="chrome://extensions/item.html"> +<script src="chrome://extensions/service.js"></script> diff --git a/chromium/chrome/browser/resources/md_extensions/service.js b/chromium/chrome/browser/resources/md_extensions/service.js new file mode 100644 index 00000000000..a0837206eb1 --- /dev/null +++ b/chromium/chrome/browser/resources/md_extensions/service.js @@ -0,0 +1,158 @@ +// Copyright 2015 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('extensions', function() { + 'use strict'; + + /** + * @constructor + * @implements {extensions.ItemDelegate} + * @implements {extensions.SidebarDelegate} + */ + function Service() {} + + Service.prototype = { + /** @private {boolean} */ + promptIsShowing_: false, + + /** @param {extensions.Manager} manager */ + managerReady: function(manager) { + /** @private {extensions.Manager} */ + this.manager_ = manager; + /** @private {extensions.Sidebar} */ + this.sidebar_ = manager.sidebar; + this.sidebar_.setDelegate(this); + chrome.developerPrivate.onProfileStateChanged.addListener( + this.onProfileStateChanged_.bind(this)); + chrome.developerPrivate.onItemStateChanged.addListener( + this.onItemStateChanged_.bind(this)); + chrome.developerPrivate.getExtensionsInfo( + {includeDisabled: true, includeTerminated: true}, + function(extensions) { + /** @private {Array<chrome.developerPrivate.ExtensionInfo>} */ + this.extensions_ = extensions; + for (let extension of extensions) + this.manager_.addItem(extension); + }.bind(this)); + chrome.developerPrivate.getProfileConfiguration( + this.onProfileStateChanged_.bind(this)); + }, + + /** + * @param {chrome.developerPrivate.ProfileInfo} profileInfo + * @private + */ + onProfileStateChanged_: function(profileInfo) { + /** @private {chrome.developerPrivate.ProfileInfo} */ + this.profileInfo_ = profileInfo; + this.manager_.set('inDevMode', profileInfo.inDeveloperMode); + }, + + /** + * @param {chrome.developerPrivate.EventData} eventData + * @private + */ + onItemStateChanged_: function(eventData) { + var currentIndex = this.extensions_.findIndex(function(extension) { + return extension.id == eventData.item_id; + }); + + var EventType = chrome.developerPrivate.EventType; + switch (eventData.event_type) { + case EventType.VIEW_REGISTERED: + case EventType.VIEW_UNREGISTERED: + case EventType.INSTALLED: + case EventType.LOADED: + case EventType.UNLOADED: + case EventType.ERROR_ADDED: + case EventType.ERRORS_REMOVED: + case EventType.PREFS_CHANGED: + // |extensionInfo| can be undefined in the case of an extension + // being unloaded right before uninstallation. There's nothing to do + // here. + if (!eventData.extensionInfo) + break; + + if (currentIndex >= 0) { + this.extensions_[currentIndex] = eventData.extensionInfo; + this.manager_.updateItem(eventData.extensionInfo); + } else { + this.extensions_.push(eventData.extensionInfo); + this.manager_.addItem(eventData.extensionInfo); + } + break; + case EventType.UNINSTALLED: + this.manager_.removeItem(this.extensions_[currentIndex]); + this.extensions_.splice(currentIndex, 1); + break; + default: + assertNotReached(); + } + }, + + /** @override */ + deleteItem: function(id) { + if (this.promptIsShowing_) + return; + this.promptIsShowing_ = true; + chrome.management.uninstall(id, {showConfirmDialog: true}, function() { + // The "last error" was almost certainly the user canceling the dialog. + // Do nothing. We only check it so we don't get noisy logs. + /** @suppress {suspiciousCode} */ + chrome.runtime.lastError; + this.promptIsShowing_ = false; + }.bind(this)); + }, + + /** @override */ + setItemEnabled: function(id, isEnabled) { + chrome.management.setEnabled(id, isEnabled); + }, + + /** @override */ + showItemDetails: function(id) {}, + + /** @override */ + setItemAllowedIncognito: function(id, isAllowedIncognito) { + chrome.developerPrivate.updateExtensionConfiguration({ + extensionId: id, + incognitoAccess: isAllowedIncognito, + }); + }, + + /** @override */ + inspectItemView: function(id, view) { + chrome.developerPrivate.openDevTools({ + extensionId: id, + renderProcessId: view.renderProcessId, + renderViewId: view.renderViewId, + incognito: view.incognito, + }); + }, + + /** @override */ + setProfileInDevMode: function(inDevMode) { + chrome.developerPrivate.updateProfileConfiguration( + {inDeveloperMode: inDevMode}); + }, + + /** @override */ + loadUnpacked: function() { + chrome.developerPrivate.loadUnpacked({failQuietly: true}); + }, + + /** @override */ + packExtension: function() { + }, + + /** @override */ + updateAllExtensions: function() { + chrome.developerPrivate.autoUpdate(); + }, + }; + + cr.addSingletonGetter(Service); + + return {Service: Service}; +}); diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.css b/chromium/chrome/browser/resources/md_extensions/sidebar.css index a7c4b14fac5..30618144e0b 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.css +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.css @@ -5,7 +5,7 @@ :host { -webkit-margin-start: 30px; margin-top: 30px; - position: fixed; + white-space: nowrap; } #section-menu { @@ -32,7 +32,7 @@ paper-item { -webkit-margin-start: 15px; } -#developer-mode { +#devtools-container { border-bottom: 1px solid #C5C5C5; border-top: 1px solid #C5C5C5; margin: 10px 0; @@ -43,3 +43,7 @@ paper-item { --primary-text-color: #5A5A5A; -webkit-margin-start: 10px; } + +#devtools-controls { + -webkit-margin-start: 20px; +} diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.html b/chromium/chrome/browser/resources/md_extensions/sidebar.html index 9bb9b02a211..a56a9e46de7 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.html +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.html @@ -1,29 +1,57 @@ <link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-item/paper-item.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-menu/paper-menu.html"> +<link rel="import" href="chrome://extensions/icons.html"> <dom-module id="extensions-sidebar"> <template> <paper-menu id="section-menu"> - <paper-item class="section-menu-item" id="sections-extensions"> + <paper-item class="section-menu-item" id="sections-extensions" + on-tap="onExtensionsTap_" hidden$="[[hideExtensionsButton]]"> <iron-icon icon="extension"></iron-icon> <span i18n-content="sidebarExtensions"></span> </paper-item> - <paper-item class="section-menu-item" id="sections-apps"> + <paper-item class="section-menu-item" id="sections-apps" + on-tap="onAppsTap_" hidden$="[[hideAppsButton]]"> <iron-icon icon="apps"></iron-icon> <span i18n-content="sidebarApps"></span> </paper-item> - <paper-item class="section-menu-item" id="sections-websites"> + <paper-item class="section-menu-item" id="sections-websites" + on-tap="onWebsitesTap_" hidden$="[[hideWebsitesButton]]"> <iron-icon icon="cloud"></iron-icon> <span i18n-content="sidebarWebsites"></span> </paper-item> </paper-menu> - <paper-item id="developer-mode"> - <span i18n-content="developerMode"></span> - <paper-checkbox id="developer-mode-checkbox"></paper-checkbox> - </paper-item> + <div id="devtools-container"> + <paper-item id="developer-mode"> + <span i18n-content="developerMode"></span> + <paper-checkbox id="developer-mode-checkbox" + checked="[[inDevMode]]" + on-change="onDevModeChange_"></paper-checkbox> + </paper-item> + <template is="dom-if" if="[[inDevMode]]"> + <div id="devtools-controls"> + <paper-item class="section-menu-item" id="load-unpacked" + on-tap="onLoadUnpackedTap_"> + <iron-icon icon="file-download"></iron-icon> + <span>[[i18n('sidebarLoadUnpacked')]]</span> + </paper-item> + <paper-item class="section-menu-item" id="pack-extensions" + on-tap="onPackTap_"> + <iron-icon icon="extensions-icons:pack"></iron-icon> + <span>[[i18n('sidebarPack')]]</span> + </paper-item> + <paper-item class="section-menu-item" id="update-now" + on-tap="onUpdateNowTap_"> + <iron-icon icon="extensions-icons:update"></iron-icon> + <span>[[i18n('sidebarUpdateNow')]]</span> + </paper-item> + </div> + </template> + </div> <paper-item id="more-extensions" i18n-content="getMoreExtensions"> </paper-item> <paper-item id="keyboard-shortcuts" i18n-content="keyboardShortcuts"> diff --git a/chromium/chrome/browser/resources/md_extensions/sidebar.js b/chromium/chrome/browser/resources/md_extensions/sidebar.js index 10dfdf60e1e..622c5ec3ada 100644 --- a/chromium/chrome/browser/resources/md_extensions/sidebar.js +++ b/chromium/chrome/browser/resources/md_extensions/sidebar.js @@ -3,10 +3,122 @@ // found in the LICENSE file. cr.define('extensions', function() { + /** @interface */ + var SidebarDelegate = function() {}; + + SidebarDelegate.prototype = { + /** + * Toggles whether or not the profile is in developer mode. + * @param {boolean} inDevMode + */ + setProfileInDevMode: assertNotReached, + + /** Opens the dialog to load unpacked extensions. */ + loadUnpacked: assertNotReached, + + /** Opens the dialog to pack an extension. */ + packExtension: assertNotReached, + + /** Updates all extensions. */ + updateAllExtensions: assertNotReached, + }; + + /** @interface */ + var SidebarScrollDelegate = function() {}; + + SidebarScrollDelegate.prototype = { + /** Scrolls to the extensions section. */ + scrollToExtensions: assertNotReached, + + /** Scrolls to the apps section. */ + scrollToApps: assertNotReached, + + /** Scrolls to the websites section. */ + scrollToWebsites: assertNotReached, + }; + var Sidebar = Polymer({ is: 'extensions-sidebar', + + properties: { + inDevMode: { + type: Boolean, + value: false, + }, + + hideExtensionsButton: { + type: Boolean, + value: false, + }, + + hideAppsButton: { + type: Boolean, + value: false, + }, + + hideWebsitesButton: { + type: Boolean, + value: false, + }, + }, + + behaviors: [ + I18nBehavior, + ], + + /** @param {extensions.SidebarDelegate} delegate */ + setDelegate: function(delegate) { + /** @private {extensions.SidebarDelegate} */ + this.delegate_ = delegate; + }, + + /** @param {extensions.SidebarScrollDelegate} scrollDelegate */ + setScrollDelegate: function(scrollDelegate) { + /** @private {extensions.SidebarScrollDelegate} */ + this.scrollDelegate_ = scrollDelegate; + }, + + /** @private */ + onExtensionsTap_: function() { + this.scrollDelegate_.scrollToExtensions(); + }, + + /** @private */ + onAppsTap_: function() { + this.scrollDelegate_.scrollToApps(); + }, + + /** @private */ + onWebsitesTap_: function() { + this.scrollDelegate_.scrollToWebsites(); + }, + + /** @private */ + onDevModeChange_: function() { + this.delegate_.setProfileInDevMode( + this.$['developer-mode-checkbox'].checked); + }, + + /** @private */ + onLoadUnpackedTap_: function() { + this.delegate_.loadUnpacked(); + }, + + /** @private */ + onPackTap_: function() { + this.delegate_.packExtension(); + }, + + /** @private */ + onUpdateNowTap_: function() { + this.delegate_.updateAllExtensions(); + }, }); - return {Sidebar: Sidebar}; + return { + Sidebar: Sidebar, + SidebarDelegate: SidebarDelegate, + SidebarScrollDelegate: SidebarScrollDelegate, + }; }); diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.html b/chromium/chrome/browser/resources/md_extensions/toolbar.html index f269d333385..8cc0aee2c8e 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.html +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.html @@ -1,6 +1,6 @@ -<link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> -<link rel="import" href="chrome://resources/cr_elements/v1_0/cr_search_field/cr_search_field.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_search_field/cr_search_field.html"> +<link rel="import" href="chrome://resources/html/cr.html"> <dom-module id="extensions-toolbar"> <template> diff --git a/chromium/chrome/browser/resources/md_extensions/toolbar.js b/chromium/chrome/browser/resources/md_extensions/toolbar.js index fade7d418ee..3da624cc6de 100644 --- a/chromium/chrome/browser/resources/md_extensions/toolbar.js +++ b/chromium/chrome/browser/resources/md_extensions/toolbar.js @@ -6,28 +6,11 @@ cr.define('extensions', function() { var Toolbar = Polymer({ is: 'extensions-toolbar', - /** @param {string} searchTerm */ - onSearchTermSearch: function(searchTerm) { + /** @param {SearchFieldDelegate} delegate */ + setSearchDelegate: function(delegate) { + this.$['search-field'].setDelegate(delegate); }, }); - /** - * @constructor - * @implements {SearchFieldDelegate} - */ - // TODO(devlin): This is a bit excessive, and it would be better to just have - // Toolbar implement SearchFieldDelegate. But for now, we don't know how to - // make that happen with closure compiler. - function ToolbarSearchFieldDelegate(toolbar) { - this.toolbar_ = toolbar; - } - - ToolbarSearchFieldDelegate.prototype = { - /** @override */ - onSearchTermSearch: function(searchTerm) { - this.toolbar_.onSearchTermSearch(searchTerm); - } - }; - return {Toolbar: Toolbar}; }); |