Bug 1561537 - Add badge/feature-callout style that matches the design spec r=r1cky

Differential Revision: https://phabricator.services.mozilla.com/D37403

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrei Oprea 2019-07-11 20:19:08 +00:00
parent 556047a14b
commit 74e5670fe7
5 changed files with 230 additions and 0 deletions

View File

@ -0,0 +1,204 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
ChromeUtils.defineModuleGetter(
this,
"EveryWindow",
"resource:///modules/EveryWindow.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ToolbarPanelHub",
"resource://activity-stream/lib/ToolbarPanelHub.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"setTimeout",
"resource://gre/modules/Timer.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"clearTimeout",
"resource://gre/modules/Timer.jsm"
);
const notificationsByWindow = new WeakMap();
class _ToolbarBadgeHub {
constructor() {
this.id = "toolbar-badge-hub";
this.template = "toolbar_badge";
this.state = null;
this.removeAllNotifications = this.removeAllNotifications.bind(this);
this.removeToolbarNotification = this.removeToolbarNotification.bind(this);
this.addToolbarNotification = this.addToolbarNotification.bind(this);
this.registerBadgeToAllWindows = this.registerBadgeToAllWindows.bind(this);
this._handleMessageRequest = null;
this._addImpression = null;
this._blockMessageById = null;
}
async init(
waitForInitialized,
{ handleMessageRequest, addImpression, blockMessageById }
) {
this._handleMessageRequest = handleMessageRequest;
this._blockMessageById = blockMessageById;
this._addImpression = addImpression;
this.state = {};
// Need to wait for ASRouter to initialize before trying to fetch messages
await waitForInitialized;
this.messageRequest("toolbarBadgeUpdate");
}
executeAction({ id }) {
switch (id) {
case "show-whatsnew-button":
ToolbarPanelHub.enableToolbarButton();
break;
}
}
_clearBadgeTimeout() {
if (this.state.showBadgeTimeoutId) {
clearTimeout(this.state.showBadgeTimeoutId);
}
}
removeAllNotifications(event) {
if (event) {
// ignore right clicks
if (
(event.type === "mousedown" || event.type === "click") &&
event.button !== 0
) {
return;
}
// ignore keyboard access that is not one of the usual accessor keys
if (
event.type === "keypress" &&
event.key !== " " &&
event.key !== "Enter"
) {
return;
}
event.target.removeEventListener(
"mousedown",
this.removeAllNotifications
);
event.target.removeEventListener("click", this.removeAllNotifications);
}
// Will call uninit on every window
EveryWindow.unregisterCallback(this.id);
if (this.state.notification) {
this._blockMessageById(this.state.notification.id);
}
this._clearBadgeTimeout();
this.state = {};
}
removeToolbarNotification(toolbarButton) {
toolbarButton
.querySelector(".toolbarbutton-badge")
.classList.remove("feature-callout");
toolbarButton
.querySelector(".toolbarbutton-icon")
.classList.add("feature-callout");
toolbarButton.removeAttribute("badged");
}
addToolbarNotification(win, message) {
const document = win.browser.ownerDocument;
if (message.content.action) {
this.executeAction(message.content.action);
}
let toolbarbutton = document.getElementById(message.content.target);
if (toolbarbutton) {
toolbarbutton.setAttribute("badged", true);
toolbarbutton
.querySelector(".toolbarbutton-badge")
.classList.add("feature-callout");
toolbarbutton
.querySelector(".toolbarbutton-icon")
.classList.add("feature-callout");
// `mousedown` event required because of the `onmousedown` defined on
// the button that prevents `click` events from firing
toolbarbutton.addEventListener("mousedown", this.removeAllNotifications);
// `click` event required for keyboard accessibility
toolbarbutton.addEventListener("click", this.removeAllNotifications);
this.state = { notification: { id: message.id } };
return toolbarbutton;
}
return null;
}
registerBadgeToAllWindows(message) {
// Impression should be added when the badge becomes visible
this._addImpression(message);
EveryWindow.registerCallback(
this.id,
win => {
if (notificationsByWindow.has(win)) {
// nothing to do
return;
}
const el = this.addToolbarNotification(win, message);
notificationsByWindow.set(win, el);
},
win => {
const el = notificationsByWindow.get(win);
this.removeToolbarNotification(el);
notificationsByWindow.delete(win);
}
);
}
registerBadgeNotificationListener(message, options = {}) {
// We need to clear any existing notifications and only show
// the one set by devtools
if (options.force) {
this.removeAllNotifications();
}
if (message.content.delay) {
this.state.showBadgeTimeoutId = setTimeout(() => {
this.registerBadgeToAllWindows(message);
}, message.content.delay);
} else {
this.registerBadgeToAllWindows(message);
}
}
async messageRequest(triggerId) {
const message = await this._handleMessageRequest({
triggerId,
template: this.template,
});
if (message) {
this.registerBadgeNotificationListener(message);
}
}
uninit() {
this._clearBadgeTimeout();
this.state = null;
}
}
this._ToolbarBadgeHub = _ToolbarBadgeHub;
/**
* ToolbarBadgeHub - singleton instance of _ToolbarBadgeHub that can initiate
* message requests and render messages.
*/
this.ToolbarBadgeHub = new _ToolbarBadgeHub();
const EXPORTED_SYMBOLS = ["ToolbarBadgeHub", "_ToolbarBadgeHub"];

View File

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle fill-opacity=".2" fill="#0DF" cx="8" cy="8" r="4"/><circle stroke-opacity=".5" stroke="#0090ED" fill="#00B3F4" cx="8" cy="8" r="2.5"/></g></svg>

After

Width:  |  Height:  |  Size: 250 B

View File

@ -0,0 +1,5 @@
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
<clipPath id="badge-cutout">
<path d="M11.1 0A5 5 0 0 0 16 6v10H0V0h11.1z"></path>
</clipPath>
</svg>

After

Width:  |  Height:  |  Size: 172 B

View File

@ -245,6 +245,8 @@
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
skin/classic/browser/update-badge.svg (../shared/update-badge.svg)
skin/classic/browser/badge-cutout.svg (../shared/badge-cutout.svg)
skin/classic/browser/badge-blue.svg (../shared/badge-blue.svg)
skin/classic/browser/warning.svg (../shared/warning.svg)
skin/classic/browser/cert-error.svg (../shared/incontent-icons/cert-error.svg)
skin/classic/browser/wifi.svg (../shared/incontent-icons/wifi.svg)

View File

@ -389,3 +389,21 @@ toolbarbutton.bookmark-item {
padding: 1px var(--toolbarbutton-inner-padding);
border-radius: var(--toolbarbutton-border-radius);
}
/* Alternative style for .toolbarbutton-badge used by CFR notifications */
.toolbarbutton-badge.feature-callout {
width: 14px;
height: 14px;
min-width: auto;
box-shadow: none;
border: none;
padding: 0;
display: block;
margin: -7px 0 0 !important;
margin-inline-end: -6px !important;
background: url(chrome://browser/skin/badge-blue.svg);
}
.toolbarbutton-icon.feature-callout {
clip-path: url(chrome://browser/skin/badge-cutout.svg#badge-cutout);
}