mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-12 15:02:11 +00:00
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:
parent
1a49c36c2c
commit
21205b994f
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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')
|
||||
)
|
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user