Bug 1657476 support multiple recommendation badges r=fluent-reviewers,rpl,flod

Differential Revision: https://phabricator.services.mozilla.com/D86124
This commit is contained in:
Shane Caraveo 2020-08-31 21:35:58 +00:00
Родитель f339ca9f42
Коммит fb21940df8
11 изменённых файлов: 308 добавлений и 56 удалений

Просмотреть файл

@ -457,11 +457,20 @@ addon-detail-private-browsing-help = When allowed, the extension will have acces
addon-detail-private-browsing-allow = Allow
addon-detail-private-browsing-disallow = Dont Allow
# This is the tooltip text for the recommended badge for an extension in about:addons. The
# badge is a small icon displayed next to an extension when it is recommended on AMO.
## This is the tooltip text for the recommended badges for an extension in about:addons. The
## badge is a small icon displayed next to an extension when it is recommended on AMO.
addon-badge-recommended2 =
.title = { -brand-product-name } only recommends extensions that meet our standards for security and performance
.aria-label = { addon-badge-recommended2.title }
addon-badge-line =
.title = This extension was created by the makers of { -brand-product-name }
.aria-label = { addon-badge-line.title }
addon-badge-verified =
.title = This extension has been code-reviewed for safety
.aria-label = { addon-badge-verified.title }
##
available-updates-heading = Available Updates
recent-updates-heading = Recent Updates

Просмотреть файл

@ -315,12 +315,14 @@ addon-card[expanded] .update-postponed-bar + .addon-card-message {
.addon-badge {
display: inline-block;
margin-inline-end: 8px;
width: 16px;
height: 16px;
background-size: 16px;
width: 22px;
height: 22px;
background-repeat: no-repeat;
background-position: center;
flex-shrink: 0;
border-radius: 11px;
-moz-context-properties: fill;
fill: #fff;
}
.addon-badge-private-browsing-allowed {
@ -330,10 +332,21 @@ addon-card[expanded] .update-postponed-bar + .addon-card-message {
.addon-badge-recommended {
background-color: var(--orange-50);
background-image: url("chrome://mozapps/skin/extensions/recommended.svg");
background-size: 10px;
border-radius: 8px;
fill: #fff;
-moz-context-properties: fill;
}
.addon-badge-line {
background-color: #fff;
background-image: url("chrome://mozapps/skin/extensions/line.svg");
background-size: 16px;
border-radius: 10px;
border: 1px solid #CFCFD8;
width: 20px;
height: 20px;
}
.addon-badge-verified {
background-color: var(--green-70);
background-image: url("chrome://mozapps/skin/extensions/check.svg");
}
.theme-enable-button {

Просмотреть файл

@ -133,6 +133,18 @@
data-l10n-id="addon-badge-recommended2"
hidden>
</a>
<a class="addon-badge addon-badge-line"
is="support-link"
support-page="recommended-extensions-program"
data-l10n-id="addon-badge-line"
hidden>
</a>
<a class="addon-badge addon-badge-verified"
is="support-link"
support-page="recommended-extensions-program"
data-l10n-id="addon-badge-verified"
hidden>
</a>
<a class="addon-badge addon-badge-private-browsing-allowed"
is="support-link"
support-page="extensions-pb"

Просмотреть файл

@ -3158,6 +3158,11 @@ class AddonCard extends HTMLElement {
// Hide the more options button if it's empty.
moreOptionsButton.hidden = this.options.visibleItems.length === 0;
// Ensure all badges are initially hidden.
for (let node of card.querySelectorAll(".addon-badge")) {
node.hidden = true;
}
// Set the private browsing badge visibility.
if (
!allowPrivateBrowsingByDefault &&
@ -3172,10 +3177,15 @@ class AddonCard extends HTMLElement {
});
}
// Show the recommended badge if needed.
card.querySelector(
".addon-badge-recommended"
).hidden = !addon.isRecommended;
// Show the recommended badges if needed.
// Plugins don't have recommendationStates, so ensure a default.
let states = addon.recommendationStates || [];
for (let badgeName of states) {
let badge = card.querySelector(`.addon-badge-${badgeName}`);
if (badge) {
badge.hidden = false;
}
}
// Update description.
card.querySelector(".addon-description").textContent = addon.description;

Просмотреть файл

@ -936,7 +936,7 @@ AddonWrapper = class {
return null;
}
get isRecommended() {
get recommendationStates() {
let addon = addonFor(this);
let state = addon.recommendationState;
if (
@ -946,9 +946,13 @@ AddonWrapper = class {
addon.isCorrectlySigned &&
!this.temporarilyInstalled
) {
return state.states.includes("recommended");
return state.states;
}
return false;
return [];
}
get isRecommended() {
return this.recommendationStates.includes("recommended");
}
get applyBackgroundUpdates() {

Просмотреть файл

@ -868,8 +868,8 @@ add_task(async function testPrivateBrowsingExtension() {
await updated;
// It's still allowed in PB.
ok(!badge.hidden, "The PB badge is shown");
ok(await hasPrivateAllowed(id), "PB is allowed");
ok(!badge.hidden, "The PB badge is shown");
// Disallow PB.
updated = BrowserTestUtils.waitForEvent(card, "update");

Просмотреть файл

@ -8,17 +8,33 @@ const SUPPORT_URL = Services.urlFormatter.formatURL(
Services.prefs.getStringPref("app.support.baseURL")
);
const SUMO_URL = SUPPORT_URL + "recommended-extensions-program";
const SUPPORTED_BADGES = ["recommended", "line", "verified"];
async function checkRecommendedBadge(id, hidden) {
async function checkRecommendedBadge(id, badges = []) {
async function checkBadge() {
let card = win.document.querySelector(`addon-card[addon-id="${id}"]`);
let badge = card.querySelector(".addon-badge-recommended");
is(badge.hidden, hidden, `badge is ${hidden ? "hidden" : "shown"}`);
if (!hidden) {
info("Verify the badge links to the support page");
let tabLoaded = BrowserTestUtils.waitForNewTab(gBrowser, SUMO_URL);
EventUtils.synthesizeMouseAtCenter(badge, {}, win);
BrowserTestUtils.removeTab(await tabLoaded);
for (let badgeName of SUPPORTED_BADGES) {
let badge = card.querySelector(`.addon-badge-${badgeName}`);
let hidden = !badges.includes(badgeName);
is(
badge.hidden,
hidden,
`badge ${badgeName} is ${hidden ? "hidden" : "shown"}`
);
if (!hidden) {
info(`Verify the ${badgeName} badge links to the support page`);
let tabLoaded = BrowserTestUtils.waitForNewTab(gBrowser, SUMO_URL);
EventUtils.synthesizeMouseAtCenter(badge, {}, win);
BrowserTestUtils.removeTab(await tabLoaded);
}
}
for (let badgeName of badges) {
if (!SUPPORTED_BADGES.includes(badgeName)) {
ok(
!card.querySelector(`.addon-badge-${badgeName}`),
`no badge element for ${badgeName}`
);
}
}
return card;
}
@ -47,22 +63,65 @@ add_task(async function testNotRecommended() {
});
await extension.startup();
await checkRecommendedBadge(id, true);
await checkRecommendedBadge(id);
await extension.unload();
});
add_task(async function testRecommended() {
let id = "recommended@mochi.test";
async function test_badged_addon(addon) {
let provider = new MockProvider();
provider.createAddons([
{
id,
isRecommended: true,
name: "Recommended",
type: "extension",
},
]);
provider.createAddons([addon]);
await checkRecommendedBadge(addon.id, addon.recommendationStates);
await checkRecommendedBadge(id, false);
provider.unregister();
}
add_task(async function testRecommended() {
await test_badged_addon({
id: "recommended@mochi.test",
isRecommended: true,
recommendationStates: ["recommended"],
name: "Recommended",
type: "extension",
});
});
add_task(async function testLine() {
await test_badged_addon({
id: "line@mochi.test",
isRecommended: false,
recommendationStates: ["line"],
name: "Line",
type: "extension",
});
});
add_task(async function testVerified() {
await test_badged_addon({
id: "verified@mochi.test",
isRecommended: false,
recommendationStates: ["verified"],
name: "Verified",
type: "extension",
});
});
add_task(async function testOther() {
await test_badged_addon({
id: "other@mochi.test",
isRecommended: false,
recommendationStates: ["other"],
name: "No Badge",
type: "extension",
});
});
add_task(async function testMultiple() {
await test_badged_addon({
id: "multiple@mochi.test",
isRecommended: false,
recommendationStates: ["verified", "recommended", "other"],
name: "Multiple",
type: "extension",
});
});

Просмотреть файл

@ -34,6 +34,19 @@ async function installAddonWithRecommendations(id, recommendation) {
return install.addon;
}
function checkRecommended(addon, recommended = true) {
equal(
addon.isRecommended,
recommended,
"The add-on isRecommended state is correct"
);
equal(
addon.recommendationStates.includes("recommended"),
recommended,
"The add-on recommendationStates is correct"
);
}
add_task(async function setup() {
await ExtensionTestUtils.startAddonManager();
});
@ -42,7 +55,7 @@ add_task(async function text_no_file() {
const id = "no-recommendations-file@test.web.extension";
let addon = await installAddonWithRecommendations(id, null);
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -51,7 +64,7 @@ add_task(async function text_malformed_file() {
const id = "no-recommendations-file@test.web.extension";
let addon = await installAddonWithRecommendations(id, "This is not JSON");
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -64,7 +77,24 @@ add_task(async function test_valid_recommendation_file() {
validity: { not_before, not_after },
});
ok(addon.isRecommended, "The add-on is recommended");
checkRecommended(addon);
await addon.uninstall();
});
add_task(async function test_multiple_valid_recommendation_file() {
const id = "recommended@test.web.extension";
let addon = await installAddonWithRecommendations(id, {
addon_id: id,
states: ["recommended", "something"],
validity: { not_before, not_after },
});
checkRecommended(addon);
ok(
addon.recommendationStates.includes("something"),
"The add-on recommendationStates contains something"
);
await addon.uninstall();
});
@ -82,7 +112,7 @@ add_task(async function test_unsigned() {
validity: { not_before, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
AddonTestUtils.useRealCertChecks = false;
@ -98,7 +128,7 @@ add_task(async function test_temporary() {
});
let addon = await XPIInstall.installTemporaryAddon(xpi);
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -127,7 +157,7 @@ add_task(async function test_temporary_directory() {
let addon = await XPIInstall.installTemporaryAddon(extDir);
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
extDir.remove(true);
@ -150,7 +180,7 @@ add_task(async function test_builtin() {
});
await extension.awaitMessage("started");
ok(!extension.addon.isRecommended, "The add-on is not recommended");
checkRecommended(extension.addon, false);
await extension.unload();
});
@ -172,7 +202,7 @@ add_task(async function test_theme() {
});
let { addon } = await AddonTestUtils.promiseInstallFile(xpi);
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -185,7 +215,11 @@ add_task(async function test_not_recommended() {
validity: { not_before, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
ok(
addon.recommendationStates.includes("something"),
"The add-on recommendationStates contains something"
);
await addon.uninstall();
});
@ -197,7 +231,7 @@ add_task(async function test_id_missing() {
validity: { not_before, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -206,11 +240,15 @@ add_task(async function test_expired() {
const id = "expired@test.web.extension";
let addon = await installAddonWithRecommendations(id, {
addon_id: id,
states: ["recommended"],
states: ["recommended", "something"],
validity: { not_before, not_after: not_before },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
ok(
!addon.recommendationStates.length,
"The add-on recommendationStates does not contain anything"
);
await addon.uninstall();
});
@ -223,7 +261,7 @@ add_task(async function test_not_valid_yet() {
validity: { not_before: not_after, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -235,7 +273,7 @@ add_task(async function test_states_missing() {
validity: { not_before, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -247,7 +285,7 @@ add_task(async function test_validity_missing() {
states: ["recommended"],
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -260,7 +298,7 @@ add_task(async function test_not_before_missing() {
validity: { not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -273,7 +311,7 @@ add_task(async function test_bad_states() {
validity: { not_before, not_after },
});
ok(!addon.isRecommended, "The add-on is not recommended");
checkRecommended(addon, false);
await addon.uninstall();
});
@ -286,13 +324,13 @@ add_task(async function test_recommendation_persist_restart() {
validity: { not_before, not_after },
});
ok(addon.isRecommended, "The add-on is recommended");
checkRecommended(addon);
await AddonTestUtils.promiseRestartManager();
addon = await AddonManager.getAddonByID(id);
ok(addon.isRecommended, "The add-on is still recommended");
checkRecommended(addon);
await addon.uninstall();
});

Просмотреть файл

@ -0,0 +1,6 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="context-fill" fill-opacity="context-fill-opacity" stroke="context-stroke none" stroke-width="0.5" d="M6 14a1 1 0 0 1-.707-.293l-3-3a1 1 0 0 1 1.414-1.414l2.157 2.157 6.316-9.023a1 1 0 0 1 1.639 1.146l-7 10a1 1 0 0 1-.732.427A.863.863 0 0 1 6 14z"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 566 B

Просмотреть файл

@ -0,0 +1,99 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<defs>
<radialGradient id="a" cx="14.305" cy="3.031" r="18.199" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff36e"/>
<stop offset=".5" stop-color="#fc4055"/>
<stop offset="1" stop-color="#e31587"/>
</radialGradient>
<radialGradient id="b" cx="1.315" cy="3.784" r="10.76" gradientUnits="userSpaceOnUse">
<stop offset=".001" stop-color="#c60084"/>
<stop offset="1" stop-color="#fc4055" stop-opacity="0"/>
</radialGradient>
<radialGradient id="c" cx="15.858" cy="1.995" r="21.371" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ffde67" stop-opacity=".6"/>
<stop offset=".093" stop-color="#ffd966" stop-opacity=".581"/>
<stop offset=".203" stop-color="#ffca65" stop-opacity=".525"/>
<stop offset=".321" stop-color="#feb262" stop-opacity=".432"/>
<stop offset=".446" stop-color="#fe8f5e" stop-opacity=".302"/>
<stop offset=".573" stop-color="#fd6459" stop-opacity=".137"/>
<stop offset=".664" stop-color="#fc4055" stop-opacity="0"/>
</radialGradient>
<radialGradient id="d" cx="8.451" cy="8.902" r="27.546" gradientUnits="userSpaceOnUse">
<stop offset=".153" stop-color="#810220"/>
<stop offset=".167" stop-color="#920b27" stop-opacity=".861"/>
<stop offset=".216" stop-color="#cb2740" stop-opacity=".398"/>
<stop offset=".253" stop-color="#ef394f" stop-opacity=".11"/>
<stop offset=".272" stop-color="#fc4055" stop-opacity="0"/>
</radialGradient>
<radialGradient id="e" cx="6.368" cy="8.555" r="27.542" gradientUnits="userSpaceOnUse">
<stop offset=".113" stop-color="#810220"/>
<stop offset=".133" stop-color="#920b27" stop-opacity=".861"/>
<stop offset=".204" stop-color="#cb2740" stop-opacity=".398"/>
<stop offset=".257" stop-color="#ef394f" stop-opacity=".11"/>
<stop offset=".284" stop-color="#fc4055" stop-opacity="0"/>
</radialGradient>
<radialGradient id="f" cx="13.937" cy="2.416" r="17.079" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ff9640"/>
<stop offset=".8" stop-color="#fc4055"/>
</radialGradient>
<radialGradient id="g" cx="13.937" cy="2.416" r="17.079" gradientUnits="userSpaceOnUse">
<stop offset=".084" stop-color="#ffde67"/>
<stop offset=".147" stop-color="#ffdc66" stop-opacity=".968"/>
<stop offset=".246" stop-color="#ffd562" stop-opacity=".879"/>
<stop offset=".369" stop-color="#ffcb5d" stop-opacity=".734"/>
<stop offset=".511" stop-color="#ffbc55" stop-opacity=".533"/>
<stop offset=".667" stop-color="#ffaa4b" stop-opacity=".28"/>
<stop offset=".822" stop-color="#ff9640" stop-opacity="0"/>
</radialGradient>
<radialGradient id="h" cx="10.011" cy="7.729" r="8.36" gradientTransform="matrix(.247 .969 -1.011 .258 15.352 -3.965)" gradientUnits="userSpaceOnUse">
<stop offset=".363" stop-color="#fc4055"/>
<stop offset=".443" stop-color="#fd604d" stop-opacity=".633"/>
<stop offset=".545" stop-color="#fe8644" stop-opacity=".181"/>
<stop offset=".59" stop-color="#ff9640" stop-opacity="0"/>
</radialGradient>
<radialGradient id="i" cx="8.575" cy="8.439" r="8.353" gradientUnits="userSpaceOnUse">
<stop offset=".216" stop-color="#fc4055" stop-opacity=".8"/>
<stop offset=".267" stop-color="#fd5251" stop-opacity=".633"/>
<stop offset=".41" stop-color="#fe8345" stop-opacity=".181"/>
<stop offset=".474" stop-color="#ff9640" stop-opacity="0"/>
</radialGradient>
<radialGradient id="j" cx="17.326" cy=".487" r="28.887" gradientUnits="userSpaceOnUse">
<stop offset=".054" stop-color="#fff36e"/>
<stop offset=".457" stop-color="#ff9640"/>
<stop offset=".639" stop-color="#ff9640"/>
</radialGradient>
<linearGradient id="k" x1="8.117" y1="-.134" x2="12.46" y2="12.441" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#b833e1"/>
<stop offset=".371" stop-color="#9059ff"/>
<stop offset=".614" stop-color="#5b6df8"/>
<stop offset="1" stop-color="#0090ed"/>
</linearGradient>
<linearGradient id="l" x1="5.542" y1=".065" x2="13.614" y2="8.137" gradientUnits="userSpaceOnUse">
<stop offset=".805" stop-color="#722291" stop-opacity="0"/>
<stop offset="1" stop-color="#592acb" stop-opacity=".5"/>
</linearGradient>
<linearGradient id="m" x1="11.836" y1="1.378" x2="3.632" y2="15.587" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#fff36e" stop-opacity=".8"/>
<stop offset=".094" stop-color="#fff36e" stop-opacity=".699"/>
<stop offset=".752" stop-color="#fff36e" stop-opacity="0"/>
</linearGradient>
</defs>
<g style="isolation:isolate">
<path d="M14.389 3.14A7.894 7.894 0 0 0 8.318 0a7 7 0 0 0-3.867.97A7.472 7.472 0 0 1 8.059.1a7.1 7.1 0 0 1 7.071 6.087 7 7 0 0 1-6.9 8.2 7.151 7.151 0 0 1-3.949-1.127C1.268 11.3 1.258 7.441 1.26 6.825a8.626 8.626 0 0 1 1.189-4.266A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189z" fill="url(#a)"/>
<path d="M14.389 3.14A7.894 7.894 0 0 0 8.318 0a7 7 0 0 0-3.867.97A7.472 7.472 0 0 1 8.059.1a7.1 7.1 0 0 1 7.071 6.087 7 7 0 0 1-6.9 8.2 7.151 7.151 0 0 1-3.949-1.127C1.268 11.3 1.258 7.441 1.26 6.825a8.626 8.626 0 0 1 1.189-4.266A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189z" fill="url(#b)" opacity=".67"/>
<path d="M14.389 3.14A7.894 7.894 0 0 0 8.318 0a7 7 0 0 0-3.867.97A7.472 7.472 0 0 1 8.059.1a7.1 7.1 0 0 1 7.071 6.087 7 7 0 0 1-6.9 8.2 7.151 7.151 0 0 1-3.949-1.127C1.268 11.3 1.258 7.441 1.26 6.825a8.626 8.626 0 0 1 1.189-4.266A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189z" fill="url(#c)"/>
<path d="M14.389 3.14A7.894 7.894 0 0 0 8.318 0a7 7 0 0 0-3.867.97A7.472 7.472 0 0 1 8.059.1a7.1 7.1 0 0 1 7.071 6.087 7 7 0 0 1-6.9 8.2 7.151 7.151 0 0 1-3.949-1.127C1.268 11.3 1.258 7.441 1.26 6.825a8.626 8.626 0 0 1 1.189-4.266A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189z" fill="url(#d)"/>
<path d="M14.389 3.14A7.894 7.894 0 0 0 8.318 0a7 7 0 0 0-3.867.97A7.472 7.472 0 0 1 8.059.1a7.1 7.1 0 0 1 7.071 6.087 7 7 0 0 1-6.9 8.2 7.151 7.151 0 0 1-3.949-1.127C1.268 11.3 1.258 7.441 1.26 6.825a8.626 8.626 0 0 1 1.189-4.266A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189z" fill="url(#e)"/>
<path d="M15.325 5.965C14.875 1.9 11.253.078 8.059.1a7.765 7.765 0 0 0-3.608.872 3.913 3.913 0 0 0-.712.54c.026-.021.1-.085.23-.172l.013-.009.011-.007A5.337 5.337 0 0 1 5.531.609 8.713 8.713 0 0 1 8.168.3a6.65 6.65 0 0 1 6.25 6.4 4.818 4.818 0 0 1-4.58 4.869 4.731 4.731 0 0 1-2.967-.72A5.425 5.425 0 0 1 5.06 8.242a4.552 4.552 0 0 1 .285-3.149A4.726 4.726 0 0 1 8.464 2.7a4.3 4.3 0 0 0-1.782-.585A5.4 5.4 0 0 0 1.7 5.177a6.035 6.035 0 0 0-.2 4.638 6.683 6.683 0 0 0 2.4 3.234A7.177 7.177 0 0 0 7.326 14.4s.153.018.309.029a8.085 8.085 0 0 0 5.439-1.6c2.811-2.377 2.315-6.285 2.251-6.864z" fill="url(#f)"/>
<path d="M15.325 5.965C14.875 1.9 11.253.078 8.059.1a7.765 7.765 0 0 0-3.608.872 3.913 3.913 0 0 0-.712.54c.026-.021.1-.085.23-.172l.013-.009.011-.007A5.337 5.337 0 0 1 5.531.609 8.713 8.713 0 0 1 8.168.3a6.65 6.65 0 0 1 6.25 6.4 4.818 4.818 0 0 1-4.58 4.869 4.731 4.731 0 0 1-2.967-.72A5.425 5.425 0 0 1 5.06 8.242a4.552 4.552 0 0 1 .285-3.149A4.726 4.726 0 0 1 8.464 2.7a4.3 4.3 0 0 0-1.782-.585A5.4 5.4 0 0 0 1.7 5.177a6.035 6.035 0 0 0-.2 4.638 6.683 6.683 0 0 0 2.4 3.234A7.177 7.177 0 0 0 7.326 14.4s.153.018.309.029a8.085 8.085 0 0 0 5.439-1.6c2.811-2.377 2.315-6.285 2.251-6.864z" fill="url(#g)"/>
<path d="M15.325 5.965C14.875 1.9 11.253.078 8.059.1a7.765 7.765 0 0 0-3.608.872 3.913 3.913 0 0 0-.712.54c.026-.021.1-.085.23-.172l.013-.009.011-.007A5.337 5.337 0 0 1 5.531.609 8.713 8.713 0 0 1 8.168.3a6.65 6.65 0 0 1 6.25 6.4 4.818 4.818 0 0 1-4.58 4.869 4.731 4.731 0 0 1-2.967-.72A5.425 5.425 0 0 1 5.06 8.242a4.552 4.552 0 0 1 .285-3.149A4.726 4.726 0 0 1 8.464 2.7a4.3 4.3 0 0 0-1.782-.585A5.4 5.4 0 0 0 1.7 5.177a6.035 6.035 0 0 0-.2 4.638 6.683 6.683 0 0 0 2.4 3.234A7.177 7.177 0 0 0 7.326 14.4s.153.018.309.029a8.085 8.085 0 0 0 5.439-1.6c2.811-2.377 2.315-6.285 2.251-6.864z" style="mix-blend-mode:multiply" opacity=".53" fill="url(#h)"/>
<path d="M15.325 5.965C14.875 1.9 11.253.078 8.059.1a7.765 7.765 0 0 0-3.608.872 3.913 3.913 0 0 0-.712.54c.026-.021.1-.085.23-.172l.013-.009.011-.007A5.337 5.337 0 0 1 5.531.609 8.713 8.713 0 0 1 8.168.3a6.65 6.65 0 0 1 6.25 6.4 4.818 4.818 0 0 1-4.58 4.869 4.731 4.731 0 0 1-2.967-.72A5.425 5.425 0 0 1 5.06 8.242a4.552 4.552 0 0 1 .285-3.149A4.726 4.726 0 0 1 8.464 2.7a4.3 4.3 0 0 0-1.782-.585A5.4 5.4 0 0 0 1.7 5.177a6.035 6.035 0 0 0-.2 4.638 6.683 6.683 0 0 0 2.4 3.234A7.177 7.177 0 0 0 7.326 14.4s.153.018.309.029a8.085 8.085 0 0 0 5.439-1.6c2.811-2.377 2.315-6.285 2.251-6.864z" style="mix-blend-mode:multiply" opacity=".53" fill="url(#i)"/>
<path d="M9.24 11.568a5.148 5.148 0 0 0 3.183-.815 5.67 5.67 0 0 0 2.39-4.234C14.957 3.381 13.094 0 8.168.3a8.713 8.713 0 0 0-2.637.309 5.745 5.745 0 0 0-1.538.715l-.011.007-.013.009c-.076.054-.151.11-.224.168a6.7 6.7 0 0 1 4.2-.787c2.827.371 5.413 2.571 5.413 5.475a4.076 4.076 0 0 1-3.747 3.817A2.849 2.849 0 0 1 6.9 8.156a2.75 2.75 0 0 1 .919-2.729 2.875 2.875 0 0 0-1.81.919A3.07 3.07 0 0 0 5.735 9.6c.84 1.746 3.031 1.929 3.505 1.968z" fill="url(#j)"/>
<path d="M14.4 3.745a4.5 4.5 0 0 0-.976-1.629 6.056 6.056 0 0 0-1.819-1.3A8.086 8.086 0 0 0 9.82.184 7.96 7.96 0 0 0 6.507.165a5.727 5.727 0 0 0-2.768 1.346 6.415 6.415 0 0 1 1.606-.64 6.712 6.712 0 0 1 6.234 1.619 5.417 5.417 0 0 1 .866 1.061 4.693 4.693 0 0 1 .123 4.773 3.8 3.8 0 0 1-2.914 1.691A4.726 4.726 0 0 0 14 7.839a4.88 4.88 0 0 0 .4-4.094z" fill="url(#k)"/>
<path d="M14.4 3.745a4.5 4.5 0 0 0-.976-1.629 6.056 6.056 0 0 0-1.819-1.3A8.086 8.086 0 0 0 9.82.184 7.96 7.96 0 0 0 6.507.165a5.727 5.727 0 0 0-2.768 1.346 6.415 6.415 0 0 1 1.606-.64 6.712 6.712 0 0 1 6.234 1.619 5.417 5.417 0 0 1 .866 1.061 4.693 4.693 0 0 1 .123 4.773 3.8 3.8 0 0 1-2.914 1.691A4.726 4.726 0 0 0 14 7.839a4.88 4.88 0 0 0 .4-4.094z" fill="url(#l)"/>
<path d="M8.318 0h-.073c.134 0 .269.013.4.022C8.538.021 8.429 0 8.318 0zM3.747 1.5a3.951 3.951 0 0 1 .453-.359 3.547 3.547 0 0 0-.453.364zm1.7-.653c-.032.008-.066.01-.1.019-.07.022-.147.046-.223.068.101-.029.209-.056.319-.082zm-1.7.658zm0 0zM14.389 3.14a8.12 8.12 0 0 0-.675-.77 6.368 6.368 0 0 0-.747-.677c-.072-.063-.156-.116-.233-.176a5.136 5.136 0 0 1 .693.6 4.5 4.5 0 0 1 .973 1.628 4.88 4.88 0 0 1-.4 4.094 4.723 4.723 0 0 1-4.342 2.175 2.609 2.609 0 0 0 .578-.07 2.81 2.81 0 0 1-.625.069A2.849 2.849 0 0 1 6.9 8.156a2.749 2.749 0 0 1 .919-2.729 2.875 2.875 0 0 0-1.81.919A3.07 3.07 0 0 0 5.735 9.6c.03.062.073.109.107.167a4.744 4.744 0 0 1-.782-1.525 4.552 4.552 0 0 1 .285-3.149A4.726 4.726 0 0 1 8.464 2.7a4.3 4.3 0 0 0-1.782-.585A5.4 5.4 0 0 0 1.7 5.177a5.133 5.133 0 0 0-.414 1.17 8.715 8.715 0 0 1 1.163-3.788A6.656 6.656 0 0 0 .576 4.984a7.734 7.734 0 0 0-.519 4.035c.012.1.023.207.036.31a8.013 8.013 0 1 0 14.3-6.189zM4.541.923c-.026.016-.065.033-.09.049.011-.007.025-.011.036-.018s.037-.02.054-.031zm-.09.049c-.017.01-.028.019-.044.029.025-.016.053-.03.079-.046a.378.378 0 0 0-.035.017z" fill="url(#m)"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Просмотреть файл

@ -15,6 +15,8 @@
skin/classic/mozapps/extensions/category-available.svg (../../shared/extensions/category-available.svg)
skin/classic/mozapps/extensions/extension.svg (../../shared/extensions/extension.svg)
skin/classic/mozapps/extensions/recommended.svg (../../shared/extensions/recommended.svg)
skin/classic/mozapps/extensions/line.svg (../../shared/extensions/line.svg)
skin/classic/mozapps/extensions/check.svg (../../shared/extensions/check.svg)
#ifndef ANDROID
skin/classic/mozapps/extensions/rating-star.svg (../../shared/extensions/rating-star.svg)
#endif