blob: c5c06a9b0a5333ce8cfa8f2aa27c0ccdab55feee [file] [log] [blame]
// Copyright 2017 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.
ProductRegistry.BadgePool = class {
/**
* @param {boolean=} forceShow
*/
constructor(forceShow) {
this._showTitles = false;
/** @type {!Map<!Element, function():!Promise<!Common.ParsedURL>>} */
this._badgeElements = new Map();
if (!forceShow) {
this._setting = Common.settings.moduleSetting('product_registry.badges-visible');
this._setting.addChangeListener(this._settingUpdated.bind(this));
if (this._setting.get())
Host.userMetrics.actionTaken(Host.UserMetrics.Action.ShowedThirdPartyBadges);
}
}
/**
* @param {boolean} value
*/
setShowTitles(value) {
this._showTitles = value;
}
/**
* @param {!SDK.ResourceTreeFrame} frame
* @return {!Element}
*/
badgeForFrame(frame) {
return this._badgeForFrameOrUrl(this._resolveUrl.bind(this, frame));
}
/**
* @param {!Common.ParsedURL} parsedUrl
* @return {!Element}
*/
badgeForURL(parsedUrl) {
return this._badgeForFrameOrUrl(() => Promise.resolve(parsedUrl));
}
reset() {
this._badgeElements.clear();
}
/**
* @param {function():!Promise<!Common.ParsedURL>} urlResolver
* @return {!Element}
*/
_badgeForFrameOrUrl(urlResolver) {
const element = createElementWithClass('span', 'hidden');
const root = UI.createShadowRootWithCoreStyles(element, 'product_registry/badge.css');
const badgeElement = root.createChild('span');
badgeElement.classList.toggle('hide-badge-title', !this._showTitles);
badgeElement.addEventListener('mousedown', event => event.consume());
badgeElement.addEventListener('click', event => {
this._showPopup(badgeElement);
event.consume();
}, false);
this._badgeElements.set(badgeElement, urlResolver);
this._renderBadge(badgeElement);
return element;
}
/**
* @param {?SDK.ResourceTreeFrame} frame
* @return {!Promise<!Common.ParsedURL>}
*/
async _resolveUrl(frame) {
const registry = await ProductRegistry.instance();
let parsedUrl = new Common.ParsedURL(frame.url);
const entry = registry.entryForUrl(parsedUrl);
if (!entry) {
frame.findCreationCallFrame(callFrame => {
if (!callFrame.url)
return false;
parsedUrl = new Common.ParsedURL(callFrame.url);
return !!registry.entryForUrl(parsedUrl);
});
}
return parsedUrl;
}
/**
* @param {!Element} badgeElement
*/
async _renderBadge(badgeElement) {
if (badgeElement.children.length || !this._isVisible(badgeElement)) {
this._updateBadgeElementVisibility(badgeElement);
return;
}
const parsedUrl = await this._badgeElements.get(badgeElement)();
const registry = await ProductRegistry.instance();
const entryName = parsedUrl && registry.nameForUrl(parsedUrl);
if (!entryName)
return;
const tokens = entryName.replace(/[a-z]*/g, '').split(' ');
let label;
if (tokens.length > 1)
label = tokens[0][0] + tokens[1][0];
else
label = entryName;
const iconElement = badgeElement.createChild('span', 'product-registry-badge monospace');
iconElement.setAttribute('data-initial', label.substring(0, 2).toUpperCase());
iconElement.title = entryName;
iconElement.style.backgroundColor = ProductRegistry.BadgePool.colorForEntryName(entryName);
badgeElement.createChild('span', 'product-registry-badge-title').textContent = entryName;
this._updateBadgeElementVisibility(badgeElement);
}
_settingUpdated() {
for (const badgeElement of this._badgeElements.keys())
this._renderBadge(badgeElement);
}
/**
* @param {!Element} badgeElement
* @return {boolean}
*/
_isVisible(badgeElement) {
return !this._setting || this._setting.get();
}
/**
* @param {!Element} badgeElement
*/
_updateBadgeElementVisibility(badgeElement) {
badgeElement.parentNodeOrShadowHost().parentNodeOrShadowHost().classList.toggle(
'hidden', !this._isVisible(badgeElement));
}
/**
* @param {!Element} badgeElement
*/
async _showPopup(badgeElement) {
if (!this._badgeElements.has(badgeElement))
return;
const registry = await ProductRegistry.instance();
const parsedUrl = await this._badgeElements.get(badgeElement)();
const entryName = registry.nameForUrl(parsedUrl);
const element = createElement('div');
const root = UI.createShadowRootWithCoreStyles(element, 'product_registry/popup.css');
const popupElement = root.createChild('div', 'product-registry-popup');
const domainElement = popupElement.createChild('div', 'product-registry-domain');
domainElement.textContent = parsedUrl.domain();
const entryNameElement = popupElement.createChild('div', 'product-registry-name');
entryNameElement.textContent = entryName;
const reportLink =
'https://docs.google.com/forms/d/e/1FAIpQLSchz2FdcQ-rRllzl8BbhWaTRRY-12BpPjW6Hr9e1-BpCA083w/viewform' +
'?entry_1425918171=' + encodeURIComponent((parsedUrl.domain() + parsedUrl.path).substr(0, 100));
popupElement.appendChild(UI.XLink.create(reportLink, 'Report mismatch', 'product-registry-link'));
const dialog = new UI.Dialog();
dialog.setContentAnchorBox(badgeElement.boxInWindow());
dialog.contentElement.appendChild(element);
dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MeasureContent);
dialog.setAnchorBehavior(UI.GlassPane.AnchorBehavior.PreferTop);
dialog.addCloseButton();
dialog.show(/** @type {!Document} */ (badgeElement.ownerDocument));
}
/**
* @param {string} entryName
* @return {string}
*/
static colorForEntryName(entryName) {
if (!ProductRegistry.BadgePool._colorGenerator) {
ProductRegistry.BadgePool._colorGenerator =
new Common.Color.Generator({min: 30, max: 330}, {min: 50, max: 80, count: 3}, 80);
}
return ProductRegistry.BadgePool._colorGenerator.colorForID(entryName);
}
};