Bug 1525178 - Part 3: Add second about:addons sidebar in HTML r=fluent-reviewers,rpl,Gijs

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Mark Striemer 2020-03-30 19:45:05 +00:00
parent 1a49c36c2c
commit 21205b994f
14 changed files with 680 additions and 56 deletions

View File

@ -36,6 +36,18 @@ body {
border-top-color: rgba(255,255,255,0.15);
}
#category-active {
background-image: url("chrome://browser/content/policies/policies-active.svg");
}
#category-documentation {
background-image: url("chrome://browser/content/policies/policies-documentation.svg");
}
#category-errors {
background-image: url("chrome://browser/content/policies/policies-error.svg");
}
/** Content area **/
.main-content {

View File

@ -19,15 +19,12 @@
<body id="body">
<div id="categories">
<div class="category" selected="true" id="category-active">
<img class="category-icon" src="chrome://browser/content/policies/policies-active.svg">
<label class="category-name" data-l10n-id="active-policies-tab"></label>
</div>
<div class="category" id="category-documentation">
<img class="category-icon" src="chrome://browser/content/policies/policies-documentation.svg">
<label class="category-name" data-l10n-id="documentation-tab"></label>
</div>
<div class="category" id="category-errors">
<img class="category-icon" src="chrome://browser/content/policies/policies-error.svg">
<label class="category-name" data-l10n-id="errors-tab"></label>
</div>
</div>

View File

@ -0,0 +1,38 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
from __future__ import absolute_import
import fluent.syntax.ast as FTL
from fluent.migrate.helpers import transforms_from
from fluent.migrate import COPY_PATTERN, COPY
def migrate(ctx):
"""Bug 1525178 - Convert about:addons sidebar to HTML, part {index}"""
ctx.add_transforms(
'toolkit/toolkit/about/aboutAddons.ftl',
'toolkit/toolkit/about/aboutAddons.ftl',
transforms_from(
"""
addon-category-discover = {COPY_PATTERN(from_path, "extensions-view-discopane.name")}
addon-category-available-updates = {COPY_PATTERN(from_path, "extensions-view-available-updates.name")}
addon-category-recent-updates = {COPY_PATTERN(from_path, "extensions-view-recent-updates.name")}
"""
, from_path='toolkit/toolkit/about/aboutAddons.ftl')
)
ctx.add_transforms(
'toolkit/toolkit/about/aboutAddons.ftl',
'toolkit/toolkit/about/aboutAddons.ftl',
transforms_from(
"""
addon-category-extension = {COPY(from_path, "type.extension.name")}
addon-category-theme = {COPY(from_path, "type.themes.name")}
addon-category-locale = {COPY(from_path, "type.locale.name")}
addon-category-plugin = {COPY(from_path, "type.plugin.name")}
addon-category-dictionary = {COPY(from_path, "type.dictionary.name")}
"""
, from_path='toolkit/chrome/mozapps/extensions/extensions.properties')
)

View File

@ -16,29 +16,29 @@
</head>
<body id="body">
<div id="categories">
<div class="category" selected="true" id="category-http">
<div class="category category-no-icon" selected="true" id="category-http">
<span class="category-name" data-l10n-id="about-networking-http"/>
</div>
<div class="category" id="category-sockets">
<div class="category category-no-icon" id="category-sockets">
<span class="category-name" data-l10n-id="about-networking-sockets"/>
</div>
<div class="category" id="category-dns">
<div class="category category-no-icon" id="category-dns">
<span class="category-name" data-l10n-id="about-networking-dns"/>
</div>
<div class="category" id="category-websockets">
<div class="category category-no-icon" id="category-websockets">
<span class="category-name" data-l10n-id="about-networking-websockets"/>
</div>
<hr></hr>
<div class="category" id="category-dnslookuptool">
<div class="category category-no-icon" id="category-dnslookuptool">
<span class="category-name" data-l10n-id="about-networking-dns-lookup"/>
</div>
<div class="category" id="category-logging">
<div class="category category-no-icon" id="category-logging">
<span class="category-name" data-l10n-id="about-networking-logging"/>
</div>
<div class="category" id="category-rcwn">
<div class="category category-no-icon" id="category-rcwn">
<span class="category-name" data-l10n-id="about-networking-rcwn"/>
</div>
<div class="category" id="category-networkid">
<div class="category category-no-icon" id="category-networkid">
<span class="category-name" data-l10n-id="about-networking-networkid"/>
</div>
</div>

View File

@ -209,6 +209,15 @@ extensions-view-available-updates =
.name = Available Updates
.tooltiptext = { extensions-view-available-updates.name }
addon-category-discover = Recommendations
addon-category-extension = Extensions
addon-category-theme = Themes
addon-category-plugin = Plugins
addon-category-dictionary = Dictionaries
addon-category-locale = Languages
addon-category-available-updates = Available Updates
addon-category-recent-updates = Recent Updates
## These are global warnings
extensions-warning-safe-mode = All add-ons have been disabled by safe mode.

View File

@ -3,12 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
:root {
--section-width: 664px;
--addon-icon-size: 32px;
--main-margin-start: 28px;
--section-width: 664px;
--sidebar-width: var(--in-content-sidebar-width);
--z-index-sticky-container: 1;
--z-index-popup: 10;
}
@media (max-width: 830px) {
:root {
--main-margin-start: 16px;
/* Maintain a main margin so card shadows don't overlap the sidebar. */
--sidebar-width: calc(var(--in-content-sidebar-width) - var(--main-margin-start));
}
}
*|*[hidden] {
display: none !important;
}
@ -19,6 +29,74 @@ body {
min-width: 500px;
}
#full {
display: grid;
grid-template-columns: var(--sidebar-width) 1fr;
}
#sidebar {
position: sticky;
top: 0;
height: 100vh;
display: flex;
flex-direction: column;
margin: 0;
overflow: hidden auto;
}
#categories {
display: flex;
flex-direction: column;
padding-inline-end: 4px; /* Leave space for the button focus styles. */
}
.category {
display: grid;
grid-template-columns: 1fr auto;
margin-block: 0;
align-items: center;
}
.category[badge-count]::after {
display: inline-block;
min-width: 20px;
background-color: var(--blue-50);
color: #fff;
font-weight: bold;
/* Use a large border-radius to get semi-circles on the sides. */
border-radius: 1000px;
padding: 2px 6px;
content: attr(badge-count);
text-align: center;
margin-inline-start: 8px;
grid-column: 2;
}
.category[name="discover"] {
background-image: url("chrome://mozapps/skin/extensions/category-discover.svg");
}
.category[name="locale"] {
background-image: url("chrome://mozapps/skin/extensions/category-languages.svg");
}
.category[name="extension"] {
background-image: url("chrome://mozapps/skin/extensions/category-extensions.svg");
}
.category[name="theme"] {
background-image: url("chrome://mozapps/skin/extensions/category-themes.svg");
}
.category[name="plugin"] {
background-image: url("chrome://mozapps/skin/extensions/category-plugins.svg");
}
.category[name="dictionary"] {
background-image: url("chrome://mozapps/skin/extensions/category-dictionaries.svg");
}
.category[name="available-updates"] {
background-image: url("chrome://mozapps/skin/extensions/category-available.svg");
}
.category[name="recent-updates"] {
background-image: url("chrome://mozapps/skin/extensions/category-recent.svg");
}
.header-name {
-moz-user-select: initial;
}
@ -43,8 +121,9 @@ body {
}
search-addons > search-textbox {
margin-inline-end: 0;
margin: 0;
width: 20em;
min-height: 32px;
}
.search-label {
@ -54,7 +133,7 @@ search-addons > search-textbox {
.main-heading {
background: var(--in-content-page-background);
display: flex;
margin-inline-start: 28px;
margin-inline-start: var(--main-margin-start);
padding-bottom: 16px;
max-width: var(--section-width);
}
@ -80,14 +159,14 @@ search-addons > search-textbox {
}
#main {
margin-inline-start: 28px;
margin-inline-start: var(--main-margin-start);
margin-bottom: 28px;
max-width: var(--section-width);
}
global-warnings,
#abuse-reports-messages {
margin-inline-start: 28px;
margin-inline-start: var(--main-margin-start);
max-width: var(--section-width);
}
@ -550,15 +629,28 @@ button.tab-button[selected] {
color: var(--in-content-category-text-selected) !important;
}
button.tab-button:focus {
border-top-color: transparent;
}
button.tab-button:-moz-focusring {
outline-offset: -2px;
-moz-outline-radius: 0;
}
.tab-group[last-input-type="mouse"] > button.tab-button:-moz-focusring {
outline: none;
box-shadow: none;
}
panel-list {
font-size: 13px;
}
@media (max-width: 830px) {
.category[badge-count]::after {
content: "";
display: block;
width: 5px;
height: 5px;
border-radius: 50%;
min-width: auto;
padding: 0;
}
}

View File

@ -31,13 +31,31 @@
<script defer src="chrome://mozapps/content/extensions/aboutaddons.js"></script>
</head>
<body>
<addon-page-header id="page-header" page-options-id="page-options"></addon-page-header>
<addon-page-options id="page-options"></addon-page-options>
<div id="full">
<div id="sidebar">
<categories-box id="categories" orientation="vertical">
<button is="discover-button" viewid="addons://discover/" name="discover"></button>
<button is="category-button" viewid="addons://list/extension" name="extension"></button>
<button is="category-button" viewid="addons://list/theme" name="theme"></button>
<button is="category-button" viewid="addons://list/plugin" name="plugin"></button>
<button is="category-button" viewid="addons://list/dictionary" name="dictionary" hidden default-hidden></button>
<button is="category-button" viewid="addons://list/locale" name="locale" hidden default-hidden></button>
<button is="category-button" viewid="addons://updates/available" name="available-updates" hidden default-hidden></button>
<button is="category-button" viewid="addons://updates/recent" name="recent-updates" hidden default-hidden></button>
</categories-box>
<div class="spacer"></div>
<sidebar-footer></sidebar-footer>
</div>
<div id="content">
<addon-page-header id="page-header" page-options-id="page-options"></addon-page-header>
<addon-page-options id="page-options"></addon-page-options>
<message-bar-stack id="abuse-reports-messages" reverse max-message-bar-count="3">
</message-bar-stack>
<message-bar-stack id="abuse-reports-messages" reverse max-message-bar-count="3">
</message-bar-stack>
<div id="main"></div>
<div id="main"></div>
</div>
</div>
<!-- Include helpers for the inline options browser select and context menus. -->
<content-select-dropdown></content-select-dropdown>

View File

@ -233,6 +233,10 @@ function hasPermission(addon, permission) {
return !!(addon.permissions & PERMISSION_MASKS[permission]);
}
function isInState(install, state) {
return install.state == AddonManager["STATE_" + state.toUpperCase()];
}
async function getAddonMessageInfo(addon) {
const { name } = addon;
const appName = brandBundle.GetStringFromName("brandShortName");
@ -421,7 +425,14 @@ async function isAddonOptionsUIAllowed(addon) {
* @param {string} param The (optional) param for the view.
*/
let loadViewFn;
/**
* This function is set in initialize() by the parent about:addons window. It
* is a helper for gViewController.replaceView(gViewDefault). This should be
* used to reset the view if we try to load an invalid view.
*/
let replaceWithDefaultViewFn;
let getCurrentViewIdFn;
let setCategoryFn;
let _templates = {};
@ -1615,6 +1626,289 @@ class AddonPageOptions extends HTMLElement {
}
customElements.define("addon-page-options", AddonPageOptions);
class CategoryButton extends HTMLButtonElement {
connectedCallback() {
if (this.childElementCount != 0) {
return;
}
this.classList.add("category");
// Make sure the aria-selected attribute is set correctly.
this.selected = this.hasAttribute("selected");
let text = document.createElement("span");
text.classList.add("category-name");
document.l10n.setAttributes(text, `addon-category-${this.name}`);
this.append(text);
}
get isVisible() {
return true;
}
get badgeCount() {
return this.getAttribute("badge-count");
}
set badgeCount(val) {
if (val !== null) {
this.setAttribute("badge-count", val);
} else {
this.removeAttribute("badge-count");
}
}
get selected() {
this.hasAttribute("selected");
}
set selected(val) {
this.toggleAttribute("selected", !!val);
this.setAttribute("aria-selected", !!val);
}
get name() {
return this.getAttribute("name");
}
// Just setting the hidden attribute isn't enough in case the category gets
// hidden while about:addons is closed since it could be the last active view
// which will unhide the button when it gets selected.
get defaultHidden() {
return this.hasAttribute("default-hidden");
}
}
customElements.define("category-button", CategoryButton, { extends: "button" });
class DiscoverButton extends CategoryButton {
get isVisible() {
return isDiscoverEnabled();
}
}
customElements.define("discover-button", DiscoverButton, { extends: "button" });
class CategoriesBox extends customElements.get("button-group") {
constructor() {
super();
// This will resolve when the initial category states have been set from
// our cached prefs. This is intended for use in testing to verify that we
// are caching the previous state.
this.promiseRendered = new Promise(resolve => {
this._resolveRendered = resolve;
});
// This will resolve when the final category states have been set by
// checking the AddonManager state and showing/hiding categories. The page
// won't be "initialized" until this resolves.
this.promiseInitialized = new Promise(resolve => {
this._resolveInitialized = resolve;
});
}
async initialize() {
let addonTypesObjects = AddonManager.addonTypes;
let addonTypes = new Set();
for (let type in addonTypesObjects) {
addonTypes.add(type);
}
let hiddenTypes = new Set([]);
for (let button of this.children) {
let { defaultHidden, name } = button;
button.hidden =
!button.isVisible || (defaultHidden && this.shouldHideCategory(name));
if (defaultHidden && addonTypes.has(name)) {
hiddenTypes.add(name);
}
}
let hiddenUpdated;
if (hiddenTypes.size) {
hiddenUpdated = this.updateHiddenCategories(Array.from(hiddenTypes));
}
this.addEventListener("click", e => {
let button = e.target.closest("[viewid]");
if (button) {
loadViewFn(button.getAttribute("viewid"));
}
});
AddonManagerListenerHandler.addListener(this);
this._resolveRendered();
await hiddenUpdated;
this._resolveInitialized();
}
shouldHideCategory(name) {
return Services.prefs.getBoolPref(`extensions.ui.${name}.hidden`, true);
}
setShouldHideCategory(name, hide) {
Services.prefs.setBoolPref(`extensions.ui.${name}.hidden`, hide);
}
getButtonByName(name) {
return this.querySelector(`[name="${name}"]`);
}
select(viewId) {
let button = this.querySelector(`[viewid="${viewId}"]`);
if (button) {
this.activeChild = button;
this.selectedChild = button;
button.hidden = false;
Services.prefs.setStringPref(PREF_UI_LASTCATEGORY, viewId);
}
}
onInstalled(addon) {
let button = this.getButtonByName(addon.type);
if (button) {
button.hidden = false;
this.setShouldHideCategory(addon.type, false);
}
this.updateAvailableCount();
}
onInstallStarted(install) {
this.onInstalled(install);
}
onNewInstall() {
this.updateAvailableCount();
}
onInstallCancelled() {
this.updateAvailableCount();
}
isManualUpdate(install) {
let isManual =
install.existingAddon &&
!AddonManager.shouldAutoUpdate(install.existingAddon);
return isManual && isInState(install, "available");
}
async updateAvailableCount() {
let installs = await AddonManager.getAllInstalls();
var count = installs.filter(install => {
return this.isManualUpdate(install, true) && !install.installed;
}).length;
let availableButton = this.getButtonByName("available-updates");
availableButton.hidden = !availableButton.selected && count == 0;
availableButton.badgeCount = count;
}
async updateHiddenCategories(types) {
let hiddenTypes = new Set(types);
let getAddons = AddonManager.getAddonsByTypes(types);
let getInstalls = AddonManager.getInstallsByTypes(types);
for (let addon of await getAddons) {
if (addon.hidden) {
continue;
}
this.onInstalled(addon);
hiddenTypes.delete(addon.type);
if (!hiddenTypes.size) {
return;
}
}
for (let install of await getInstalls) {
if (
install.existingAddon ||
install.state == AddonManager.STATE_AVAILABLE
) {
continue;
}
this.onInstalled(install);
hiddenTypes.delete(install.type);
if (!hiddenTypes.size) {
return;
}
}
for (let type of hiddenTypes) {
let button = this.getButtonByName(type);
if (button.selected) {
// Cancel the load if this view should be hidden.
replaceWithDefaultViewFn();
}
this.setShouldHideCategory(type, true);
button.hidden = true;
}
}
}
customElements.define("categories-box", CategoriesBox);
class SidebarFooter extends HTMLElement {
connectedCallback() {
let list = document.createElement("ul");
list.classList.add("sidebar-footer-list");
let prefsItem = document.createElement("li");
prefsItem.classList.add("sidebar-footer-item");
let prefsLink = document.createElement("a");
prefsLink.classList.add("sidebar-footer-link", "preferences-icon");
prefsLink.id = "preferencesButton";
prefsLink.href = "about:preferences";
let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
prefsLink.addEventListener("click", e => {
e.preventDefault();
AMTelemetry.recordLinkEvent({
object: "aboutAddons",
value: "about:preferences",
extra: {
view: getTelemetryViewName(this),
},
});
windowRoot.ownerGlobal.switchToTabHavingURI("about:preferences", true, {
ignoreFragment: "whenComparing",
triggeringPrincipal: systemPrincipal,
});
});
let prefsText = document.createElement("span");
prefsText.classList.add("sidebar-footer-link-text");
document.l10n.setAttributes(prefsText, "preferences");
prefsLink.append(prefsText);
prefsItem.append(prefsLink);
let supportItem = document.createElement("li");
supportItem.classList.add("sidebar-footer-item");
let supportLink = document.createElement("a", { is: "support-link" });
supportLink.classList.add("sidebar-footer-link", "help-icon");
supportLink.id = "help-button";
supportLink.setAttribute("support-page", "addons-help");
supportLink.addEventListener("click", e => {
AMTelemetry.recordLinkEvent({
object: "aboutAddons",
value: "support",
extra: {
view: getTelemetryViewName(this),
},
});
});
let supportText = document.createElement("span");
supportText.classList.add("sidebar-footer-link-text");
document.l10n.setAttributes(supportText, "help-button");
supportLink.append(supportText);
supportItem.append(supportLink);
list.append(prefsItem, supportItem);
this.append(list);
}
}
customElements.define("sidebar-footer", SidebarFooter, { extends: "footer" });
class AddonOptions extends HTMLElement {
connectedCallback() {
if (!this.children.length) {
@ -4087,7 +4381,9 @@ let addonPageHeader = null;
* @returns {string} The current view name.
*/
function getTelemetryViewName(el) {
return el.closest("[current-view]").getAttribute("current-view");
let root =
el.closest("[current-view]") || document.querySelector("[current-view]");
return root.getAttribute("current-view");
}
/**
@ -4133,6 +4429,10 @@ function initialize(opts) {
loadViewFn = opts.loadViewFn;
replaceWithDefaultViewFn = opts.replaceWithDefaultViewFn;
setCategoryFn = opts.setCategoryFn;
getCurrentViewIdFn = opts.getCurrentViewIdFn;
let categoriesBox = document.querySelector("categories-box");
categoriesBox.initialize();
categoriesBox.select(getCurrentViewIdFn());
AddonManagerListenerHandler.startup();
window.addEventListener(
"unload",

View File

@ -7,8 +7,9 @@
/* exported attachUpdateHandler, gBrowser, getBrowserElement,
* installAddonsFromFilePicker, isCorrectlySigned, isDisabledUnsigned,
* isPending, loadReleaseNotes, openOptionsInTab, promiseEvent,
* shouldShowPermissionsPrompt, showPermissionsPrompt */
* isDiscoverEnabled, isPending, loadReleaseNotes, openOptionsInTab,
* promiseEvent, shouldShowPermissionsPrompt, showPermissionsPrompt,
* PREF_UI_LASTCATEGORY */
const { AddonSettings } = ChromeUtils.import(
"resource://gre/modules/addons/AddonSettings.jsm"
@ -33,6 +34,37 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/Extension.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"XPINSTALL_ENABLED",
"xpinstall.enabled",
true
);
const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
function isDiscoverEnabled() {
if (
Services.prefs.getPrefType(PREF_DISCOVERURL) == Services.prefs.PREF_INVALID
) {
return false;
}
try {
if (!Services.prefs.getBoolPref(PREF_DISCOVER_ENABLED)) {
return false;
}
} catch (e) {}
if (!XPINSTALL_ENABLED) {
return false;
}
return true;
}
function getBrowserElement() {
return window.docShell.chromeEventHandler;
}

View File

@ -19,16 +19,7 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/AddonManager.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"XPINSTALL_ENABLED",
"xpinstall.enabled",
true
);
const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
var gViewDefault = "addons://discover/";
@ -1172,7 +1163,7 @@ const addonTypes = new Set([
]);
const htmlViewOpts = {
loadViewFn(view) {
let viewId = `addons://${view}`;
let viewId = view.startsWith("addons://") ? view : `addons://${view}`;
gViewController.loadView(viewId);
},
replaceWithDefaultViewFn() {
@ -1183,6 +1174,9 @@ const htmlViewOpts = {
gCategories.select(`addons://list/${name}`);
}
},
getCurrentViewIdFn() {
return gViewController.currentViewId;
},
};
// View wrappers for the HTML version of about:addons. These delegate to an

View File

@ -138,12 +138,16 @@ class ButtonGroup extends HTMLElement {
this.addEventListener("button-group:selected", this);
this.addEventListener("keydown", this);
this.addEventListener("mousedown", this);
document.addEventListener("keypress", this);
}
disconnectedCallback() {
this.observer.disconnect();
this.removeEventListener("button-group:selected", this);
this.removeEventListener("keydown", this);
this.removeEventListener("mousedown", this);
document.removeEventListener("keypress", this);
}
attributeChangedCallback(name, oldVal, newVal) {
@ -230,6 +234,7 @@ class ButtonGroup extends HTMLElement {
handleEvent(e) {
let { previousKey, nextKey } = this._navigationKeys();
if (e.type == "keydown" && (e.key == previousKey || e.key == nextKey)) {
this.setAttribute("last-input-type", "keyboard");
e.preventDefault();
let oldFocus = this.activeChild;
this.walker.currentNode = oldFocus;
@ -244,6 +249,10 @@ class ButtonGroup extends HTMLElement {
}
} else if (e.type == "button-group:selected") {
this.activeChild = e.target;
} else if (e.type == "mousedown") {
this.setAttribute("last-input-type", "mouse");
} else if (e.type == "keypress" && e.key == "Tab") {
this.setAttribute("last-input-type", "keyboard");
}
}

View File

@ -5,6 +5,8 @@
function setupPanel(win) {
let doc = win.document;
// Clear out the other elements so only our test content is on the page.
doc.body.textContent = "";
let panelList = doc.createElement("panel-list");
let items = ["one", "two", "three"];
let panelItems = items.map(item => {

View File

@ -55,19 +55,35 @@ add_task(async function testClickingSidebarEntriesChangesView() {
add_task(async function testClickingSidebarPaddingNoChange() {
let win = await loadInitialView("theme");
let { managerWindow } = win;
let categoryUtils = new CategoryUtilities(managerWindow);
let themeCategory = categoryUtils.get("theme");
let loadDetailView = async () => {
let loaded = waitForViewLoad(win);
getAddonCard(win, THEME_ID).click();
await loaded;
is(
managerWindow.gViewController.currentViewId,
`addons://detail/${THEME_ID}`,
"The detail view loaded"
);
};
// Confirm that clicking the button directly works.
await loadDetailView();
let loaded = waitForViewLoad(win);
getAddonCard(win, THEME_ID).click();
EventUtils.synthesizeMouseAtCenter(themeCategory, {}, win);
await loaded;
ok(
managerWindow.gViewController.currentViewId.startsWith("addons://detail/"),
is(
managerWindow.gViewController.currentViewId,
`addons://list/theme`,
"The detail view loaded"
);
let themeCategory = managerWindow.document.getElementById("category-theme");
EventUtils.synthesizeMouse(themeCategory, -5, -5, {}, managerWindow);
// Confirm that clicking on the padding beside it does nothing.
await loadDetailView();
EventUtils.synthesizeMouse(themeCategory, -5, -5, {}, win);
ok(!managerWindow.gViewController.isLoading, "No view is loading");
await closeView(win);

View File

@ -50,6 +50,7 @@
--in-content-table-border-dark-color: #d1d1d1;
--in-content-table-header-background: #0a84ff;
--in-content-dialog-header-background: #f1f1f1;
--in-content-sidebar-width: 240px;
--panel-border-radius: 2px; /* This is overridden on Windows */
@ -674,10 +675,15 @@ xul|*.radio-label-box {
background-color: initial; /* override the background-color set on all richlistboxes in common.inc.css */
margin: 70px 0 0;
border-width: 0;
width: 240px;
width: var(--in-content-sidebar-width);
box-shadow: none;
}
html|*#categories {
box-sizing: border-box;
padding: 1px;
}
*|*#categories > *|*.category {
min-height: 48px;
-moz-appearance: none;
@ -687,6 +693,25 @@ xul|*.radio-label-box {
transition: background-color 150ms;
}
html|*#categories > html|*.category {
border: 0;
background-color: initial;
background-size: 24px;
background-repeat: no-repeat;
background-position-x: 10px;
background-position-y: 12px;
margin-inline-end: 0;
min-width: auto;
padding-inline-start: 34px;
text-align: start;
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
}
html|*#categories > html|*.category:dir(rtl) {
background-position-x: right 10px;
}
*|*#categories > *|*.category:hover {
background-color: var(--in-content-category-background-hover);
border-radius: 2px;
@ -698,7 +723,7 @@ xul|*.radio-label-box {
*|*#categories > *|*.category[selected],
*|*#categories > *|*.category.selected {
color: var(--in-content-category-text-selected);
color: var(--in-content-category-text-selected) !important;
background-color: transparent;
}
@ -709,7 +734,7 @@ xul|*.radio-label-box {
*|*#categories > *|*.category[selected]:hover:active,
*|*#categories > *|*.category.selected:hover:active {
color: var(--in-content-category-text-selected-active);
color: var(--in-content-category-text-selected-active) !important;
background-color: var(--in-content-category-background-selected-active);
}
@ -717,10 +742,14 @@ xul|*.radio-label-box {
outline: var(--in-content-category-outline-focus);
}
html|*#categories[last-input-type="mouse"] > html|button.category:-moz-focusring {
outline: none;
box-shadow: none;
}
*|*.category-name {
font-size: 1.07em;
line-height: 1.4em;
padding-bottom: 2px;
padding-inline-start: 9px;
margin: 0;
-moz-user-select: none;
@ -739,19 +768,32 @@ xul|*.radio-label-box {
}
@media (max-width: 830px) {
#categories {
width: 118px;
:root {
--in-content-sidebar-width: 118px;
}
html|*.category:not(.category-no-icon) > html|*.category-name,
.category-icon + .category-name {
display: none;
}
#categories > .category {
padding-inline-start: 13px; /* make category icons align center */
padding-inline-start: 12px; /* make category icons align center */
margin-inline-end: 33px;
}
html|*#categories > html|*.category {
width: 48px;
min-width: auto;
box-sizing: border-box;
}
html|*#categories > html|*.category,
/* We need to override the full-width RTL rule, so explicitly specify RTL. */
html|*#categories > html|*.category:dir(rtl) {
background-position: center;
}
.main-content {
padding-left: 0;
padding-right: 0;
@ -997,10 +1039,14 @@ input[type="text"][warning]:enabled:not(:focus) {
transform: scaleX(-1);
}
.sidebar-footer-button {
padding: 1px; /* Adding padding around help label in order to make entire keyboard focusing outline visible */
.sidebar-footer-list {
list-style-type: none;
margin-block: 0 36px;
margin-inline: 34px 0;
padding: 0;
}
.sidebar-footer-link,
.sidebar-footer-button > .text-link {
-moz-box-flex: 1;
margin-inline-start: 34px;
@ -1012,15 +1058,49 @@ input[type="text"][warning]:enabled:not(:focus) {
cursor: default;
}
.sidebar-footer-link {
display: flex;
align-items: center;
background-size: 16px;
background-repeat: no-repeat;
background-position-x: 13px;
background-position-y: 10px;
cursor: default;
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
padding-inline-start: 43px;
/* These override the common styles. */
margin: 0;
font-size: .9em;
width: auto;
}
.sidebar-footer-link:dir(rtl) {
background-position-x: right 13px;
}
.sidebar-footer-link,
.sidebar-footer-link:hover,
.sidebar-footer-link:hover:active,
.sidebar-footer-link:visited {
text-decoration: none;
color: currentColor;
}
.sidebar-footer-link:hover,
.sidebar-footer-button > .text-link:hover {
background-color: var(--in-content-category-background-hover);
border-radius: 2px;
}
.sidebar-footer-link:hover:active:not([disabled]),
.sidebar-footer-button > .text-link:hover:active:not([disabled]) {
background-color: var(--in-content-category-background-active);
color: currentColor;
}
.sidebar-footer-link:-moz-focusring,
.sidebar-footer-button > .text-link:-moz-focusring {
outline: var(--in-content-category-outline-focus);
}
@ -1043,12 +1123,16 @@ input[type="text"][warning]:enabled:not(:focus) {
margin-bottom: 36px;
}
.help-icon {
xul|*.help-icon {
list-style-image: url("chrome://global/skin/icons/help.svg");
}
html|*.help-icon {
background-image: url("chrome://global/skin/icons/help.svg");
}
.preferences-icon {
list-style-image: url("chrome://browser/skin/preferences/in-content/general.svg");
background-image: url("chrome://browser/skin/preferences/in-content/general.svg");
}
.addons-icon {
@ -1080,6 +1164,27 @@ input[type="text"][warning]:enabled:not(:focus) {
.sidebar-footer-label {
display: none;
}
.sidebar-footer-list {
margin-inline-start: 40px;
}
.sidebar-footer-link {
width: 36px;
height: 36px;
padding-inline-start: 0;
margin-inline-start: 1px;
}
.sidebar-footer-link,
/* We need to override the full-width RTL rule, so explicitly specify RTL. */
.sidebar-footer-link:dir(rtl) {
background-position: center;
}
.sidebar-footer-link-text {
display: none;
}
}
.back-button {