Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* vim:set ts=2 sw=2 sts=2 cin et: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <stdlib.h>
7 #include <unistd.h>
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/Endian.h"
11 #include <algorithm>
13 #ifdef MOZ_ENABLE_GNOMEUI
14 // Older versions of these headers seem to be missing an extern "C"
15 extern "C" {
16 #include <libgnome/libgnome.h>
17 #include <libgnomeui/gnome-icon-theme.h>
18 #include <libgnomeui/gnome-icon-lookup.h>
20 #include <libgnomevfs/gnome-vfs-file-info.h>
21 #include <libgnomevfs/gnome-vfs-ops.h>
22 }
23 #endif
24 #ifdef MOZ_ENABLE_GIO
25 #include <gio/gio.h>
26 #endif
28 #include <gtk/gtk.h>
30 #include "nsMimeTypes.h"
31 #include "nsIMIMEService.h"
33 #include "nsIStringBundle.h"
35 #include "nsNetUtil.h"
36 #include "nsIURL.h"
37 #include "prlink.h"
39 #include "nsIconChannel.h"
41 NS_IMPL_ISUPPORTS(nsIconChannel,
42 nsIRequest,
43 nsIChannel)
45 #ifdef MOZ_ENABLE_GNOMEUI
46 // These let us have a soft dependency on libgnomeui rather than a hard one. These are just basically the prototypes
47 // of the functions in the libraries.
48 typedef char* (*_GnomeIconLookup_fn)(GtkIconTheme *icon_theme, GnomeThumbnailFactory *thumbnail_factory,
49 const char *file_uri, const char *custom_icon, GnomeVFSFileInfo *file_info,
50 const char *mime_type, GnomeIconLookupFlags flags, GnomeIconLookupResultFlags *result);
51 typedef GnomeIconTheme* (*_GnomeIconThemeNew_fn)(void);
52 typedef int (*_GnomeInit_fn)(const char *app_id, const char *app_version, int argc, char **argv, const struct poptOption *options,
53 int flags, poptContext *return_ctx);
54 typedef GnomeProgram* (*_GnomeProgramGet_fn)(void);
55 typedef GnomeVFSResult (*_GnomeVFSGetFileInfo_fn)(const gchar *text_uri, GnomeVFSFileInfo *info, GnomeVFSFileInfoOptions options);
56 typedef void (*_GnomeVFSFileInfoClear_fn)(GnomeVFSFileInfo *info);
58 static PRLibrary* gLibGnomeUI = nullptr;
59 static PRLibrary* gLibGnome = nullptr;
60 static PRLibrary* gLibGnomeVFS = nullptr;
61 static bool gTriedToLoadGnomeLibs = false;
63 static _GnomeIconLookup_fn _gnome_icon_lookup = nullptr;
64 static _GnomeIconThemeNew_fn _gnome_icon_theme_new = nullptr;
65 static _GnomeInit_fn _gnome_init = nullptr;
66 static _GnomeProgramGet_fn _gnome_program_get = nullptr;
67 static _GnomeVFSGetFileInfo_fn _gnome_vfs_get_file_info = nullptr;
68 static _GnomeVFSFileInfoClear_fn _gnome_vfs_file_info_clear = nullptr;
69 #endif //MOZ_ENABLE_GNOMEUI
71 static nsresult
72 moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI *aURI,
73 nsIChannel **aChannel)
74 {
75 int width = gdk_pixbuf_get_width(aPixbuf);
76 int height = gdk_pixbuf_get_height(aPixbuf);
77 NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
78 gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
79 gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
80 gdk_pixbuf_get_has_alpha(aPixbuf) &&
81 gdk_pixbuf_get_n_channels(aPixbuf) == 4,
82 NS_ERROR_UNEXPECTED);
84 const int n_channels = 4;
85 gsize buf_size = 2 + n_channels * height * width;
86 uint8_t * const buf = (uint8_t*)NS_Alloc(buf_size);
87 NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
88 uint8_t *out = buf;
90 *(out++) = width;
91 *(out++) = height;
93 const guchar * const pixels = gdk_pixbuf_get_pixels(aPixbuf);
94 int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels;
96 // encode the RGB data and the A data
97 const guchar * in = pixels;
98 for (int y = 0; y < height; ++y, in += rowextra) {
99 for (int x = 0; x < width; ++x) {
100 uint8_t r = *(in++);
101 uint8_t g = *(in++);
102 uint8_t b = *(in++);
103 uint8_t a = *(in++);
104 #define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255))
105 #if MOZ_LITTLE_ENDIAN
106 *(out++) = DO_PREMULTIPLY(b);
107 *(out++) = DO_PREMULTIPLY(g);
108 *(out++) = DO_PREMULTIPLY(r);
109 *(out++) = a;
110 #else
111 *(out++) = a;
112 *(out++) = DO_PREMULTIPLY(r);
113 *(out++) = DO_PREMULTIPLY(g);
114 *(out++) = DO_PREMULTIPLY(b);
115 #endif
116 #undef DO_PREMULTIPLY
117 }
118 }
120 NS_ASSERTION(out == buf + buf_size, "size miscalculation");
122 nsresult rv;
123 nsCOMPtr<nsIStringInputStream> stream =
124 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
125 NS_ENSURE_SUCCESS(rv, rv);
127 rv = stream->AdoptData((char*)buf, buf_size);
128 NS_ENSURE_SUCCESS(rv, rv);
130 rv = NS_NewInputStreamChannel(aChannel, aURI, stream,
131 NS_LITERAL_CSTRING(IMAGE_ICON_MS));
132 return rv;
133 }
135 static GtkWidget *gProtoWindow = nullptr;
136 static GtkWidget *gStockImageWidget = nullptr;
137 #ifdef MOZ_ENABLE_GNOMEUI
138 static GnomeIconTheme *gIconTheme = nullptr;
139 #endif //MOZ_ENABLE_GNOMEUI
141 static void
142 ensure_stock_image_widget()
143 {
144 // Only the style of the GtkImage needs to be used, but the widget is kept
145 // to track dynamic style changes.
146 if (!gProtoWindow) {
147 gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
148 GtkWidget* protoLayout = gtk_fixed_new();
149 gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
151 gStockImageWidget = gtk_image_new();
152 gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget);
154 gtk_widget_ensure_style(gStockImageWidget);
155 }
156 }
158 #ifdef MOZ_ENABLE_GNOMEUI
159 static nsresult
160 ensure_libgnomeui()
161 {
162 // Attempt to get the libgnomeui symbol references. We do it this way so that stock icons from Init()
163 // don't get held back by InitWithGnome()'s libgnomeui dependency.
164 if (!gTriedToLoadGnomeLibs) {
165 gLibGnomeUI = PR_LoadLibrary("libgnomeui-2.so.0");
166 if (!gLibGnomeUI)
167 return NS_ERROR_NOT_AVAILABLE;
169 _gnome_init = (_GnomeInit_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_init_with_popt_table");
170 _gnome_icon_theme_new = (_GnomeIconThemeNew_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_theme_new");
171 _gnome_icon_lookup = (_GnomeIconLookup_fn)PR_FindFunctionSymbol(gLibGnomeUI, "gnome_icon_lookup");
173 if (!_gnome_init || !_gnome_icon_theme_new || !_gnome_icon_lookup) {
174 PR_UnloadLibrary(gLibGnomeUI);
175 gLibGnomeUI = nullptr;
176 return NS_ERROR_NOT_AVAILABLE;
177 }
178 }
180 if (!gLibGnomeUI)
181 return NS_ERROR_NOT_AVAILABLE;
183 return NS_OK;
184 }
186 static nsresult
187 ensure_libgnome()
188 {
189 if (!gTriedToLoadGnomeLibs) {
190 gLibGnome = PR_LoadLibrary("libgnome-2.so.0");
191 if (!gLibGnome)
192 return NS_ERROR_NOT_AVAILABLE;
194 _gnome_program_get = (_GnomeProgramGet_fn)PR_FindFunctionSymbol(gLibGnome, "gnome_program_get");
195 if (!_gnome_program_get) {
196 PR_UnloadLibrary(gLibGnome);
197 gLibGnome = nullptr;
198 return NS_ERROR_NOT_AVAILABLE;
199 }
200 }
202 if (!gLibGnome)
203 return NS_ERROR_NOT_AVAILABLE;
205 return NS_OK;
206 }
208 static nsresult
209 ensure_libgnomevfs()
210 {
211 if (!gTriedToLoadGnomeLibs) {
212 gLibGnomeVFS = PR_LoadLibrary("libgnomevfs-2.so.0");
213 if (!gLibGnomeVFS)
214 return NS_ERROR_NOT_AVAILABLE;
216 _gnome_vfs_get_file_info = (_GnomeVFSGetFileInfo_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_get_file_info");
217 _gnome_vfs_file_info_clear = (_GnomeVFSFileInfoClear_fn)PR_FindFunctionSymbol(gLibGnomeVFS, "gnome_vfs_file_info_clear");
218 if (!_gnome_vfs_get_file_info || !_gnome_vfs_file_info_clear) {
219 PR_UnloadLibrary(gLibGnomeVFS);
220 gLibGnomeVFS = nullptr;
221 return NS_ERROR_NOT_AVAILABLE;
222 }
223 }
225 if (!gLibGnomeVFS)
226 return NS_ERROR_NOT_AVAILABLE;
228 return NS_OK;
229 }
230 #endif //MOZ_ENABLE_GNOMEUI
232 static GtkIconSize
233 moz_gtk_icon_size(const char *name)
234 {
235 if (strcmp(name, "button") == 0)
236 return GTK_ICON_SIZE_BUTTON;
238 if (strcmp(name, "menu") == 0)
239 return GTK_ICON_SIZE_MENU;
241 if (strcmp(name, "toolbar") == 0)
242 return GTK_ICON_SIZE_LARGE_TOOLBAR;
244 if (strcmp(name, "toolbarsmall") == 0)
245 return GTK_ICON_SIZE_SMALL_TOOLBAR;
247 if (strcmp(name, "dnd") == 0)
248 return GTK_ICON_SIZE_DND;
250 if (strcmp(name, "dialog") == 0)
251 return GTK_ICON_SIZE_DIALOG;
253 return GTK_ICON_SIZE_MENU;
254 }
256 #if defined(MOZ_ENABLE_GNOMEUI) || defined(MOZ_ENABLE_GIO)
257 static int32_t
258 GetIconSize(nsIMozIconURI *aIconURI)
259 {
260 nsAutoCString iconSizeString;
262 aIconURI->GetIconSize(iconSizeString);
263 if (iconSizeString.IsEmpty()) {
264 uint32_t size;
265 mozilla::DebugOnly<nsresult> rv = aIconURI->GetImageSize(&size);
266 NS_ASSERTION(NS_SUCCEEDED(rv), "GetImageSize failed");
267 return size;
268 } else {
269 int size;
271 GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
272 gtk_icon_size_lookup(icon_size, &size, nullptr);
273 return size;
274 }
275 }
277 /* Scale icon buffer to preferred size */
278 static nsresult
279 ScaleIconBuf(GdkPixbuf **aBuf, int32_t iconSize)
280 {
281 // Scale buffer only if width or height differ from preferred size
282 if (gdk_pixbuf_get_width(*aBuf) != iconSize &&
283 gdk_pixbuf_get_height(*aBuf) != iconSize) {
284 GdkPixbuf *scaled = gdk_pixbuf_scale_simple(*aBuf, iconSize, iconSize,
285 GDK_INTERP_BILINEAR);
286 // replace original buffer by scaled
287 g_object_unref(*aBuf);
288 *aBuf = scaled;
289 if (!scaled)
290 return NS_ERROR_OUT_OF_MEMORY;
291 }
292 return NS_OK;
293 }
294 #endif
296 #ifdef MOZ_ENABLE_GNOMEUI
297 nsresult
298 nsIconChannel::InitWithGnome(nsIMozIconURI *aIconURI)
299 {
300 nsresult rv;
302 if (NS_FAILED(ensure_libgnomeui()) || NS_FAILED(ensure_libgnome()) || NS_FAILED(ensure_libgnomevfs())) {
303 gTriedToLoadGnomeLibs = true;
304 return NS_ERROR_NOT_AVAILABLE;
305 }
307 gTriedToLoadGnomeLibs = true;
309 if (!_gnome_program_get()) {
310 // Get the brandShortName from the string bundle to pass to GNOME
311 // as the application name. This may be used for things such as
312 // the title of grouped windows in the panel.
313 nsCOMPtr<nsIStringBundleService> bundleService =
314 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
316 NS_ASSERTION(bundleService, "String bundle service must be present!");
318 nsCOMPtr<nsIStringBundle> bundle;
319 bundleService->CreateBundle("chrome://branding/locale/brand.properties",
320 getter_AddRefs(bundle));
321 nsAutoString appName;
323 if (bundle) {
324 bundle->GetStringFromName(MOZ_UTF16("brandShortName"),
325 getter_Copies(appName));
326 } else {
327 NS_WARNING("brand.properties not present, using default application name");
328 appName.Assign(NS_LITERAL_STRING("Gecko"));
329 }
331 char* empty[] = { "" };
332 _gnome_init(NS_ConvertUTF16toUTF8(appName).get(),
333 "1.0", 1, empty, nullptr, 0, nullptr);
334 }
336 uint32_t iconSize = GetIconSize(aIconURI);
337 nsAutoCString type;
338 aIconURI->GetContentType(type);
340 GnomeVFSFileInfo fileInfo = {0};
341 fileInfo.refcount = 1; // In case some GnomeVFS function addrefs and releases it
343 nsAutoCString spec;
344 nsCOMPtr<nsIURL> url;
345 rv = aIconURI->GetIconURL(getter_AddRefs(url));
346 if (url) {
347 url->GetAsciiSpec(spec);
348 // Only ask gnome-vfs for a GnomeVFSFileInfo for file: uris, to avoid a
349 // network request
350 bool isFile;
351 if (NS_SUCCEEDED(url->SchemeIs("file", &isFile)) && isFile) {
352 _gnome_vfs_get_file_info(spec.get(), &fileInfo, GNOME_VFS_FILE_INFO_DEFAULT);
353 }
354 else {
355 // The filename we get is UTF-8-compatible, which matches gnome expectations.
356 // See also: http://lists.gnome.org/archives/gnome-vfs-list/2004-March/msg00049.html
357 // "Whenever we can detect the charset used for the URI type we try to
358 // convert it to/from utf8 automatically inside gnome-vfs."
359 // I'll interpret that as "otherwise, this field is random junk".
360 nsAutoCString name;
361 url->GetFileName(name);
362 fileInfo.name = g_strdup(name.get());
364 if (!type.IsEmpty()) {
365 fileInfo.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE;
366 fileInfo.mime_type = g_strdup(type.get());
367 }
368 }
369 }
371 if (type.IsEmpty()) {
372 nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
373 if (ms) {
374 nsAutoCString fileExt;
375 aIconURI->GetFileExtension(fileExt);
376 if (!fileExt.IsEmpty()) {
377 ms->GetTypeFromExtension(fileExt, type);
378 }
379 }
380 }
381 // Get the icon theme
382 if (!gIconTheme) {
383 gIconTheme = _gnome_icon_theme_new();
385 if (!gIconTheme) {
386 _gnome_vfs_file_info_clear(&fileInfo);
387 return NS_ERROR_NOT_AVAILABLE;
388 }
389 }
391 char* name = _gnome_icon_lookup(gIconTheme, nullptr, spec.get(), nullptr,
392 &fileInfo, type.get(),
393 GNOME_ICON_LOOKUP_FLAGS_NONE, nullptr);
395 _gnome_vfs_file_info_clear(&fileInfo);
396 if (!name)
397 return NS_ERROR_NOT_AVAILABLE;
399 // Get the default theme associated with the screen
400 // Do NOT free.
401 GtkIconTheme *theme = gtk_icon_theme_get_default();
402 if (!theme) {
403 g_free(name);
404 return NS_ERROR_UNEXPECTED;
405 }
407 GError *err = nullptr;
408 GdkPixbuf* buf = gtk_icon_theme_load_icon(theme, name, iconSize, (GtkIconLookupFlags)0, &err);
409 g_free(name);
411 if (!buf) {
412 if (err)
413 g_error_free(err);
414 return NS_ERROR_UNEXPECTED;
415 }
417 rv = ScaleIconBuf(&buf, iconSize);
418 NS_ENSURE_SUCCESS(rv, rv);
420 rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
421 getter_AddRefs(mRealChannel));
422 g_object_unref(buf);
423 return rv;
424 }
425 #endif // MOZ_ENABLE_GNOMEUI
427 #ifdef MOZ_ENABLE_GIO
428 nsresult
429 nsIconChannel::InitWithGIO(nsIMozIconURI *aIconURI)
430 {
431 GIcon *icon = nullptr;
432 nsCOMPtr<nsIURL> fileURI;
434 // Read icon content
435 aIconURI->GetIconURL(getter_AddRefs(fileURI));
437 // Get icon for file specified by URI
438 if (fileURI) {
439 bool isFile;
440 nsAutoCString spec;
441 fileURI->GetAsciiSpec(spec);
442 if (NS_SUCCEEDED(fileURI->SchemeIs("file", &isFile)) && isFile) {
443 GFile *file = g_file_new_for_uri(spec.get());
444 GFileInfo *fileInfo = g_file_query_info(file,
445 G_FILE_ATTRIBUTE_STANDARD_ICON,
446 G_FILE_QUERY_INFO_NONE,
447 nullptr, nullptr);
448 g_object_unref(file);
449 if (fileInfo) {
450 // icon from g_content_type_get_icon doesn't need unref
451 icon = g_file_info_get_icon(fileInfo);
452 if (icon)
453 g_object_ref(icon);
454 g_object_unref(fileInfo);
455 }
456 }
457 }
459 // Try to get icon by using MIME type
460 if (!icon) {
461 nsAutoCString type;
462 aIconURI->GetContentType(type);
463 // Try to get MIME type from file extension by using nsIMIMEService
464 if (type.IsEmpty()) {
465 nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
466 if (ms) {
467 nsAutoCString fileExt;
468 aIconURI->GetFileExtension(fileExt);
469 ms->GetTypeFromExtension(fileExt, type);
470 }
471 }
472 char *ctype = nullptr; // character representation of content type
473 if (!type.IsEmpty()) {
474 ctype = g_content_type_from_mime_type(type.get());
475 }
476 if (ctype) {
477 icon = g_content_type_get_icon(ctype);
478 g_free(ctype);
479 }
480 }
482 // Get default icon theme
483 GtkIconTheme *iconTheme = gtk_icon_theme_get_default();
484 GtkIconInfo *iconInfo = nullptr;
485 // Get icon size
486 int32_t iconSize = GetIconSize(aIconURI);
488 if (icon) {
489 // Use icon and theme to get GtkIconInfo
490 iconInfo = gtk_icon_theme_lookup_by_gicon(iconTheme,
491 icon, iconSize,
492 (GtkIconLookupFlags)0);
493 g_object_unref(icon);
494 }
496 if (!iconInfo) {
497 // Mozilla's mimetype lookup failed. Try the "unknown" icon.
498 iconInfo = gtk_icon_theme_lookup_icon(iconTheme,
499 "unknown", iconSize,
500 (GtkIconLookupFlags)0);
501 if (!iconInfo) {
502 return NS_ERROR_NOT_AVAILABLE;
503 }
504 }
506 // Create a GdkPixbuf buffer containing icon and scale it
507 GdkPixbuf* buf = gtk_icon_info_load_icon(iconInfo, nullptr);
508 gtk_icon_info_free(iconInfo);
509 if (!buf) {
510 return NS_ERROR_UNEXPECTED;
511 }
513 nsresult rv = ScaleIconBuf(&buf, iconSize);
514 NS_ENSURE_SUCCESS(rv, rv);
516 rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
517 getter_AddRefs(mRealChannel));
518 g_object_unref(buf);
519 return rv;
520 }
521 #endif // MOZ_ENABLE_GIO
523 nsresult
524 nsIconChannel::Init(nsIURI* aURI)
525 {
526 nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
527 NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
529 nsAutoCString stockIcon;
530 iconURI->GetStockIcon(stockIcon);
531 if (stockIcon.IsEmpty()) {
532 #ifdef MOZ_ENABLE_GNOMEUI
533 return InitWithGnome(iconURI);
534 #else
535 #ifdef MOZ_ENABLE_GIO
536 return InitWithGIO(iconURI);
537 #else
538 return NS_ERROR_NOT_AVAILABLE;
539 #endif
540 #endif
541 }
543 // Search for stockIcon
544 nsAutoCString iconSizeString;
545 iconURI->GetIconSize(iconSizeString);
547 nsAutoCString iconStateString;
548 iconURI->GetIconState(iconStateString);
550 GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
551 GtkStateType state = iconStateString.EqualsLiteral("disabled") ?
552 GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
554 // First lookup the icon by stock id and text direction.
555 GtkTextDirection direction = GTK_TEXT_DIR_NONE;
556 if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-ltr"))) {
557 direction = GTK_TEXT_DIR_LTR;
558 } else if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-rtl"))) {
559 direction = GTK_TEXT_DIR_RTL;
560 }
562 bool forceDirection = direction != GTK_TEXT_DIR_NONE;
563 nsAutoCString stockID;
564 bool useIconName = false;
565 if (!forceDirection) {
566 direction = gtk_widget_get_default_direction();
567 stockID = stockIcon;
568 } else {
569 // GTK versions < 2.22 use icon names from concatenating stock id with
570 // -(rtl|ltr), which is how the moz-icon stock name is interpreted here.
571 stockID = Substring(stockIcon, 0, stockIcon.Length() - 4);
572 // However, if we lookup bidi icons by the stock name, then GTK versions
573 // >= 2.22 will use a bidi lookup convention that most icon themes do not
574 // yet follow. Therefore, we first check to see if the theme supports the
575 // old icon name as this will have bidi support (if found).
576 GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
577 // Micking what gtk_icon_set_render_icon does with sizes, though it's not
578 // critical as icons will be scaled to suit size. It just means we follow
579 // the same pathes and so share caches.
580 gint width, height;
581 if (gtk_icon_size_lookup(icon_size, &width, &height)) {
582 gint size = std::min(width, height);
583 // We use gtk_icon_theme_lookup_icon() without
584 // GTK_ICON_LOOKUP_USE_BUILTIN instead of gtk_icon_theme_has_icon() so
585 // we don't pick up fallback icons added by distributions for backward
586 // compatibility.
587 GtkIconInfo *icon =
588 gtk_icon_theme_lookup_icon(icon_theme, stockIcon.get(),
589 size, (GtkIconLookupFlags)0);
590 if (icon) {
591 useIconName = true;
592 gtk_icon_info_free(icon);
593 }
594 }
595 }
597 ensure_stock_image_widget();
598 GtkStyle *style = gtk_widget_get_style(gStockImageWidget);
599 GtkIconSet *icon_set = nullptr;
600 if (!useIconName) {
601 icon_set = gtk_style_lookup_icon_set(style, stockID.get());
602 }
604 if (!icon_set) {
605 // Either we have choosen icon-name lookup for a bidi icon, or stockIcon is
606 // not a stock id so we assume it is an icon name.
607 useIconName = true;
608 // Creating a GtkIconSet is a convenient way to allow the style to
609 // render the icon, possibly with variations suitable for insensitive
610 // states.
611 icon_set = gtk_icon_set_new();
612 GtkIconSource *icon_source = gtk_icon_source_new();
614 gtk_icon_source_set_icon_name(icon_source, stockIcon.get());
615 gtk_icon_set_add_source(icon_set, icon_source);
616 gtk_icon_source_free(icon_source);
617 }
619 GdkPixbuf *icon =
620 gtk_icon_set_render_icon(icon_set, style, direction, state,
621 icon_size, gStockImageWidget, nullptr);
622 if (useIconName) {
623 gtk_icon_set_unref(icon_set);
624 }
626 // According to documentation, gtk_icon_set_render_icon() never returns
627 // nullptr, but it does return nullptr when we have the problem reported
628 // here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13
629 if (!icon)
630 return NS_ERROR_NOT_AVAILABLE;
632 nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI,
633 getter_AddRefs(mRealChannel));
635 g_object_unref(icon);
637 return rv;
638 }
640 void
641 nsIconChannel::Shutdown() {
642 if (gProtoWindow) {
643 gtk_widget_destroy(gProtoWindow);
644 gProtoWindow = nullptr;
645 gStockImageWidget = nullptr;
646 }
647 #ifdef MOZ_ENABLE_GNOMEUI
648 if (gIconTheme) {
649 g_object_unref(G_OBJECT(gIconTheme));
650 gIconTheme = nullptr;
651 }
652 gTriedToLoadGnomeLibs = false;
653 if (gLibGnomeUI) {
654 PR_UnloadLibrary(gLibGnomeUI);
655 gLibGnomeUI = nullptr;
656 }
657 if (gLibGnome) {
658 PR_UnloadLibrary(gLibGnome);
659 gLibGnome = nullptr;
660 }
661 if (gLibGnomeVFS) {
662 PR_UnloadLibrary(gLibGnomeVFS);
663 gLibGnomeVFS = nullptr;
664 }
665 #endif //MOZ_ENABLE_GNOMEUI
666 }