Bug 1968920 - Add an internal -moz-symbolic-icon image that draws a symbolic icon respecting the context fill. r=stransky,desktop-theme-reviewers,dao, a=dmeehan
Factor out the nsIconChannel symbolic lookup, and expose it to nsImageRenderer to do the icon look-ups with the right CSS color. Differential Revision: https://phabricator.services.mozilla.com/D252791
This commit is contained in:
committed by
dmeehan@mozilla.com
parent
e1b2520d94
commit
db018846ae
@@ -52,6 +52,7 @@ module.exports = {
|
|||||||
ignoreFunctions: [
|
ignoreFunctions: [
|
||||||
"light-dark" /* Used for color-scheme dependent colors */,
|
"light-dark" /* Used for color-scheme dependent colors */,
|
||||||
"add" /* Used in mathml.css */,
|
"add" /* Used in mathml.css */,
|
||||||
|
"-moz-symbolic-icon" /* Used for GTK icons */,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
|
|
||||||
.titlebar-button {
|
.titlebar-button {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
color: inherit;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-inline: calc(env(-moz-gtk-csd-titlebar-button-spacing) / 2);
|
padding-inline: calc(env(-moz-gtk-csd-titlebar-button-spacing) / 2);
|
||||||
@@ -205,32 +206,14 @@
|
|||||||
.titlebar-max {
|
.titlebar-max {
|
||||||
order: env(-moz-gtk-csd-maximize-button-position);
|
order: env(-moz-gtk-csd-maximize-button-position);
|
||||||
> .toolbarbutton-icon {
|
> .toolbarbutton-icon {
|
||||||
background-image: image-set(
|
background-image: -moz-symbolic-icon(window-maximize-symbolic);
|
||||||
url(moz-icon://stock/window-maximize-symbolic?dark=0),
|
|
||||||
url(moz-icon://stock/window-maximize-symbolic?dark=0&scale=2) 2x
|
|
||||||
);
|
|
||||||
toolbar[brighttext] & {
|
|
||||||
background-image: image-set(
|
|
||||||
url(moz-icon://stock/window-maximize-symbolic?dark=1),
|
|
||||||
url(moz-icon://stock/window-maximize-symbolic?dark=1&scale=2) 2x
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-restore {
|
.titlebar-restore {
|
||||||
order: env(-moz-gtk-csd-maximize-button-position);
|
order: env(-moz-gtk-csd-maximize-button-position);
|
||||||
> .toolbarbutton-icon {
|
> .toolbarbutton-icon {
|
||||||
background-image: image-set(
|
background-image: -moz-symbolic-icon(window-restore-symbolic);
|
||||||
url(moz-icon://stock/window-restore-symbolic?dark=0),
|
|
||||||
url(moz-icon://stock/window-restore-symbolic?dark=0&scale=2) 2x
|
|
||||||
);
|
|
||||||
toolbar[brighttext] & {
|
|
||||||
background-image: image-set(
|
|
||||||
url(moz-icon://stock/window-restore-symbolic?dark=1),
|
|
||||||
url(moz-icon://stock/window-restore-symbolic?dark=1&scale=2) 2x
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,16 +228,7 @@
|
|||||||
order: env(-moz-gtk-csd-close-button-position);
|
order: env(-moz-gtk-csd-close-button-position);
|
||||||
|
|
||||||
> .toolbarbutton-icon {
|
> .toolbarbutton-icon {
|
||||||
background-image: image-set(
|
background-image: -moz-symbolic-icon(window-close-symbolic);
|
||||||
url(moz-icon://stock/window-close-symbolic?dark=0),
|
|
||||||
url(moz-icon://stock/window-close-symbolic?dark=0&scale=2) 2x
|
|
||||||
);
|
|
||||||
toolbar[brighttext] & {
|
|
||||||
background-image: image-set(
|
|
||||||
url(moz-icon://stock/window-close-symbolic?dark=1),
|
|
||||||
url(moz-icon://stock/window-close-symbolic?dark=1&scale=2) 2x
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media not (-moz-gtk-csd-close-button) {
|
@media not (-moz-gtk-csd-close-button) {
|
||||||
@@ -266,16 +240,7 @@
|
|||||||
order: env(-moz-gtk-csd-minimize-button-position);
|
order: env(-moz-gtk-csd-minimize-button-position);
|
||||||
|
|
||||||
> .toolbarbutton-icon {
|
> .toolbarbutton-icon {
|
||||||
background-image: image-set(
|
background-image: -moz-symbolic-icon(window-minimize-symbolic);
|
||||||
url(moz-icon://stock/window-minimize-symbolic?dark=0),
|
|
||||||
url(moz-icon://stock/window-minimize-symbolic?dark=0&scale=2) 2x
|
|
||||||
);
|
|
||||||
toolbar[brighttext] & {
|
|
||||||
background-image: image-set(
|
|
||||||
url(moz-icon://stock/window-minimize-symbolic?dark=1),
|
|
||||||
url(moz-icon://stock/window-minimize-symbolic?dark=1&scale=2) 2x
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media not (-moz-gtk-csd-minimize-button) {
|
@media not (-moz-gtk-csd-minimize-button) {
|
||||||
|
|||||||
@@ -8,7 +8,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ErrorList.h"
|
||||||
#include "gdk/gdk.h"
|
#include "gdk/gdk.h"
|
||||||
|
#include "mozilla/AlreadyAddRefed.h"
|
||||||
|
#include "nsIIconURI.h"
|
||||||
|
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/EndianUtils.h"
|
#include "mozilla/EndianUtils.h"
|
||||||
#include "mozilla/NullPrincipal.h"
|
#include "mozilla/NullPrincipal.h"
|
||||||
@@ -40,24 +44,48 @@
|
|||||||
#include "nsIAsyncInputStream.h"
|
#include "nsIAsyncInputStream.h"
|
||||||
#include "nsIAsyncOutputStream.h"
|
#include "nsIAsyncOutputStream.h"
|
||||||
#include "prlink.h"
|
#include "prlink.h"
|
||||||
|
#include "gfxUtils.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
|
|
||||||
using mozilla::CheckedInt32;
|
using namespace mozilla;
|
||||||
using mozilla::ipc::ByteBuf;
|
using mozilla::ipc::ByteBuf;
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(nsIconChannel, nsIRequest, nsIChannel)
|
NS_IMPL_ISUPPORTS(nsIconChannel, nsIRequest, nsIChannel)
|
||||||
|
|
||||||
static nsresult MozGdkPixbufToByteBuf(GdkPixbuf* aPixbuf, gint aScale,
|
static bool IsValidRGBAPixbuf(GdkPixbuf* aPixbuf) {
|
||||||
ByteBuf* aByteBuf) {
|
return gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
|
||||||
|
gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
|
||||||
|
gdk_pixbuf_get_has_alpha(aPixbuf) &&
|
||||||
|
gdk_pixbuf_get_n_channels(aPixbuf) == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static already_AddRefed<gfx::DataSourceSurface> MozGdkPixbufToDataSurface(
|
||||||
|
GdkPixbuf* aPixbuf) {
|
||||||
|
if (NS_WARN_IF(!IsValidRGBAPixbuf(aPixbuf))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int width = gdk_pixbuf_get_width(aPixbuf);
|
||||||
|
int height = gdk_pixbuf_get_height(aPixbuf);
|
||||||
|
if (!width || !height) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
guchar* const pixels = gdk_pixbuf_get_pixels(aPixbuf);
|
||||||
|
const int stride = gdk_pixbuf_get_rowstride(aPixbuf);
|
||||||
|
RefPtr wrapper = gfx::Factory::CreateWrappingDataSourceSurface(
|
||||||
|
pixels, stride, gfx::IntSize(width, height),
|
||||||
|
gfx::SurfaceFormat::R8G8B8A8);
|
||||||
|
if (NS_WARN_IF(!wrapper)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return gfxUtils::CreatePremultipliedDataSurface(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static nsresult MozGdkPixbufToByteBuf(GdkPixbuf* aPixbuf, ByteBuf* aByteBuf) {
|
||||||
int width = gdk_pixbuf_get_width(aPixbuf);
|
int width = gdk_pixbuf_get_width(aPixbuf);
|
||||||
int height = gdk_pixbuf_get_height(aPixbuf);
|
int height = gdk_pixbuf_get_height(aPixbuf);
|
||||||
NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
|
NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
|
||||||
gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
|
IsValidRGBAPixbuf(aPixbuf),
|
||||||
gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
|
|
||||||
gdk_pixbuf_get_has_alpha(aPixbuf) &&
|
|
||||||
gdk_pixbuf_get_n_channels(aPixbuf) == 4,
|
|
||||||
NS_ERROR_UNEXPECTED);
|
NS_ERROR_UNEXPECTED);
|
||||||
|
|
||||||
const int n_channels = 4;
|
const int n_channels = 4;
|
||||||
CheckedInt32 buf_size =
|
CheckedInt32 buf_size =
|
||||||
4 + n_channels * CheckedInt32(height) * CheckedInt32(width);
|
4 + n_channels * CheckedInt32(height) * CheckedInt32(width);
|
||||||
@@ -112,7 +140,17 @@ static nsresult ByteBufToStream(ByteBuf&& aBuf, nsIInputStream** aStream) {
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GdkRGBA GetForegroundColor(nsIMozIconURI* aIconURI) {
|
static GdkRGBA GeckoColorToGdk(nscolor aColor) {
|
||||||
|
auto ToGdk = [](uint8_t aGecko) { return aGecko / 255.0; };
|
||||||
|
return GdkRGBA{
|
||||||
|
.red = ToGdk(NS_GET_R(aColor)),
|
||||||
|
.green = ToGdk(NS_GET_G(aColor)),
|
||||||
|
.blue = ToGdk(NS_GET_B(aColor)),
|
||||||
|
.alpha = ToGdk(NS_GET_A(aColor)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static nscolor GetForegroundColor(nsIMozIconURI* aIconURI) {
|
||||||
auto scheme = [&] {
|
auto scheme = [&] {
|
||||||
bool dark = false;
|
bool dark = false;
|
||||||
if (NS_FAILED(aIconURI->GetImageDark(&dark))) {
|
if (NS_FAILED(aIconURI->GetImageDark(&dark))) {
|
||||||
@@ -120,21 +158,12 @@ static GdkRGBA GetForegroundColor(nsIMozIconURI* aIconURI) {
|
|||||||
}
|
}
|
||||||
return dark ? mozilla::ColorScheme::Dark : mozilla::ColorScheme::Light;
|
return dark ? mozilla::ColorScheme::Dark : mozilla::ColorScheme::Light;
|
||||||
}();
|
}();
|
||||||
auto color = mozilla::LookAndFeel::Color(
|
return mozilla::LookAndFeel::Color(mozilla::LookAndFeel::ColorID::Windowtext,
|
||||||
mozilla::LookAndFeel::ColorID::Windowtext, scheme,
|
scheme,
|
||||||
mozilla::LookAndFeel::UseStandins::No);
|
mozilla::LookAndFeel::UseStandins::No);
|
||||||
auto ToGdk = [](uint8_t aGecko) { return aGecko / 255.0; };
|
|
||||||
return GdkRGBA{
|
|
||||||
.red = ToGdk(NS_GET_R(color)),
|
|
||||||
.green = ToGdk(NS_GET_G(color)),
|
|
||||||
.blue = ToGdk(NS_GET_B(color)),
|
|
||||||
.alpha = ToGdk(NS_GET_A(color)),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
static nsresult GetIconWithGIO(nsIMozIconURI* aIconURI, ByteBuf* aDataOut) {
|
||||||
nsresult nsIconChannel::GetIconWithGIO(nsIMozIconURI* aIconURI,
|
|
||||||
ByteBuf* aDataOut) {
|
|
||||||
RefPtr<GIcon> icon;
|
RefPtr<GIcon> icon;
|
||||||
nsCOMPtr<nsIURL> fileURI;
|
nsCOMPtr<nsIURL> fileURI;
|
||||||
|
|
||||||
@@ -218,14 +247,31 @@ nsresult nsIconChannel::GetIconWithGIO(nsIMozIconURI* aIconURI,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a GdkPixbuf buffer containing icon and scale it
|
// Create a GdkPixbuf buffer containing icon and scale it
|
||||||
const auto fg = GetForegroundColor(aIconURI);
|
const auto fg = GeckoColorToGdk(GetForegroundColor(aIconURI));
|
||||||
RefPtr<GdkPixbuf> pixbuf = dont_AddRef(gtk_icon_info_load_symbolic(
|
RefPtr<GdkPixbuf> pixbuf = dont_AddRef(gtk_icon_info_load_symbolic(
|
||||||
iconInfo, &fg, nullptr, nullptr, nullptr, nullptr, nullptr));
|
iconInfo, &fg, nullptr, nullptr, nullptr, nullptr, nullptr));
|
||||||
|
|
||||||
if (!pixbuf) {
|
if (!pixbuf) {
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
return MozGdkPixbufToByteBuf(pixbuf, scale, aDataOut);
|
return MozGdkPixbufToByteBuf(pixbuf, aDataOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
static already_AddRefed<GdkPixbuf> GetSymbolicIconPixbuf(const nsCString& aName,
|
||||||
|
int aIconSize,
|
||||||
|
int aScale,
|
||||||
|
nscolor aFgColor) {
|
||||||
|
GtkIconTheme* theme = gtk_icon_theme_get_default();
|
||||||
|
RefPtr<GtkIconInfo> iconInfo =
|
||||||
|
dont_AddRef(gtk_icon_theme_lookup_icon_for_scale(
|
||||||
|
theme, aName.get(), aIconSize, aScale, GtkIconLookupFlags(0)));
|
||||||
|
if (!iconInfo) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto fg = GeckoColorToGdk(aFgColor);
|
||||||
|
RefPtr<GdkPixbuf> pixbuf = dont_AddRef(gtk_icon_info_load_symbolic(
|
||||||
|
iconInfo, &fg, nullptr, nullptr, nullptr, nullptr, nullptr));
|
||||||
|
return pixbuf.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
@@ -247,27 +293,23 @@ nsresult nsIconChannel::GetIcon(nsIURI* aURI, ByteBuf* aDataOut) {
|
|||||||
return GetIconWithGIO(iconURI, aDataOut);
|
return GetIconWithGIO(iconURI, aDataOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkIconTheme* theme = gtk_icon_theme_get_default();
|
|
||||||
const gint iconSize = iconURI->GetImageSize();
|
const gint iconSize = iconURI->GetImageSize();
|
||||||
const gint scale = iconURI->GetImageScale();
|
const gint scale = iconURI->GetImageScale();
|
||||||
RefPtr<GtkIconInfo> iconInfo =
|
const nscolor fg = GetForegroundColor(iconURI);
|
||||||
dont_AddRef(gtk_icon_theme_lookup_icon_for_scale(
|
RefPtr pixbuf = GetSymbolicIconPixbuf(stockIcon, iconSize, scale, fg);
|
||||||
theme, stockIcon.get(), iconSize, scale, GtkIconLookupFlags(0)));
|
|
||||||
if (!iconInfo) {
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
}
|
|
||||||
const auto fg = GetForegroundColor(iconURI);
|
|
||||||
RefPtr<GdkPixbuf> pixbuf = dont_AddRef(gtk_icon_info_load_symbolic(
|
|
||||||
iconInfo, &fg, nullptr, nullptr, nullptr, nullptr, nullptr));
|
|
||||||
|
|
||||||
// According to documentation, gtk_icon_set_render_icon() never returns
|
|
||||||
// nullptr, but it does return nullptr when we have the problem reported
|
|
||||||
// here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13
|
|
||||||
if (!pixbuf) {
|
if (!pixbuf) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
return MozGdkPixbufToByteBuf(pixbuf, aDataOut);
|
||||||
|
}
|
||||||
|
|
||||||
return MozGdkPixbufToByteBuf(pixbuf, scale, aDataOut);
|
already_AddRefed<gfx::DataSourceSurface> nsIconChannel::GetSymbolicIcon(
|
||||||
|
const nsCString& aName, int aIconSize, int aScale, nscolor aFgColor) {
|
||||||
|
RefPtr pixbuf = GetSymbolicIconPixbuf(aName, aIconSize, aScale, aFgColor);
|
||||||
|
if (!pixbuf) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return MozGdkPixbufToDataSurface(pixbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsIconChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
|
nsresult nsIconChannel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
|
||||||
|
|||||||
@@ -10,12 +10,16 @@
|
|||||||
|
|
||||||
#include "nsIChannel.h"
|
#include "nsIChannel.h"
|
||||||
#include "nsIURI.h"
|
#include "nsIURI.h"
|
||||||
#include "nsIIconURI.h"
|
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
|
||||||
namespace mozilla::ipc {
|
namespace mozilla {
|
||||||
|
namespace ipc {
|
||||||
class ByteBuf;
|
class ByteBuf;
|
||||||
}
|
}
|
||||||
|
namespace gfx {
|
||||||
|
class DataSourceSurface;
|
||||||
|
}
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
/// This class is the GTK implementation of nsIconChannel. It asks
|
/// This class is the GTK implementation of nsIconChannel. It asks
|
||||||
/// GTK for the icon, translates the pixel data in-memory into
|
/// GTK for the icon, translates the pixel data in-memory into
|
||||||
@@ -27,7 +31,7 @@ class nsIconChannel final : public nsIChannel {
|
|||||||
NS_FORWARD_NSIREQUEST(mRealChannel->)
|
NS_FORWARD_NSIREQUEST(mRealChannel->)
|
||||||
NS_FORWARD_NSICHANNEL(mRealChannel->)
|
NS_FORWARD_NSICHANNEL(mRealChannel->)
|
||||||
|
|
||||||
nsIconChannel() {}
|
nsIconChannel() = default;
|
||||||
|
|
||||||
static void Shutdown();
|
static void Shutdown();
|
||||||
|
|
||||||
@@ -39,15 +43,14 @@ class nsIconChannel final : public nsIChannel {
|
|||||||
/// Obtains an icon, in nsIconDecoder format, as a ByteBuf instead
|
/// Obtains an icon, in nsIconDecoder format, as a ByteBuf instead
|
||||||
/// of a channel. For use with IPC.
|
/// of a channel. For use with IPC.
|
||||||
static nsresult GetIcon(nsIURI* aURI, mozilla::ipc::ByteBuf* aDataOut);
|
static nsresult GetIcon(nsIURI* aURI, mozilla::ipc::ByteBuf* aDataOut);
|
||||||
|
static already_AddRefed<mozilla::gfx::DataSourceSurface> GetSymbolicIcon(
|
||||||
|
const nsCString& aName, int aIconSize, int aScale, nscolor aFgColor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~nsIconChannel() {}
|
~nsIconChannel() = default;
|
||||||
/// The input stream channel which will yield the image.
|
/// The input stream channel which will yield the image.
|
||||||
/// Will always be non-null after a successful Init.
|
/// Will always be non-null after a successful Init.
|
||||||
nsCOMPtr<nsIChannel> mRealChannel;
|
nsCOMPtr<nsIChannel> mRealChannel;
|
||||||
|
|
||||||
static nsresult GetIconWithGIO(nsIMozIconURI* aIconURI,
|
|
||||||
mozilla::ipc::ByteBuf* aDataOut);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // mozilla_image_decoders_icon_gtk_nsIconChannel_h
|
#endif // mozilla_image_decoders_icon_gtk_nsIconChannel_h
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
|
|
||||||
#include "mozilla/webrender/WebRenderAPI.h"
|
#include "mozilla/webrender/WebRenderAPI.h"
|
||||||
|
|
||||||
|
#ifdef MOZ_WIDGET_GTK
|
||||||
|
# include "nsIconChannel.h"
|
||||||
|
#endif
|
||||||
#include "gfxContext.h"
|
#include "gfxContext.h"
|
||||||
#include "gfxDrawable.h"
|
#include "gfxDrawable.h"
|
||||||
#include "ImageOps.h"
|
#include "ImageOps.h"
|
||||||
@@ -64,6 +67,57 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
|
|||||||
mExtendMode(ExtendMode::CLAMP),
|
mExtendMode(ExtendMode::CLAMP),
|
||||||
mMaskOp(StyleMaskMode::MatchSource) {}
|
mMaskOp(StyleMaskMode::MatchSource) {}
|
||||||
|
|
||||||
|
using SymbolicImageKey = std::tuple<RefPtr<nsAtom>, int, nscolor>;
|
||||||
|
struct SymbolicImageEntry {
|
||||||
|
SymbolicImageKey mKey;
|
||||||
|
nsCOMPtr<imgIContainer> mImage;
|
||||||
|
};
|
||||||
|
struct SymbolicImageCache final
|
||||||
|
: public mozilla::MruCache<SymbolicImageKey, SymbolicImageEntry,
|
||||||
|
SymbolicImageCache, 5> {
|
||||||
|
static HashNumber Hash(const KeyType& aKey) {
|
||||||
|
return AddToHash(std::get<0>(aKey)->hash(),
|
||||||
|
HashGeneric(std::get<1>(aKey), std::get<2>(aKey)));
|
||||||
|
}
|
||||||
|
static bool Match(const KeyType& aKey, const ValueType& aVal) {
|
||||||
|
return aVal.mKey == aKey;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_DECLARE_FRAME_PROPERTY_DELETABLE(SymbolicImageCacheProp, SymbolicImageCache);
|
||||||
|
|
||||||
|
static already_AddRefed<imgIContainer> GetSymbolicIconImage(nsAtom* aName,
|
||||||
|
int aScale,
|
||||||
|
nsIFrame* aFrame) {
|
||||||
|
if (NS_WARN_IF(!XRE_IsParentProcess())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto fg = aFrame->StyleText()->mColor.ToColor();
|
||||||
|
auto key = std::make_tuple(aName, aScale, fg);
|
||||||
|
auto* cache = aFrame->GetProperty(SymbolicImageCacheProp());
|
||||||
|
if (!cache) {
|
||||||
|
cache = new SymbolicImageCache();
|
||||||
|
aFrame->SetProperty(SymbolicImageCacheProp(), cache);
|
||||||
|
}
|
||||||
|
auto lookup = cache->Lookup(key);
|
||||||
|
if (lookup) {
|
||||||
|
return do_AddRef(lookup.Data().mImage);
|
||||||
|
}
|
||||||
|
RefPtr<gfx::DataSourceSurface> surface;
|
||||||
|
#ifdef MOZ_WIDGET_GTK
|
||||||
|
surface =
|
||||||
|
nsIconChannel::GetSymbolicIcon(nsAtomCString(aName), 16, aScale, fg);
|
||||||
|
#endif
|
||||||
|
if (NS_WARN_IF(!surface)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
RefPtr drawable = new gfxSurfaceDrawable(surface, surface->GetSize());
|
||||||
|
nsCOMPtr<imgIContainer> container = ImageOps::CreateFromDrawable(drawable);
|
||||||
|
MOZ_ASSERT(container);
|
||||||
|
lookup.Set(SymbolicImageEntry{std::move(key), std::move(container)});
|
||||||
|
return do_AddRef(lookup.Data().mImage);
|
||||||
|
}
|
||||||
|
|
||||||
bool nsImageRenderer::PrepareImage() {
|
bool nsImageRenderer::PrepareImage() {
|
||||||
if (mImage->IsNone()) {
|
if (mImage->IsNone()) {
|
||||||
mPrepareResult = ImgDrawResult::BAD_IMAGE;
|
mPrepareResult = ImgDrawResult::BAD_IMAGE;
|
||||||
@@ -182,6 +236,17 @@ bool nsImageRenderer::PrepareImage() {
|
|||||||
mPaintServerFrame = paintServerFrame;
|
mPaintServerFrame = paintServerFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPrepareResult = ImgDrawResult::SUCCESS;
|
||||||
|
} else if (mImage->IsMozSymbolicIcon()) {
|
||||||
|
auto deviceScale =
|
||||||
|
std::ceil(mForFrame->PresContext()->CSSToDevPixelScale().scale);
|
||||||
|
mImageResolution.ScaleBy(deviceScale);
|
||||||
|
mImageContainer = GetSymbolicIconImage(mImage->AsMozSymbolicIcon().AsAtom(),
|
||||||
|
int(deviceScale), mForFrame);
|
||||||
|
if (!mImageContainer) {
|
||||||
|
mPrepareResult = ImgDrawResult::BAD_IMAGE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
mPrepareResult = ImgDrawResult::SUCCESS;
|
mPrepareResult = ImgDrawResult::SUCCESS;
|
||||||
} else if (mImage->IsCrossFade()) {
|
} else if (mImage->IsCrossFade()) {
|
||||||
// See bug 546052 - cross-fade implementation still being worked
|
// See bug 546052 - cross-fade implementation still being worked
|
||||||
@@ -202,6 +267,7 @@ CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() {
|
|||||||
|
|
||||||
CSSSizeOrRatio result;
|
CSSSizeOrRatio result;
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
|
case StyleImage::Tag::MozSymbolicIcon:
|
||||||
case StyleImage::Tag::Url: {
|
case StyleImage::Tag::Url: {
|
||||||
bool haveWidth, haveHeight;
|
bool haveWidth, haveHeight;
|
||||||
CSSIntSize imageIntSize;
|
CSSIntSize imageIntSize;
|
||||||
@@ -489,6 +555,7 @@ ImgDrawResult nsImageRenderer::Draw(nsPresContext* aPresContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (mType) {
|
switch (mType) {
|
||||||
|
case StyleImage::Tag::MozSymbolicIcon:
|
||||||
case StyleImage::Tag::Url: {
|
case StyleImage::Tag::Url: {
|
||||||
result = nsLayoutUtils::DrawBackgroundImage(
|
result = nsLayoutUtils::DrawBackgroundImage(
|
||||||
*ctx, mForFrame, aPresContext, mImageContainer, samplingFilter, aDest,
|
*ctx, mForFrame, aPresContext, mImageContainer, samplingFilter, aDest,
|
||||||
@@ -590,6 +657,7 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
|
|||||||
!aItem->BackfaceIsHidden(), aOpacity);
|
!aItem->BackfaceIsHidden(), aOpacity);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case StyleImage::Tag::MozSymbolicIcon:
|
||||||
case StyleImage::Tag::Url: {
|
case StyleImage::Tag::Url: {
|
||||||
ExtendMode extendMode = mExtendMode;
|
ExtendMode extendMode = mExtendMode;
|
||||||
if (aDest.Contains(aFill)) {
|
if (aDest.Contains(aFill)) {
|
||||||
@@ -889,10 +957,8 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
|
|||||||
return ImgDrawResult::SUCCESS;
|
return ImgDrawResult::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isRequestBacked = mType == StyleImage::Tag::Url;
|
const bool hasImage = !!mImageContainer;
|
||||||
MOZ_ASSERT(isRequestBacked == mImage->IsImageRequestType());
|
if (hasImage || mType == StyleImage::Tag::Element) {
|
||||||
|
|
||||||
if (isRequestBacked || mType == StyleImage::Tag::Element) {
|
|
||||||
nsCOMPtr<imgIContainer> subImage;
|
nsCOMPtr<imgIContainer> subImage;
|
||||||
|
|
||||||
// To draw one portion of an image into a border component, we stretch that
|
// To draw one portion of an image into a border component, we stretch that
|
||||||
@@ -911,10 +977,10 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
|
|||||||
}
|
}
|
||||||
// Retrieve or create the subimage we'll draw.
|
// Retrieve or create the subimage we'll draw.
|
||||||
nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
|
nsIntRect srcRect(aSrc.x, aSrc.y, aSrc.width, aSrc.height);
|
||||||
if (isRequestBacked) {
|
if (hasImage) {
|
||||||
subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
|
subImage = ImageOps::Clip(mImageContainer, srcRect, aSVGViewportSize);
|
||||||
} else {
|
} else {
|
||||||
// This path, for eStyleImageType_Element, is currently slower than it
|
// This path, for Element, is currently slower than it
|
||||||
// needs to be because we don't cache anything. (In particular, if we have
|
// needs to be because we don't cache anything. (In particular, if we have
|
||||||
// to draw to a temporary surface inside ClippedImage, we don't cache that
|
// to draw to a temporary surface inside ClippedImage, we don't cache that
|
||||||
// temporary surface since we immediately throw the ClippedImage we create
|
// temporary surface since we immediately throw the ClippedImage we create
|
||||||
@@ -1027,7 +1093,7 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
|
|||||||
return ImgDrawResult::BAD_IMAGE;
|
return ImgDrawResult::BAD_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nsImageRenderer::IsRasterImage() {
|
bool nsImageRenderer::IsRasterImage() const {
|
||||||
return mImageContainer &&
|
return mImageContainer &&
|
||||||
mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
|
mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ class nsImageRenderer {
|
|||||||
ImgDrawResult DrawShapeImage(nsPresContext* aPresContext,
|
ImgDrawResult DrawShapeImage(nsPresContext* aPresContext,
|
||||||
gfxContext& aRenderingContext);
|
gfxContext& aRenderingContext);
|
||||||
|
|
||||||
bool IsRasterImage();
|
bool IsRasterImage() const;
|
||||||
|
|
||||||
/// Retrieves the image associated with this nsImageRenderer, if there is one.
|
/// Retrieves the image associated with this nsImageRenderer, if there is one.
|
||||||
already_AddRefed<imgIContainer> GetImage();
|
already_AddRefed<imgIContainer> GetImage();
|
||||||
|
|||||||
@@ -1583,7 +1583,7 @@ bool StyleImage::IsOpaque() const {
|
|||||||
return AsGradient()->IsOpaque();
|
return AsGradient()->IsOpaque();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsElement()) {
|
if (IsElement() || IsMozSymbolicIcon()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1604,6 +1604,7 @@ bool StyleImage::IsComplete() const {
|
|||||||
return false;
|
return false;
|
||||||
case Tag::Gradient:
|
case Tag::Gradient:
|
||||||
case Tag::Element:
|
case Tag::Element:
|
||||||
|
case Tag::MozSymbolicIcon:
|
||||||
return true;
|
return true;
|
||||||
case Tag::Url: {
|
case Tag::Url: {
|
||||||
if (!IsResolved()) {
|
if (!IsResolved()) {
|
||||||
@@ -1638,6 +1639,7 @@ bool StyleImage::IsSizeAvailable() const {
|
|||||||
return false;
|
return false;
|
||||||
case Tag::Gradient:
|
case Tag::Gradient:
|
||||||
case Tag::Element:
|
case Tag::Element:
|
||||||
|
case Tag::MozSymbolicIcon:
|
||||||
return true;
|
return true;
|
||||||
case Tag::Url: {
|
case Tag::Url: {
|
||||||
imgRequestProxy* req = GetImageRequest();
|
imgRequestProxy* req = GetImageRequest();
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ impl ToComputedValue for specified::Image {
|
|||||||
Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)),
|
Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Self::Element(e) => Image::Element(e.to_computed_value(context)),
|
Self::Element(e) => Image::Element(e.to_computed_value(context)),
|
||||||
|
Self::MozSymbolicIcon(e) => Image::MozSymbolicIcon(e.to_computed_value(context)),
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)),
|
Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)),
|
||||||
Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)),
|
Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)),
|
||||||
@@ -232,6 +233,7 @@ impl ToComputedValue for specified::Image {
|
|||||||
Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)),
|
Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)),
|
Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)),
|
||||||
|
Image::MozSymbolicIcon(e) => Self::MozSymbolicIcon(ToComputedValue::from_computed_value(e)),
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)),
|
Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)),
|
||||||
Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)),
|
Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use style_traits::{CssWriter, ToCss};
|
|||||||
pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
|
pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
|
||||||
/// `none` variant.
|
/// `none` variant.
|
||||||
None,
|
None,
|
||||||
|
|
||||||
/// A `<url()>` image.
|
/// A `<url()>` image.
|
||||||
Url(ImageUrl),
|
Url(ImageUrl),
|
||||||
|
|
||||||
@@ -37,6 +38,12 @@ pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
|
|||||||
#[css(function = "-moz-element")]
|
#[css(function = "-moz-element")]
|
||||||
Element(Atom),
|
Element(Atom),
|
||||||
|
|
||||||
|
/// A `-moz-symbolic-icon(<icon-id>)`
|
||||||
|
/// NOTE(emilio): #[css(skip)] only really affects SpecifiedValueInfo, which we want because
|
||||||
|
/// this is chrome-only.
|
||||||
|
#[css(function, skip)]
|
||||||
|
MozSymbolicIcon(Atom),
|
||||||
|
|
||||||
/// A paint worklet image.
|
/// A paint worklet image.
|
||||||
/// <https://drafts.css-houdini.org/css-paint-api/>
|
/// <https://drafts.css-houdini.org/css-paint-api/>
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
@@ -427,6 +434,11 @@ where
|
|||||||
serialize_atom_identifier(selector, dest)?;
|
serialize_atom_identifier(selector, dest)?;
|
||||||
dest.write_char(')')
|
dest.write_char(')')
|
||||||
},
|
},
|
||||||
|
Image::MozSymbolicIcon(ref id) => {
|
||||||
|
dest.write_str("-moz-symbolic-icon(")?;
|
||||||
|
serialize_atom_identifier(id, dest)?;
|
||||||
|
dest.write_char(')')
|
||||||
|
},
|
||||||
Image::ImageSet(ref is) => is.to_css(dest),
|
Image::ImageSet(ref is) => is.to_css(dest),
|
||||||
Image::CrossFade(ref cf) => cf.to_css(dest),
|
Image::CrossFade(ref cf) => cf.to_css(dest),
|
||||||
Image::LightDark(ref ld) => ld.to_css(dest),
|
Image::LightDark(ref ld) => ld.to_css(dest),
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ impl Image {
|
|||||||
})?)),
|
})?)),
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
"-moz-element" => Self::Element(Self::parse_element(input)?),
|
"-moz-element" => Self::Element(Self::parse_element(input)?),
|
||||||
|
"-moz-symbolic-icon" if context.chrome_rules_enabled() => Self::MozSymbolicIcon(input.expect_ident()?.as_ref().into()),
|
||||||
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function))),
|
_ => return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function))),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user