1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/decoders/icon/gtk/nsIconChannel.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,666 @@ 1.4 +/* vim:set ts=2 sw=2 sts=2 cin et: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <stdlib.h> 1.10 +#include <unistd.h> 1.11 + 1.12 +#include "mozilla/DebugOnly.h" 1.13 +#include "mozilla/Endian.h" 1.14 +#include <algorithm> 1.15 + 1.16 +#ifdef MOZ_ENABLE_GNOMEUI 1.17 +// Older versions of these headers seem to be missing an extern "C" 1.18 +extern "C" { 1.19 +#include <libgnome/libgnome.h> 1.20 +#include <libgnomeui/gnome-icon-theme.h> 1.21 +#include <libgnomeui/gnome-icon-lookup.h> 1.22 + 1.23 +#include <libgnomevfs/gnome-vfs-file-info.h> 1.24 +#include <libgnomevfs/gnome-vfs-ops.h> 1.25 +} 1.26 +#endif 1.27 +#ifdef MOZ_ENABLE_GIO 1.28 +#include <gio/gio.h> 1.29 +#endif 1.30 + 1.31 +#include <gtk/gtk.h> 1.32 + 1.33 +#include "nsMimeTypes.h" 1.34 +#include "nsIMIMEService.h" 1.35 + 1.36 +#include "nsIStringBundle.h" 1.37 + 1.38 +#include "nsNetUtil.h" 1.39 +#include "nsIURL.h" 1.40 +#include "prlink.h" 1.41 + 1.42 +#include "nsIconChannel.h" 1.43 + 1.44 +NS_IMPL_ISUPPORTS(nsIconChannel, 1.45 + nsIRequest, 1.46 + nsIChannel) 1.47 + 1.48 +#ifdef MOZ_ENABLE_GNOMEUI 1.49 +// These let us have a soft dependency on libgnomeui rather than a hard one. These are just basically the prototypes 1.50 +// of the functions in the libraries. 1.51 +typedef char* (*_GnomeIconLookup_fn)(GtkIconTheme *icon_theme, GnomeThumbnailFactory *thumbnail_factory, 1.52 + const char *file_uri, const char *custom_icon, GnomeVFSFileInfo *file_info, 1.53 + const char *mime_type, GnomeIconLookupFlags flags, GnomeIconLookupResultFlags *result); 1.54 +typedef GnomeIconTheme* (*_GnomeIconThemeNew_fn)(void); 1.55 +typedef int (*_GnomeInit_fn)(const char *app_id, const char *app_version, int argc, char **argv, const struct poptOption *options, 1.56 + int flags, poptContext *return_ctx); 1.57 +typedef GnomeProgram* (*_GnomeProgramGet_fn)(void); 1.58 +typedef GnomeVFSResult (*_GnomeVFSGetFileInfo_fn)(const gchar *text_uri, GnomeVFSFileInfo *info, GnomeVFSFileInfoOptions options); 1.59 +typedef void (*_GnomeVFSFileInfoClear_fn)(GnomeVFSFileInfo *info); 1.60 + 1.61 +static PRLibrary* gLibGnomeUI = nullptr; 1.62 +static PRLibrary* gLibGnome = nullptr; 1.63 +static PRLibrary* gLibGnomeVFS = nullptr; 1.64 +static bool gTriedToLoadGnomeLibs = false; 1.65 + 1.66 +static _GnomeIconLookup_fn _gnome_icon_lookup = nullptr; 1.67 +static _GnomeIconThemeNew_fn _gnome_icon_theme_new = nullptr; 1.68 +static _GnomeInit_fn _gnome_init = nullptr; 1.69 +static _GnomeProgramGet_fn _gnome_program_get = nullptr; 1.70 +static _GnomeVFSGetFileInfo_fn _gnome_vfs_get_file_info = nullptr; 1.71 +static _GnomeVFSFileInfoClear_fn _gnome_vfs_file_info_clear = nullptr; 1.72 +#endif //MOZ_ENABLE_GNOMEUI 1.73 + 1.74 +static nsresult 1.75 +moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI *aURI, 1.76 + nsIChannel **aChannel) 1.77 +{ 1.78 + int width = gdk_pixbuf_get_width(aPixbuf); 1.79 + int height = gdk_pixbuf_get_height(aPixbuf); 1.80 + NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 && 1.81 + gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB && 1.82 + gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 && 1.83 + gdk_pixbuf_get_has_alpha(aPixbuf) && 1.84 + gdk_pixbuf_get_n_channels(aPixbuf) == 4, 1.85 + NS_ERROR_UNEXPECTED); 1.86 + 1.87 + const int n_channels = 4; 1.88 + gsize buf_size = 2 + n_channels * height * width; 1.89 + uint8_t * const buf = (uint8_t*)NS_Alloc(buf_size); 1.90 + NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); 1.91 + uint8_t *out = buf; 1.92 + 1.93 + *(out++) = width; 1.94 + *(out++) = height; 1.95 + 1.96 + const guchar * const pixels = gdk_pixbuf_get_pixels(aPixbuf); 1.97 + int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels; 1.98 + 1.99 + // encode the RGB data and the A data 1.100 + const guchar * in = pixels; 1.101 + for (int y = 0; y < height; ++y, in += rowextra) { 1.102 + for (int x = 0; x < width; ++x) { 1.103 + uint8_t r = *(in++); 1.104 + uint8_t g = *(in++); 1.105 + uint8_t b = *(in++); 1.106 + uint8_t a = *(in++); 1.107 +#define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255)) 1.108 +#if MOZ_LITTLE_ENDIAN 1.109 + *(out++) = DO_PREMULTIPLY(b); 1.110 + *(out++) = DO_PREMULTIPLY(g); 1.111 + *(out++) = DO_PREMULTIPLY(r); 1.112 + *(out++) = a; 1.113 +#else 1.114 + *(out++) = a; 1.115 + *(out++) = DO_PREMULTIPLY(r); 1.116 + *(out++) = DO_PREMULTIPLY(g); 1.117 + *(out++) = DO_PREMULTIPLY(b); 1.118 +#endif 1.119 +#undef DO_PREMULTIPLY 1.120 + } 1.121 + } 1.122 + 1.123 + NS_ASSERTION(out == buf + buf_size, "size miscalculation"); 1.124 + 1.125 + nsresult rv; 1.126 + nsCOMPtr<nsIStringInputStream> stream = 1.127 + do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); 1.128 + NS_ENSURE_SUCCESS(rv, rv); 1.129 + 1.130 + rv = stream->AdoptData((char*)buf, buf_size); 1.131 + NS_ENSURE_SUCCESS(rv, rv); 1.132 + 1.133 + rv = NS_NewInputStreamChannel(aChannel, aURI, stream, 1.134 + NS_LITERAL_CSTRING(IMAGE_ICON_MS)); 1.135 + return rv; 1.136 +} 1.137 + 1.138 +static GtkWidget *gProtoWindow = nullptr; 1.139 +static GtkWidget *gStockImageWidget = nullptr; 1.140 +#ifdef MOZ_ENABLE_GNOMEUI 1.141 +static GnomeIconTheme *gIconTheme = nullptr; 1.142 +#endif //MOZ_ENABLE_GNOMEUI 1.143 + 1.144 +static void 1.145 +ensure_stock_image_widget() 1.146 +{ 1.147 + // Only the style of the GtkImage needs to be used, but the widget is kept 1.148 + // to track dynamic style changes. 1.149 + if (!gProtoWindow) { 1.150 + gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP); 1.151 + GtkWidget* protoLayout = gtk_fixed_new(); 1.152 + gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout); 1.153 + 1.154 + gStockImageWidget = gtk_image_new(); 1.155 + gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget); 1.156 + 1.157 + gtk_widget_ensure_style(gStockImageWidget); 1.158 + } 1.159 +} 1.160 + 1.161 +#ifdef MOZ_ENABLE_GNOMEUI 1.162 +static nsresult 1.163 +ensure_libgnomeui() 1.164 +{ 1.165 + // Attempt to get the libgnomeui symbol references. We do it this way so that stock icons from Init() 1.166 + // don't get held back by InitWithGnome()'s libgnomeui dependency. 1.167 + if (!gTriedToLoadGnomeLibs) { 1.168 + gLibGnomeUI = PR_LoadLibrary("libgnomeui-2.so.0"); 1.169 + if (!gLibGnomeUI) 1.170 + return NS_ERROR_NOT_AVAILABLE; 1.171 + 1.172 + _gnome_init = (_GnomeInit_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_init_with_popt_table"); 1.173 + _gnome_icon_theme_new = (_GnomeIconThemeNew_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_theme_new"); 1.174 + _gnome_icon_lookup = (_GnomeIconLookup_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_lookup"); 1.175 + 1.176 + if (!_gnome_init || !_gnome_icon_theme_new || !_gnome_icon_lookup) { 1.177 + PR_UnloadLibrary(gLibGnomeUI); 1.178 + gLibGnomeUI = nullptr; 1.179 + return NS_ERROR_NOT_AVAILABLE; 1.180 + } 1.181 + } 1.182 + 1.183 + if (!gLibGnomeUI) 1.184 + return NS_ERROR_NOT_AVAILABLE; 1.185 + 1.186 + return NS_OK; 1.187 +} 1.188 + 1.189 +static nsresult 1.190 +ensure_libgnome() 1.191 +{ 1.192 + if (!gTriedToLoadGnomeLibs) { 1.193 + gLibGnome = PR_LoadLibrary("libgnome-2.so.0"); 1.194 + if (!gLibGnome) 1.195 + return NS_ERROR_NOT_AVAILABLE; 1.196 + 1.197 + _gnome_program_get = (_GnomeProgramGet_fn)PR_FindFunctionSymbol(gLibGnome, "gnome_program_get"); 1.198 + if (!_gnome_program_get) { 1.199 + PR_UnloadLibrary(gLibGnome); 1.200 + gLibGnome = nullptr; 1.201 + return NS_ERROR_NOT_AVAILABLE; 1.202 + } 1.203 + } 1.204 + 1.205 + if (!gLibGnome) 1.206 + return NS_ERROR_NOT_AVAILABLE; 1.207 + 1.208 + return NS_OK; 1.209 +} 1.210 + 1.211 +static nsresult 1.212 +ensure_libgnomevfs() 1.213 +{ 1.214 + if (!gTriedToLoadGnomeLibs) { 1.215 + gLibGnomeVFS = PR_LoadLibrary("libgnomevfs-2.so.0"); 1.216 + if (!gLibGnomeVFS) 1.217 + return NS_ERROR_NOT_AVAILABLE; 1.218 + 1.219 + _gnome_vfs_get_file_info = (_GnomeVFSGetFileInfo_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_get_file_info"); 1.220 + _gnome_vfs_file_info_clear = (_GnomeVFSFileInfoClear_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_file_info_clear"); 1.221 + if (!_gnome_vfs_get_file_info || !_gnome_vfs_file_info_clear) { 1.222 + PR_UnloadLibrary(gLibGnomeVFS); 1.223 + gLibGnomeVFS = nullptr; 1.224 + return NS_ERROR_NOT_AVAILABLE; 1.225 + } 1.226 + } 1.227 + 1.228 + if (!gLibGnomeVFS) 1.229 + return NS_ERROR_NOT_AVAILABLE; 1.230 + 1.231 + return NS_OK; 1.232 +} 1.233 +#endif //MOZ_ENABLE_GNOMEUI 1.234 + 1.235 +static GtkIconSize 1.236 +moz_gtk_icon_size(const char *name) 1.237 +{ 1.238 + if (strcmp(name, "button") == 0) 1.239 + return GTK_ICON_SIZE_BUTTON; 1.240 + 1.241 + if (strcmp(name, "menu") == 0) 1.242 + return GTK_ICON_SIZE_MENU; 1.243 + 1.244 + if (strcmp(name, "toolbar") == 0) 1.245 + return GTK_ICON_SIZE_LARGE_TOOLBAR; 1.246 + 1.247 + if (strcmp(name, "toolbarsmall") == 0) 1.248 + return GTK_ICON_SIZE_SMALL_TOOLBAR; 1.249 + 1.250 + if (strcmp(name, "dnd") == 0) 1.251 + return GTK_ICON_SIZE_DND; 1.252 + 1.253 + if (strcmp(name, "dialog") == 0) 1.254 + return GTK_ICON_SIZE_DIALOG; 1.255 + 1.256 + return GTK_ICON_SIZE_MENU; 1.257 +} 1.258 + 1.259 +#if defined(MOZ_ENABLE_GNOMEUI) || defined(MOZ_ENABLE_GIO) 1.260 +static int32_t 1.261 +GetIconSize(nsIMozIconURI *aIconURI) 1.262 +{ 1.263 + nsAutoCString iconSizeString; 1.264 + 1.265 + aIconURI->GetIconSize(iconSizeString); 1.266 + if (iconSizeString.IsEmpty()) { 1.267 + uint32_t size; 1.268 + mozilla::DebugOnly<nsresult> rv = aIconURI->GetImageSize(&size); 1.269 + NS_ASSERTION(NS_SUCCEEDED(rv), "GetImageSize failed"); 1.270 + return size; 1.271 + } else { 1.272 + int size; 1.273 + 1.274 + GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get()); 1.275 + gtk_icon_size_lookup(icon_size, &size, nullptr); 1.276 + return size; 1.277 + } 1.278 +} 1.279 + 1.280 +/* Scale icon buffer to preferred size */ 1.281 +static nsresult 1.282 +ScaleIconBuf(GdkPixbuf **aBuf, int32_t iconSize) 1.283 +{ 1.284 + // Scale buffer only if width or height differ from preferred size 1.285 + if (gdk_pixbuf_get_width(*aBuf) != iconSize && 1.286 + gdk_pixbuf_get_height(*aBuf) != iconSize) { 1.287 + GdkPixbuf *scaled = gdk_pixbuf_scale_simple(*aBuf, iconSize, iconSize, 1.288 + GDK_INTERP_BILINEAR); 1.289 + // replace original buffer by scaled 1.290 + g_object_unref(*aBuf); 1.291 + *aBuf = scaled; 1.292 + if (!scaled) 1.293 + return NS_ERROR_OUT_OF_MEMORY; 1.294 + } 1.295 + return NS_OK; 1.296 +} 1.297 +#endif 1.298 + 1.299 +#ifdef MOZ_ENABLE_GNOMEUI 1.300 +nsresult 1.301 +nsIconChannel::InitWithGnome(nsIMozIconURI *aIconURI) 1.302 +{ 1.303 + nsresult rv; 1.304 + 1.305 + if (NS_FAILED(ensure_libgnomeui()) || NS_FAILED(ensure_libgnome()) || NS_FAILED(ensure_libgnomevfs())) { 1.306 + gTriedToLoadGnomeLibs = true; 1.307 + return NS_ERROR_NOT_AVAILABLE; 1.308 + } 1.309 + 1.310 + gTriedToLoadGnomeLibs = true; 1.311 + 1.312 + if (!_gnome_program_get()) { 1.313 + // Get the brandShortName from the string bundle to pass to GNOME 1.314 + // as the application name. This may be used for things such as 1.315 + // the title of grouped windows in the panel. 1.316 + nsCOMPtr<nsIStringBundleService> bundleService = 1.317 + do_GetService(NS_STRINGBUNDLE_CONTRACTID); 1.318 + 1.319 + NS_ASSERTION(bundleService, "String bundle service must be present!"); 1.320 + 1.321 + nsCOMPtr<nsIStringBundle> bundle; 1.322 + bundleService->CreateBundle("chrome://branding/locale/brand.properties", 1.323 + getter_AddRefs(bundle)); 1.324 + nsAutoString appName; 1.325 + 1.326 + if (bundle) { 1.327 + bundle->GetStringFromName(MOZ_UTF16("brandShortName"), 1.328 + getter_Copies(appName)); 1.329 + } else { 1.330 + NS_WARNING("brand.properties not present, using default application name"); 1.331 + appName.Assign(NS_LITERAL_STRING("Gecko")); 1.332 + } 1.333 + 1.334 + char* empty[] = { "" }; 1.335 + _gnome_init(NS_ConvertUTF16toUTF8(appName).get(), 1.336 + "1.0", 1, empty, nullptr, 0, nullptr); 1.337 + } 1.338 + 1.339 + uint32_t iconSize = GetIconSize(aIconURI); 1.340 + nsAutoCString type; 1.341 + aIconURI->GetContentType(type); 1.342 + 1.343 + GnomeVFSFileInfo fileInfo = {0}; 1.344 + fileInfo.refcount = 1; // In case some GnomeVFS function addrefs and releases it 1.345 + 1.346 + nsAutoCString spec; 1.347 + nsCOMPtr<nsIURL> url; 1.348 + rv = aIconURI->GetIconURL(getter_AddRefs(url)); 1.349 + if (url) { 1.350 + url->GetAsciiSpec(spec); 1.351 + // Only ask gnome-vfs for a GnomeVFSFileInfo for file: uris, to avoid a 1.352 + // network request 1.353 + bool isFile; 1.354 + if (NS_SUCCEEDED(url->SchemeIs("file", &isFile)) && isFile) { 1.355 + _gnome_vfs_get_file_info(spec.get(), &fileInfo, GNOME_VFS_FILE_INFO_DEFAULT); 1.356 + } 1.357 + else { 1.358 + // The filename we get is UTF-8-compatible, which matches gnome expectations. 1.359 + // See also: http://lists.gnome.org/archives/gnome-vfs-list/2004-March/msg00049.html 1.360 + // "Whenever we can detect the charset used for the URI type we try to 1.361 + // convert it to/from utf8 automatically inside gnome-vfs." 1.362 + // I'll interpret that as "otherwise, this field is random junk". 1.363 + nsAutoCString name; 1.364 + url->GetFileName(name); 1.365 + fileInfo.name = g_strdup(name.get()); 1.366 + 1.367 + if (!type.IsEmpty()) { 1.368 + fileInfo.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; 1.369 + fileInfo.mime_type = g_strdup(type.get()); 1.370 + } 1.371 + } 1.372 + } 1.373 + 1.374 + if (type.IsEmpty()) { 1.375 + nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1")); 1.376 + if (ms) { 1.377 + nsAutoCString fileExt; 1.378 + aIconURI->GetFileExtension(fileExt); 1.379 + if (!fileExt.IsEmpty()) { 1.380 + ms->GetTypeFromExtension(fileExt, type); 1.381 + } 1.382 + } 1.383 + } 1.384 + // Get the icon theme 1.385 + if (!gIconTheme) { 1.386 + gIconTheme = _gnome_icon_theme_new(); 1.387 + 1.388 + if (!gIconTheme) { 1.389 + _gnome_vfs_file_info_clear(&fileInfo); 1.390 + return NS_ERROR_NOT_AVAILABLE; 1.391 + } 1.392 + } 1.393 + 1.394 + char* name = _gnome_icon_lookup(gIconTheme, nullptr, spec.get(), nullptr, 1.395 + &fileInfo, type.get(), 1.396 + GNOME_ICON_LOOKUP_FLAGS_NONE, nullptr); 1.397 + 1.398 + _gnome_vfs_file_info_clear(&fileInfo); 1.399 + if (!name) 1.400 + return NS_ERROR_NOT_AVAILABLE; 1.401 + 1.402 + // Get the default theme associated with the screen 1.403 + // Do NOT free. 1.404 + GtkIconTheme *theme = gtk_icon_theme_get_default(); 1.405 + if (!theme) { 1.406 + g_free(name); 1.407 + return NS_ERROR_UNEXPECTED; 1.408 + } 1.409 + 1.410 + GError *err = nullptr; 1.411 + GdkPixbuf* buf = gtk_icon_theme_load_icon(theme, name, iconSize, (GtkIconLookupFlags)0, &err); 1.412 + g_free(name); 1.413 + 1.414 + if (!buf) { 1.415 + if (err) 1.416 + g_error_free(err); 1.417 + return NS_ERROR_UNEXPECTED; 1.418 + } 1.419 + 1.420 + rv = ScaleIconBuf(&buf, iconSize); 1.421 + NS_ENSURE_SUCCESS(rv, rv); 1.422 + 1.423 + rv = moz_gdk_pixbuf_to_channel(buf, aIconURI, 1.424 + getter_AddRefs(mRealChannel)); 1.425 + g_object_unref(buf); 1.426 + return rv; 1.427 +} 1.428 +#endif // MOZ_ENABLE_GNOMEUI 1.429 + 1.430 +#ifdef MOZ_ENABLE_GIO 1.431 +nsresult 1.432 +nsIconChannel::InitWithGIO(nsIMozIconURI *aIconURI) 1.433 +{ 1.434 + GIcon *icon = nullptr; 1.435 + nsCOMPtr<nsIURL> fileURI; 1.436 + 1.437 + // Read icon content 1.438 + aIconURI->GetIconURL(getter_AddRefs(fileURI)); 1.439 + 1.440 + // Get icon for file specified by URI 1.441 + if (fileURI) { 1.442 + bool isFile; 1.443 + nsAutoCString spec; 1.444 + fileURI->GetAsciiSpec(spec); 1.445 + if (NS_SUCCEEDED(fileURI->SchemeIs("file", &isFile)) && isFile) { 1.446 + GFile *file = g_file_new_for_uri(spec.get()); 1.447 + GFileInfo *fileInfo = g_file_query_info(file, 1.448 + G_FILE_ATTRIBUTE_STANDARD_ICON, 1.449 + G_FILE_QUERY_INFO_NONE, 1.450 + nullptr, nullptr); 1.451 + g_object_unref(file); 1.452 + if (fileInfo) { 1.453 + // icon from g_content_type_get_icon doesn't need unref 1.454 + icon = g_file_info_get_icon(fileInfo); 1.455 + if (icon) 1.456 + g_object_ref(icon); 1.457 + g_object_unref(fileInfo); 1.458 + } 1.459 + } 1.460 + } 1.461 + 1.462 + // Try to get icon by using MIME type 1.463 + if (!icon) { 1.464 + nsAutoCString type; 1.465 + aIconURI->GetContentType(type); 1.466 + // Try to get MIME type from file extension by using nsIMIMEService 1.467 + if (type.IsEmpty()) { 1.468 + nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1")); 1.469 + if (ms) { 1.470 + nsAutoCString fileExt; 1.471 + aIconURI->GetFileExtension(fileExt); 1.472 + ms->GetTypeFromExtension(fileExt, type); 1.473 + } 1.474 + } 1.475 + char *ctype = nullptr; // character representation of content type 1.476 + if (!type.IsEmpty()) { 1.477 + ctype = g_content_type_from_mime_type(type.get()); 1.478 + } 1.479 + if (ctype) { 1.480 + icon = g_content_type_get_icon(ctype); 1.481 + g_free(ctype); 1.482 + } 1.483 + } 1.484 + 1.485 + // Get default icon theme 1.486 + GtkIconTheme *iconTheme = gtk_icon_theme_get_default(); 1.487 + GtkIconInfo *iconInfo = nullptr; 1.488 + // Get icon size 1.489 + int32_t iconSize = GetIconSize(aIconURI); 1.490 + 1.491 + if (icon) { 1.492 + // Use icon and theme to get GtkIconInfo 1.493 + iconInfo = gtk_icon_theme_lookup_by_gicon(iconTheme, 1.494 + icon, iconSize, 1.495 + (GtkIconLookupFlags)0); 1.496 + g_object_unref(icon); 1.497 + } 1.498 + 1.499 + if (!iconInfo) { 1.500 + // Mozilla's mimetype lookup failed. Try the "unknown" icon. 1.501 + iconInfo = gtk_icon_theme_lookup_icon(iconTheme, 1.502 + "unknown", iconSize, 1.503 + (GtkIconLookupFlags)0); 1.504 + if (!iconInfo) { 1.505 + return NS_ERROR_NOT_AVAILABLE; 1.506 + } 1.507 + } 1.508 + 1.509 + // Create a GdkPixbuf buffer containing icon and scale it 1.510 + GdkPixbuf* buf = gtk_icon_info_load_icon(iconInfo, nullptr); 1.511 + gtk_icon_info_free(iconInfo); 1.512 + if (!buf) { 1.513 + return NS_ERROR_UNEXPECTED; 1.514 + } 1.515 + 1.516 + nsresult rv = ScaleIconBuf(&buf, iconSize); 1.517 + NS_ENSURE_SUCCESS(rv, rv); 1.518 + 1.519 + rv = moz_gdk_pixbuf_to_channel(buf, aIconURI, 1.520 + getter_AddRefs(mRealChannel)); 1.521 + g_object_unref(buf); 1.522 + return rv; 1.523 +} 1.524 +#endif // MOZ_ENABLE_GIO 1.525 + 1.526 +nsresult 1.527 +nsIconChannel::Init(nsIURI* aURI) 1.528 +{ 1.529 + nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI); 1.530 + NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI"); 1.531 + 1.532 + nsAutoCString stockIcon; 1.533 + iconURI->GetStockIcon(stockIcon); 1.534 + if (stockIcon.IsEmpty()) { 1.535 +#ifdef MOZ_ENABLE_GNOMEUI 1.536 + return InitWithGnome(iconURI); 1.537 +#else 1.538 +#ifdef MOZ_ENABLE_GIO 1.539 + return InitWithGIO(iconURI); 1.540 +#else 1.541 + return NS_ERROR_NOT_AVAILABLE; 1.542 +#endif 1.543 +#endif 1.544 + } 1.545 + 1.546 + // Search for stockIcon 1.547 + nsAutoCString iconSizeString; 1.548 + iconURI->GetIconSize(iconSizeString); 1.549 + 1.550 + nsAutoCString iconStateString; 1.551 + iconURI->GetIconState(iconStateString); 1.552 + 1.553 + GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get()); 1.554 + GtkStateType state = iconStateString.EqualsLiteral("disabled") ? 1.555 + GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; 1.556 + 1.557 + // First lookup the icon by stock id and text direction. 1.558 + GtkTextDirection direction = GTK_TEXT_DIR_NONE; 1.559 + if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-ltr"))) { 1.560 + direction = GTK_TEXT_DIR_LTR; 1.561 + } else if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-rtl"))) { 1.562 + direction = GTK_TEXT_DIR_RTL; 1.563 + } 1.564 + 1.565 + bool forceDirection = direction != GTK_TEXT_DIR_NONE; 1.566 + nsAutoCString stockID; 1.567 + bool useIconName = false; 1.568 + if (!forceDirection) { 1.569 + direction = gtk_widget_get_default_direction(); 1.570 + stockID = stockIcon; 1.571 + } else { 1.572 + // GTK versions < 2.22 use icon names from concatenating stock id with 1.573 + // -(rtl|ltr), which is how the moz-icon stock name is interpreted here. 1.574 + stockID = Substring(stockIcon, 0, stockIcon.Length() - 4); 1.575 + // However, if we lookup bidi icons by the stock name, then GTK versions 1.576 + // >= 2.22 will use a bidi lookup convention that most icon themes do not 1.577 + // yet follow. Therefore, we first check to see if the theme supports the 1.578 + // old icon name as this will have bidi support (if found). 1.579 + GtkIconTheme *icon_theme = gtk_icon_theme_get_default(); 1.580 + // Micking what gtk_icon_set_render_icon does with sizes, though it's not 1.581 + // critical as icons will be scaled to suit size. It just means we follow 1.582 + // the same pathes and so share caches. 1.583 + gint width, height; 1.584 + if (gtk_icon_size_lookup(icon_size, &width, &height)) { 1.585 + gint size = std::min(width, height); 1.586 + // We use gtk_icon_theme_lookup_icon() without 1.587 + // GTK_ICON_LOOKUP_USE_BUILTIN instead of gtk_icon_theme_has_icon() so 1.588 + // we don't pick up fallback icons added by distributions for backward 1.589 + // compatibility. 1.590 + GtkIconInfo *icon = 1.591 + gtk_icon_theme_lookup_icon(icon_theme, stockIcon.get(), 1.592 + size, (GtkIconLookupFlags)0); 1.593 + if (icon) { 1.594 + useIconName = true; 1.595 + gtk_icon_info_free(icon); 1.596 + } 1.597 + } 1.598 + } 1.599 + 1.600 + ensure_stock_image_widget(); 1.601 + GtkStyle *style = gtk_widget_get_style(gStockImageWidget); 1.602 + GtkIconSet *icon_set = nullptr; 1.603 + if (!useIconName) { 1.604 + icon_set = gtk_style_lookup_icon_set(style, stockID.get()); 1.605 + } 1.606 + 1.607 + if (!icon_set) { 1.608 + // Either we have choosen icon-name lookup for a bidi icon, or stockIcon is 1.609 + // not a stock id so we assume it is an icon name. 1.610 + useIconName = true; 1.611 + // Creating a GtkIconSet is a convenient way to allow the style to 1.612 + // render the icon, possibly with variations suitable for insensitive 1.613 + // states. 1.614 + icon_set = gtk_icon_set_new(); 1.615 + GtkIconSource *icon_source = gtk_icon_source_new(); 1.616 + 1.617 + gtk_icon_source_set_icon_name(icon_source, stockIcon.get()); 1.618 + gtk_icon_set_add_source(icon_set, icon_source); 1.619 + gtk_icon_source_free(icon_source); 1.620 + } 1.621 + 1.622 + GdkPixbuf *icon = 1.623 + gtk_icon_set_render_icon(icon_set, style, direction, state, 1.624 + icon_size, gStockImageWidget, nullptr); 1.625 + if (useIconName) { 1.626 + gtk_icon_set_unref(icon_set); 1.627 + } 1.628 + 1.629 + // According to documentation, gtk_icon_set_render_icon() never returns 1.630 + // nullptr, but it does return nullptr when we have the problem reported 1.631 + // here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13 1.632 + if (!icon) 1.633 + return NS_ERROR_NOT_AVAILABLE; 1.634 + 1.635 + nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI, 1.636 + getter_AddRefs(mRealChannel)); 1.637 + 1.638 + g_object_unref(icon); 1.639 + 1.640 + return rv; 1.641 +} 1.642 + 1.643 +void 1.644 +nsIconChannel::Shutdown() { 1.645 + if (gProtoWindow) { 1.646 + gtk_widget_destroy(gProtoWindow); 1.647 + gProtoWindow = nullptr; 1.648 + gStockImageWidget = nullptr; 1.649 + } 1.650 +#ifdef MOZ_ENABLE_GNOMEUI 1.651 + if (gIconTheme) { 1.652 + g_object_unref(G_OBJECT(gIconTheme)); 1.653 + gIconTheme = nullptr; 1.654 + } 1.655 + gTriedToLoadGnomeLibs = false; 1.656 + if (gLibGnomeUI) { 1.657 + PR_UnloadLibrary(gLibGnomeUI); 1.658 + gLibGnomeUI = nullptr; 1.659 + } 1.660 + if (gLibGnome) { 1.661 + PR_UnloadLibrary(gLibGnome); 1.662 + gLibGnome = nullptr; 1.663 + } 1.664 + if (gLibGnomeVFS) { 1.665 + PR_UnloadLibrary(gLibGnomeVFS); 1.666 + gLibGnomeVFS = nullptr; 1.667 + } 1.668 +#endif //MOZ_ENABLE_GNOMEUI 1.669 +}