Skip to content

Commit 28b6c5c

Browse files
committed
Bug 1777343 - Implement simple origin controls attention indicator, r=willdurand,desktop-theme-reviewers,Itiel
Differential Revision: https://phabricator.services.mozilla.com/D158476
1 parent 1778c77 commit 28b6c5c

File tree

7 files changed

+82
-7
lines changed

7 files changed

+82
-7
lines changed

browser/base/content/browser-addons.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,12 @@ customElements.define(
13351335

13361336
this.setAttribute("extension-id", this.addon.id);
13371337

1338+
let policy = WebExtensionPolicy.getByID(this.addon.id);
1339+
this.setAttribute(
1340+
"attention",
1341+
lazy.OriginControls.getAttention(policy, this.ownerGlobal)
1342+
);
1343+
13381344
this.querySelector(
13391345
".unified-extensions-item-name"
13401346
).textContent = this.addon.name;

browser/components/extensions/parent/ext-browserAction.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ ChromeUtils.defineModuleGetter(
3636
"BrowserUsageTelemetry",
3737
"resource:///modules/BrowserUsageTelemetry.jsm"
3838
);
39+
ChromeUtils.defineModuleGetter(
40+
this,
41+
"OriginControls",
42+
"resource://gre/modules/ExtensionPermissions.jsm"
43+
);
3944

4045
var { DefaultWeakMap } = ExtensionUtils;
4146

@@ -249,7 +254,7 @@ this.browserAction = class extends ExtensionAPIPersistent {
249254
node.onmouseout = event => this.handleEvent(event);
250255
node.onauxclick = event => this.handleEvent(event);
251256

252-
this.updateButton(node, this.action.getContextData(null), true);
257+
this.updateButton(node, this.action.getContextData(null), true, false);
253258
},
254259

255260
onBeforeCommand: event => {
@@ -566,12 +571,14 @@ this.browserAction = class extends ExtensionAPIPersistent {
566571

567572
// Update the toolbar button |node| with the tab context data
568573
// in |tabData|.
569-
updateButton(node, tabData, sync = false) {
574+
updateButton(node, tabData, sync = false, attention = false) {
570575
let title = tabData.title || this.extension.name;
571576
let callback = () => {
572577
node.setAttribute("tooltiptext", title);
573578
node.setAttribute("label", title);
574579

580+
node.setAttribute("attention", attention);
581+
575582
if (tabData.badgeText) {
576583
node.setAttribute("badge", tabData.badgeText);
577584
} else {
@@ -640,7 +647,12 @@ this.browserAction = class extends ExtensionAPIPersistent {
640647
let node = this.widget.forWindow(window).node;
641648
if (node) {
642649
let tab = window.gBrowser.selectedTab;
643-
this.updateButton(node, this.action.getContextData(tab));
650+
this.updateButton(
651+
node,
652+
this.action.getContextData(tab),
653+
false,
654+
OriginControls.getAttention(this.extension.policy, window)
655+
);
644656
}
645657
}
646658

browser/components/extensions/test/browser/browser_ext_originControls.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,27 @@ async function makeExtension({ id, permissions, host_permissions, granted }) {
5757
async function testOriginControls(
5858
extension,
5959
{ win, contextMenuId },
60-
{ items, selected, click, granted, revoked }
60+
{ items, selected, click, granted, revoked, attention }
6161
) {
6262
info(
6363
`Testing ${extension.id} on ${gBrowser.currentURI.spec} with contextMenuId=${contextMenuId}.`
6464
);
6565

66+
let button;
6667
let menu;
6768
let manageExtensionClassName;
6869

6970
switch (contextMenuId) {
7071
case "toolbar-context-menu":
7172
let target = `#${CSS.escape(makeWidgetId(extension.id))}-browser-action`;
73+
button = win.document.querySelector(target);
7274
menu = await openChromeContextMenu(contextMenuId, target);
7375
manageExtensionClassName = "customize-context-manageExtension";
7476
break;
7577

7678
case "unified-extensions-context-menu":
7779
await openExtensionsPanel(win);
80+
button = getUnifiedExtensionsItem(win, extension.id);
7881
menu = await openUnifiedExtensionsContextMenu(win, extension.id);
7982
manageExtensionClassName =
8083
"unified-extensions-context-menu-manage-extension";
@@ -102,6 +105,12 @@ async function testOriginControls(
102105
"All items accounted for."
103106
);
104107

108+
is(
109+
button.getAttribute("attention"),
110+
attention ? "true" : "false",
111+
"Expected attention badge before clicking."
112+
);
113+
105114
let itemToClick;
106115
if (click) {
107116
itemToClick = menu.children[click];
@@ -178,18 +187,21 @@ const originControlsInContextMenu = async options => {
178187
await testOriginControls(ext2, options, {
179188
items: [ACCESS_OPTIONS, WHEN_CLICKED],
180189
selected: 1,
190+
attention: true,
181191
});
182192

183193
// Could access mochi.test when clicked.
184194
await testOriginControls(ext3, options, {
185195
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
186196
selected: 1,
197+
attention: true,
187198
});
188199

189200
// Has <all_urls> granted.
190201
await testOriginControls(ext4, options, {
191202
items: [ACCESS_OPTIONS, ALL_SITES],
192203
selected: 1,
204+
attention: false,
193205
});
194206
});
195207

@@ -206,16 +218,19 @@ const originControlsInContextMenu = async options => {
206218
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
207219
selected: 1,
208220
click: 1,
221+
attention: true,
209222
});
210223
await testOriginControls(ext3, options, {
211224
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
212225
selected: 2,
213226
click: 2,
227+
attention: false,
214228
});
215229
await testOriginControls(ext4, options, {
216230
items: [ACCESS_OPTIONS, ALL_SITES],
217231
selected: 1,
218232
click: 1,
233+
attention: false,
219234
});
220235

221236
// Click the other option, expect example.com permission granted/revoked.
@@ -224,22 +239,26 @@ const originControlsInContextMenu = async options => {
224239
selected: 1,
225240
click: 2,
226241
granted: ["*://example.com/*"],
242+
attention: true,
227243
});
228244
await testOriginControls(ext3, options, {
229245
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
230246
selected: 2,
231247
click: 1,
232248
revoked: ["*://example.com/*"],
249+
attention: false,
233250
});
234251

235252
// Other option is now selected.
236253
await testOriginControls(ext2, options, {
237254
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
238255
selected: 2,
256+
attention: false,
239257
});
240258
await testOriginControls(ext3, options, {
241259
items: [ACCESS_OPTIONS, WHEN_CLICKED, ALWAYS_ON],
242260
selected: 1,
261+
attention: true,
243262
});
244263
});
245264

browser/themes/shared/addons/unified-extensions-item.css

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,33 @@
44

55
/* Style for the custom element "unified-extensions-item". */
66

7+
:root {
8+
--icon-size: 32px;
9+
--dot-position: calc(var(--icon-size) / 2 + var(--arrowpanel-menuitem-margin-inline) + var(--arrowpanel-menuitem-padding-inline) - 4px);
10+
}
11+
12+
:root[uidensity="compact"] {
13+
--icon-size: 24px;
14+
}
15+
716
unified-extensions-item {
817
align-items: center;
918
display: flex;
1019
}
1120

21+
/* Larger attention indicator for use below larger icons, see for reference:
22+
* https://searchfox.org/mozilla-central/rev/560b7b1b17/browser/themes/shared/tabs.css#624 */
23+
unified-extensions-item[attention="true"] {
24+
background-image: radial-gradient(circle, var(--tab-attention-icon-color), var(--tab-attention-icon-color) 3px, transparent 3px);
25+
background-position: left var(--dot-position) bottom 3px;
26+
background-size: 8px 8px;
27+
background-repeat: no-repeat;
28+
}
29+
30+
unified-extensions-item[attention="true"]:-moz-locale-dir(rtl) {
31+
background-position-x: right var(--dot-position);
32+
}
33+
1234
.unified-extensions-item-action {
1335
overflow: hidden;
1436
}
@@ -33,8 +55,8 @@ unified-extensions-item[secondary-button-hovered="true"] .unified-extensions-ite
3355
}
3456

3557
.unified-extensions-item-icon {
36-
height: 32px;
37-
width: 32px;
58+
height: var(--icon-size);
59+
width: var(--icon-size);
3860
margin-inline-end: 6px;
3961
}
4062

browser/themes/shared/tabs.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,13 +621,18 @@ toolbar[brighttext] {
621621

622622
.tabbrowser-tab:is([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected="true"]),
623623
.tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected="true"]),
624-
#firefox-view-button[attention] {
624+
#firefox-view-button[attention],
625+
.webextension-browser-action[attention="true"] {
625626
background-image: radial-gradient(circle, var(--tab-attention-icon-color), var(--tab-attention-icon-color) 2px, transparent 2px);
626627
background-position: center bottom calc(6.5px + var(--tabs-navbar-shadow-size));
627628
background-size: 4px 4px;
628629
background-repeat: no-repeat;
629630
}
630631

632+
:root[uidensity="compact"] .webextension-browser-action[attention="true"] {
633+
background-position-y: bottom 4.5px;
634+
}
635+
631636
.tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned], [selected="true"]) {
632637
background-position-x: left 14px;
633638
}

toolkit/components/extensions/ExtensionActions.jsm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ class PanelActionBase {
6262
this.updateOnChange(tab);
6363
});
6464

65+
// eslint-disable-next-line mozilla/balanced-listeners
66+
extension.on("add-permissions", () => this.updateOnChange());
67+
// eslint-disable-next-line mozilla/balanced-listeners
68+
extension.on("remove-permissions", () => this.updateOnChange());
69+
6570
// When preloading a popup we temporarily grant active tab permissions to
6671
// the preloaded popup. If we don't end up opening we need to clear this
6772
// permission when clearing the popup.

toolkit/components/extensions/ExtensionPermissions.jsm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@ var OriginControls = {
463463
};
464464
},
465465

466+
// Whether to show the attention indicator for extension on current tab.
467+
getAttention(policy, window) {
468+
let state = this.getState(policy, window.gBrowser.currentURI);
469+
return !!state.whenClicked && !state.hasAccess;
470+
},
471+
466472
// Grant extension host permission to always run on this host.
467473
setAlwaysOn(policy, uri) {
468474
if (!policy.active) {

0 commit comments

Comments
 (0)