blob: 722d78de00f62faae50218d626181f0a3047535a [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
import 'chrome://resources/cr_elements/cr_tooltip/cr_tooltip.js';
import 'chrome://resources/cr_elements/icons.html.js';
import 'chrome://resources/cr_elements/policy/cr_tooltip_icon.js';
import 'chrome://resources/js/action_link.js';
import 'chrome://resources/cr_elements/cr_icon/cr_icon.js';
import './host_permissions_toggle_list.js';
import './icons.html.js';
import './runtime_host_permissions.js';
import '/strings.m.js';
import './toggle_row.js';
import {AnchorAlignment} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import type {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js';
import type {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js';
import type {CrTooltipIconElement} from 'chrome://resources/cr_elements/policy/cr_tooltip_icon.js';
import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
import {focusWithoutInk} from 'chrome://resources/js/focus_without_ink.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import {getCss} from './detail_view.css.js';
import {getHtml} from './detail_view.html.js';
import type {ItemDelegate} from './item.js';
import {DummyItemDelegate} from './item.js';
import {ItemMixin} from './item_mixin.js';
import {computeInspectableViewLabel, convertSafetyCheckReason, createDummyExtensionInfo, EnableControl, getEnableControl, getEnableToggleAriaLabel, getEnableToggleTooltipText, getItemSource, getItemSourceString, isEnabled, SAFETY_HUB_EXTENSION_KEPT_HISTOGRAM_NAME, SAFETY_HUB_EXTENSION_REMOVED_HISTOGRAM_NAME, SAFETY_HUB_WARNING_REASON_MAX_SIZE, sortViews, UPLOAD_EXTENSION_TO_ACCOUNT_DETAILS_VIEW_PAGE_HISTOGRAM_NAME, userCanChangeEnablement} from './item_util.js';
import type {Mv2DeprecationDelegate} from './mv2_deprecation_delegate.js';
import {getMv2ExperimentStage, Mv2ExperimentStage} from './mv2_deprecation_util.js';
import {navigation, Page} from './navigation_helper.js';
import type {ExtensionsToggleRowElement} from './toggle_row.js';
class DummyDetailViewDelegate extends DummyItemDelegate {
dismissMv2DeprecationNotice() {}
dismissMv2DeprecationNoticeForExtension(_id: string) {}
}
export interface ExtensionsDetailViewElement {
$: {
actionMenu: CrActionMenuElement,
closeButton: HTMLElement,
description: HTMLElement,
enableToggle: CrToggleElement,
extensionsActivityLogLink: HTMLElement,
extensionsOptions: CrLinkRowElement,
parentDisabledPermissionsToolTip: CrTooltipIconElement,
safetyCheckWarningContainer: HTMLElement,
source: HTMLElement,
};
}
const ExtensionsDetailViewElementBase = I18nMixinLit(ItemMixin(CrLitElement));
export class ExtensionsDetailViewElement extends
ExtensionsDetailViewElementBase {
static get is() {
return 'extensions-detail-view';
}
static override get styles() {
return getCss();
}
override render() {
return getHtml.bind(this)();
}
static override get properties() {
return {
/**
* The underlying ExtensionInfo for the details being displayed.
*/
data: {type: Object},
size_: {type: String},
delegate: {type: Object},
/** Whether the user has enabled the UI's developer mode. */
inDevMode: {type: Boolean},
/**
* Whether enhanced site controls have been enabled (through a feature
* flag). For this page, there are some changes to the site permissions
* section.
*/
enableEnhancedSiteControls: {type: Boolean},
/** Whether "allow in incognito" option should be shown. */
incognitoAvailable: {type: Boolean},
/** Whether "View Activity Log" link should be shown. */
showActivityLog: {type: Boolean},
/** Whether the user navigated to this page from the activity log page. */
fromActivityLog: {type: Boolean},
/** Inspectable views sorted to put background/service workers first */
sortedViews_: {type: Array},
/** Whether the extensions safety check warning is shown. */
showSafetyCheck_: {type: Boolean},
/**
* Current Manifest V2 experiment stage.
*/
mv2ExperimentStage_: {
type: Number,
state: true,
},
};
}
accessor data: chrome.developerPrivate.ExtensionInfo =
createDummyExtensionInfo();
accessor delegate: ItemDelegate&Mv2DeprecationDelegate =
new DummyDetailViewDelegate();
accessor inDevMode: boolean = false;
accessor enableEnhancedSiteControls: boolean = false;
accessor incognitoAvailable: boolean = false;
accessor showActivityLog: boolean = false;
accessor fromActivityLog: boolean = false;
protected accessor showSafetyCheck_: boolean = false;
protected accessor size_: string = '';
protected accessor sortedViews_: chrome.developerPrivate.ExtensionView[] = [];
private accessor mv2ExperimentStage_: Mv2ExperimentStage =
getMv2ExperimentStage(loadTimeData.getInteger('MV2ExperimentStage'));
override firstUpdated() {
this.addEventListener('view-enter-start', this.onViewEnterStart_);
}
override willUpdate(changedProperties: PropertyValues<this>) {
super.willUpdate(changedProperties);
if (changedProperties.has('data')) {
this.sortedViews_ = sortViews(this.data.views);
this.showSafetyCheck_ = this.computeShowSafetyCheck_();
}
if (changedProperties.has('data') || changedProperties.has('delegate')) {
this.onItemIdChanged_();
}
}
override updated(changedProperties: PropertyValues<this>) {
super.updated(changedProperties);
const changedPrivateProperties =
changedProperties as Map<PropertyKey, unknown>;
if (changedPrivateProperties.has('showSafetyCheck_') &&
this.showSafetyCheck_) {
chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailWarningShown');
}
}
/**
* Focuses the extensions options button. This should be used after the
* dialog closes.
*/
focusOptionsButton() {
this.$.extensionsOptions.focus();
}
/**
* Focuses the back button when page is loaded.
*/
private async onViewEnterStart_() {
const elementToFocus = this.fromActivityLog ?
this.$.extensionsActivityLogLink :
this.$.closeButton;
await this.updateComplete;
focusWithoutInk(elementToFocus);
}
private onItemIdChanged_() {
// Clear the size, since this view is reused, such that no obsolete size
// is displayed.:
this.size_ = '';
this.delegate.getExtensionSize(this.data.id).then(size => {
this.size_ = size;
});
}
protected onActivityLogClick_() {
navigation.navigateTo({page: Page.ACTIVITY_LOG, extensionId: this.data.id});
}
protected getDescription_(): string {
return this.data.description || loadTimeData.getString('noDescription');
}
protected getBackButtonAriaLabel_(): string {
return loadTimeData.getStringF(
'itemDetailsBackButtonAriaLabel', this.data.name);
}
protected getBackButtonAriaRoleDescription_(): string {
return loadTimeData.getStringF(
'itemDetailsBackButtonRoleDescription', this.data.name);
}
protected getEnableToggleAriaLabel_(): string {
return getEnableToggleAriaLabel(
this.isEnabled_(), this.data.type, this.i18n('appEnabled'),
this.i18n('extensionEnabled'), this.i18n('itemOff'));
}
protected getEnableToggleTooltipText_(): string {
return getEnableToggleTooltipText(this.data);
}
protected onCloseButtonClick_() {
navigation.navigateTo({page: Page.LIST});
}
protected isEnabled_(): boolean {
return isEnabled(this.data.state);
}
protected isEnableToggleEnabled_(): boolean {
return userCanChangeEnablement(this.data, this.mv2ExperimentStage_);
}
protected hasDependentExtensions_(): boolean {
return this.data.dependentExtensions.length > 0;
}
protected hasSevereWarnings_(): boolean {
return this.data.disableReasons.corruptInstall ||
this.data.disableReasons.suspiciousInstall ||
this.data.disableReasons.publishedInStoreRequired ||
this.data.disableReasons.unsupportedDeveloperExtension ||
this.data.disableReasons.updateRequired || !!this.data.blocklistText ||
this.data.runtimeWarnings.length > 0;
}
protected showAccountUploadButton_(): boolean {
return this.data.canUploadAsAccountExtension;
}
protected showDevReloadButton_(): boolean {
return this.canReloadItem();
}
protected computeEnabledStyle_(): string {
return this.isEnabled_() ? 'enabled-text' : '';
}
protected computeEnabledText_(): string {
// TODO(devlin): Get the full spectrum of these strings from bettes.
return loadTimeData.getString(
isEnabled(this.data.state) ? 'itemOn' : 'itemOff');
}
protected computeInspectLabel_(view: chrome.developerPrivate.ExtensionView):
string {
return computeInspectableViewLabel(view);
}
protected shouldShowOptionsLink_(): boolean {
return !!this.data.optionsPage;
}
protected shouldShowOptionsSection_(): boolean {
return this.canPinToToolbar_() || this.data.incognitoAccess.isEnabled ||
this.data.fileAccess.isEnabled || this.data.errorCollection.isEnabled;
}
protected canPinToToolbar_(): boolean {
return this.data.pinnedToToolbar !== undefined;
}
protected shouldShowIncognitoOption_(): boolean {
return this.data.incognitoAccess.isEnabled && this.incognitoAvailable;
}
protected showUserScriptSectionToggle_(): boolean {
return this.data.userScriptsAccess.isEnabled;
}
protected onEnableToggleChange_() {
this.delegate.setItemEnabled(this.data.id, this.$.enableToggle.checked);
this.$.enableToggle.checked = this.isEnabled_();
}
protected onInspectClick_(e: Event) {
const index = Number((e.target as HTMLElement).dataset['index']);
this.delegate.inspectItemView(this.data.id, this.sortedViews_[index]!);
}
protected onExtensionOptionsClick_() {
this.delegate.showItemOptionsPage(this.data);
}
protected onReloadClick_() {
this.reloadItem().catch((loadError) => this.fire('load-error', loadError));
}
protected async onUploadClick_() {
const uploaded = await this.delegate.uploadItemToAccount(this.data.id);
chrome.metricsPrivate.recordBoolean(
UPLOAD_EXTENSION_TO_ACCOUNT_DETAILS_VIEW_PAGE_HISTOGRAM_NAME, uploaded);
}
protected onRemoveClick_() {
if (this.showSafetyCheck_) {
chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailRemoveClicked');
chrome.metricsPrivate.recordEnumerationValue(
SAFETY_HUB_EXTENSION_REMOVED_HISTOGRAM_NAME,
convertSafetyCheckReason(this.data.safetyCheckWarningReason),
SAFETY_HUB_WARNING_REASON_MAX_SIZE);
}
this.delegate.deleteItem(this.data.id);
}
protected onKeepClick_() {
if (this.showSafetyCheck_) {
chrome.metricsPrivate.recordUserAction('SafetyCheck.DetailKeepClicked');
chrome.metricsPrivate.recordEnumerationValue(
SAFETY_HUB_EXTENSION_KEPT_HISTOGRAM_NAME,
convertSafetyCheckReason(this.data.safetyCheckWarningReason),
SAFETY_HUB_WARNING_REASON_MAX_SIZE);
}
this.delegate.setItemSafetyCheckWarningAcknowledged(
this.data.id, this.data.safetyCheckWarningReason);
}
/**
* Opens a URL in the Web Store with extensions recommendations for the
* extension.
*/
protected onFindAlternativeButtonClick_(): void {
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.Warning.FindAlternativeForExtension.Entry');
const recommendationsUrl: string|undefined = this.data.recommendationsUrl;
assert(!!recommendationsUrl);
this.delegate.openUrl(recommendationsUrl);
}
/**
* Triggers the extension's removal.
*/
protected onRemoveButtonClick_(): void {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
assertNotReached();
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.DisableWithReEnable.Remove');
break;
case Mv2ExperimentStage.UNSUPPORTED:
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.Unsupported.RemoveExtension.DetailPage');
break;
}
this.delegate.deleteItem(this.data.id);
}
protected onRepairClick_() {
this.delegate.repairItem(this.data.id);
}
protected onLoadPathClick_() {
this.delegate.showInFolder(this.data.id);
}
protected onPinnedToToolbarChange_() {
this.delegate.setItemPinnedToToolbar(
this.data.id,
this.shadowRoot
.querySelector<ExtensionsToggleRowElement>(
'#pin-to-toolbar')!.checked);
}
protected onAllowIncognitoChange_() {
this.delegate.setItemAllowedIncognito(
this.data.id,
this.shadowRoot
.querySelector<ExtensionsToggleRowElement>(
'#allow-incognito')!.checked);
if (this.data.controlledInfo) {
// If admin-installed, the change might be postponed until Chromium
// restarts.
this.data = {
...this.data,
incognitoAccessPendingChange: !this.data.incognitoAccessPendingChange,
};
}
}
protected onAllowUserScriptsChange_() {
this.delegate.setItemAllowedUserScripts(
this.data.id,
this.shadowRoot
.querySelector<ExtensionsToggleRowElement>(
'#allow-user-scripts')!.checked);
}
protected onAllowOnFileUrlsChange_() {
this.delegate.setItemAllowedOnFileUrls(
this.data.id,
this.shadowRoot
.querySelector<ExtensionsToggleRowElement>(
'#allow-on-file-urls')!.checked);
if (this.data.controlledInfo) {
// If admin-installed, the change might be postponed until Chromium
// restarts.
this.data = {
...this.data,
fileAccessPendingChange: !this.data.fileAccessPendingChange,
};
}
}
protected onCollectErrorsChange_() {
this.delegate.setItemCollectsErrors(
this.data.id,
this.shadowRoot
.querySelector<ExtensionsToggleRowElement>(
'#collect-errors')!.checked);
}
protected onExtensionWebSiteClick_() {
this.delegate.openUrl(this.data.manifestHomePageUrl);
}
protected onSiteSettingsClick_() {
this.delegate.openUrl(
`chrome://settings/content/siteDetails?site=chrome-extension://${
this.data.id}`);
}
protected onViewInStoreClick_() {
this.delegate.openUrl(this.data.webStoreUrl);
}
protected computeDependentEntry_(
item: chrome.developerPrivate.DependentExtension): string {
return loadTimeData.getStringF('itemDependentEntry', item.name, item.id);
}
protected computeSourceString_(): string {
return this.data.locationText ||
getItemSourceString(getItemSource(this.data));
}
protected hasPermissions_(): boolean {
return this.data.permissions.simplePermissions.length > 0 ||
this.hasRuntimeHostPermissions_();
}
protected getNoPermissionsString_(): string {
const showPermissionsAndSiteAccessStrings =
this.enableEnhancedSiteControls && !this.showSiteAccessContent_();
return loadTimeData.getString(
showPermissionsAndSiteAccessStrings ?
'itemPermissionsAndSiteAccessEmpty' :
'itemPermissionsEmpty');
}
private hasRuntimeHostPermissions_(): boolean {
return !!this.data.permissions.runtimeHostPermissions;
}
// Returns whether the site access section should be shown. This includes the
// "no site access" message shown in the section if
// |enableEnhancedSiteControls| is not enabled.
protected showSiteAccessSection_(): boolean {
return !this.enableEnhancedSiteControls || this.showSiteAccessContent_();
}
protected showSiteAccessContent_(): boolean {
return this.showFreeformRuntimeHostPermissions_() ||
this.showHostPermissionsToggleList_();
}
protected showFreeformRuntimeHostPermissions_(): boolean {
return this.hasRuntimeHostPermissions_() &&
this.data.permissions.runtimeHostPermissions!.hasAllHosts;
}
protected showHostPermissionsToggleList_(): boolean {
return this.hasRuntimeHostPermissions_() &&
!this.data.permissions.runtimeHostPermissions!.hasAllHosts;
}
protected showEnableAccessRequestsToggle_(): boolean {
return this.showSiteAccessContent_() && this.enableEnhancedSiteControls;
}
protected onShowAccessRequestsChange_() {
const showAccessRequestsToggle =
this.shadowRoot.querySelector<ExtensionsToggleRowElement>(
'#show-access-requests-toggle');
assert(showAccessRequestsToggle);
this.delegate.setShowAccessRequestsInToolbar(
this.data.id, showAccessRequestsToggle.checked);
}
protected showReloadButton_(): boolean {
return getEnableControl(this.data) === EnableControl.RELOAD;
}
private computeShowSafetyCheck_(): boolean {
const ExtensionType = chrome.developerPrivate.ExtensionType;
// Check to make sure this is an extension and not a Chrome app.
if (!(this.data.type === ExtensionType.EXTENSION ||
this.data.type === ExtensionType.SHARED_MODULE)) {
return false;
}
return !!(
this.data.safetyCheckText && this.data.safetyCheckText.detailString);
}
/**
* Returns whether the mv2 deprecation message should be displayed.
*/
protected shouldShowMv2DeprecationMessage_(): boolean {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
return false;
case Mv2ExperimentStage.WARNING:
return this.data.isAffectedByMV2Deprecation;
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
return this.data.isAffectedByMV2Deprecation &&
this.data.disableReasons.unsupportedManifestVersion &&
!this.data.didAcknowledgeMV2DeprecationNotice;
case Mv2ExperimentStage.UNSUPPORTED:
return this.data.isAffectedByMV2Deprecation &&
this.data.disableReasons.unsupportedManifestVersion;
default:
assertNotReached();
}
}
/**
* Returns whether the find alternative button in the mv2 deprecation message
* should be displayed.
*/
protected shouldShowMv2DeprecationFindAlternativeButton_(): boolean {
return this.mv2ExperimentStage_ === Mv2ExperimentStage.WARNING &&
!!this.data.recommendationsUrl;
}
/**
* Returns whether the remove button in the mv2 deprecation message should be
* displayed.
*/
protected shouldShowMv2DeprecationRemoveButton_(): boolean {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
return false;
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
case Mv2ExperimentStage.UNSUPPORTED:
return !this.data.mustRemainInstalled;
}
}
/**
* Returns whether the action menu button in the mv2 deprecation message
* should be displayed.
*/
protected shouldShowMv2DeprecationActionMenu_(): boolean {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
return false;
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
return true;
case Mv2ExperimentStage.UNSUPPORTED:
// 'Find alternative' is the only action for this stage. Thus, we only
// show the menu if the action should be visible. For UNSUPPORTED, this
// is when the recommendationsUrl is non-empty.
return !!this.data.recommendationsUrl;
}
}
/**
* Returns whether the find alternative button in mv2 deprecation message
* action menu should be displayed.
*/
protected shouldShowMv2DeprecationFindAlternativeAction_(): boolean {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
return false;
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
case Mv2ExperimentStage.UNSUPPORTED:
return !!this.data.recommendationsUrl;
}
}
/**
* Returns whether the keep button in mv2 deprecation message action menu
* should be displayed.
*/
protected shouldShowMv2DeprecationKeepAction_(): boolean {
return this.mv2ExperimentStage_ ===
Mv2ExperimentStage.DISABLE_WITH_REENABLE;
}
protected shouldShowBlocklistText_(): boolean {
return !this.showSafetyCheck_ && !!this.data.blocklistText;
}
/**
* Shows only one text if both unsupported developer extension and safety
* check texts are present. Safety check text takes precedence.
*/
protected shouldShowUnsupportedDeveloperExtensionText_(): boolean {
return !this.showSafetyCheck_ &&
this.data.disableReasons.unsupportedDeveloperExtension;
}
protected showRepairButton_(): boolean {
return getEnableControl(this.data) === EnableControl.REPAIR;
}
protected showEnableToggle_(): boolean {
const enableControl = getEnableControl(this.data);
// We still show the toggle even if we also show the repair button in the
// detail view, because the repair button appears just beneath it.
return enableControl === EnableControl.ENABLE_TOGGLE ||
enableControl === EnableControl.REPAIR;
}
protected showAllowlistWarning_(): boolean {
// Only show the allowlist warning if there is no blocklist warning. It
// would be redundant since all blocklisted items are necessarily not
// included in the Safe Browsing allowlist.
return this.data.showSafeBrowsingAllowlistWarning &&
!this.data.blocklistText;
}
/** Opens the action menu for the extension. */
protected onActionMenuButtonClick_(event: MouseEvent): void {
this.$.actionMenu.showAt(
event.target as HTMLElement,
{anchorAlignmentY: AnchorAlignment.AFTER_END});
}
/**
* Opens a URL in the Web Store with extensions recommendations for the
* extension.
*/
protected onFindAlternativeActionClick_(): void {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
assertNotReached();
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.Disabled.FindAlternativeForExtension.DetailPage');
break;
case Mv2ExperimentStage.UNSUPPORTED:
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.Unsupported.FindAlternativeForExtension.DetailPage');
break;
}
this.$.actionMenu.close();
const recommendationsUrl: string|undefined = this.data.recommendationsUrl;
assert(!!recommendationsUrl);
this.delegate.openUrl(recommendationsUrl);
}
/**
* Dismisses the notice for a given extension in the disable experiment stage.
* It will not be shown again during this stage.
*/
protected onKeepActionClick_(): void {
assert(
this.mv2ExperimentStage_ === Mv2ExperimentStage.DISABLE_WITH_REENABLE);
chrome.metricsPrivate.recordUserAction(
'Extensions.Mv2Deprecation.Disabled.DismissedForExtension.DetailPage');
this.$.actionMenu.close();
this.delegate.dismissMv2DeprecationNoticeForExtension(this.data.id);
}
/**
* Returns the Manifest V2 deprecation message header.
*/
protected getMv2DeprecationMessageHeader_(): string {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
return '';
case Mv2ExperimentStage.WARNING:
return this.i18n('mv2DeprecationMessageWarningHeader');
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
case Mv2ExperimentStage.UNSUPPORTED:
return this.i18n('mv2DeprecationMessageDisabledHeader');
default:
assertNotReached();
}
}
/**
* Returns the HTML representation of the Manifest V2 deprecation message
* subtitle string. We need the HTML representation instead of the string
* since the string holds substitutions.
*/
protected getMv2DeprecationMessageSubtitle_(): TrustedHTML {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
return window.trustedTypes!.emptyHTML;
case Mv2ExperimentStage.WARNING:
return this.i18nAdvanced('mv2DeprecationMessageWarningSubtitle', {
substitutions: [
'https://chromewebstore.google.com/category/extensions',
this.i18n('opensInNewTab'),
],
attrs: ['aria-description'],
});
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
case Mv2ExperimentStage.UNSUPPORTED:
return this.i18nAdvanced('mv2DeprecationMessageDisabledSubtitle', {
substitutions: [
'https://support.google.com/chrome_webstore' +
'?p=unsupported_extensions',
this.i18n('opensInNewTab'),
],
attrs: ['aria-description'],
});
default:
assertNotReached();
}
}
/**
* Returns the Manifest V2 deprecation message icon.
*/
protected getMv2DeprecationMessageIcon_(): string {
switch (this.mv2ExperimentStage_) {
case Mv2ExperimentStage.NONE:
case Mv2ExperimentStage.WARNING:
return 'extensions-icons:my_extensions';
case Mv2ExperimentStage.DISABLE_WITH_REENABLE:
case Mv2ExperimentStage.UNSUPPORTED:
return 'extensions-icons:extension_off';
default:
assertNotReached();
}
}
/** Returns the accessible label for the action menu button */
protected getActionMenuButtonLabel_(): string {
return this.i18n(
'mv2DeprecationPanelExtensionActionMenuLabel', this.data.name);
}
}
declare global {
interface HTMLElementTagNameMap {
'extensions-detail-view': ExtensionsDetailViewElement;
}
}
customElements.define(
ExtensionsDetailViewElement.is, ExtensionsDetailViewElement);