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