|
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/. */ |
|
5 |
|
6 #include <stdlib.h> |
|
7 #include <unistd.h> |
|
8 |
|
9 #include "mozilla/DebugOnly.h" |
|
10 #include "mozilla/Endian.h" |
|
11 #include <algorithm> |
|
12 |
|
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> |
|
19 |
|
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 |
|
27 |
|
28 #include <gtk/gtk.h> |
|
29 |
|
30 #include "nsMimeTypes.h" |
|
31 #include "nsIMIMEService.h" |
|
32 |
|
33 #include "nsIStringBundle.h" |
|
34 |
|
35 #include "nsNetUtil.h" |
|
36 #include "nsIURL.h" |
|
37 #include "prlink.h" |
|
38 |
|
39 #include "nsIconChannel.h" |
|
40 |
|
41 NS_IMPL_ISUPPORTS(nsIconChannel, |
|
42 nsIRequest, |
|
43 nsIChannel) |
|
44 |
|
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); |
|
57 |
|
58 static PRLibrary* gLibGnomeUI = nullptr; |
|
59 static PRLibrary* gLibGnome = nullptr; |
|
60 static PRLibrary* gLibGnomeVFS = nullptr; |
|
61 static bool gTriedToLoadGnomeLibs = false; |
|
62 |
|
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 |
|
70 |
|
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); |
|
83 |
|
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; |
|
89 |
|
90 *(out++) = width; |
|
91 *(out++) = height; |
|
92 |
|
93 const guchar * const pixels = gdk_pixbuf_get_pixels(aPixbuf); |
|
94 int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels; |
|
95 |
|
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 } |
|
119 |
|
120 NS_ASSERTION(out == buf + buf_size, "size miscalculation"); |
|
121 |
|
122 nsresult rv; |
|
123 nsCOMPtr<nsIStringInputStream> stream = |
|
124 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); |
|
125 NS_ENSURE_SUCCESS(rv, rv); |
|
126 |
|
127 rv = stream->AdoptData((char*)buf, buf_size); |
|
128 NS_ENSURE_SUCCESS(rv, rv); |
|
129 |
|
130 rv = NS_NewInputStreamChannel(aChannel, aURI, stream, |
|
131 NS_LITERAL_CSTRING(IMAGE_ICON_MS)); |
|
132 return rv; |
|
133 } |
|
134 |
|
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 |
|
140 |
|
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); |
|
150 |
|
151 gStockImageWidget = gtk_image_new(); |
|
152 gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget); |
|
153 |
|
154 gtk_widget_ensure_style(gStockImageWidget); |
|
155 } |
|
156 } |
|
157 |
|
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; |
|
168 |
|
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"); |
|
172 |
|
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 } |
|
179 |
|
180 if (!gLibGnomeUI) |
|
181 return NS_ERROR_NOT_AVAILABLE; |
|
182 |
|
183 return NS_OK; |
|
184 } |
|
185 |
|
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; |
|
193 |
|
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 } |
|
201 |
|
202 if (!gLibGnome) |
|
203 return NS_ERROR_NOT_AVAILABLE; |
|
204 |
|
205 return NS_OK; |
|
206 } |
|
207 |
|
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; |
|
215 |
|
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 } |
|
224 |
|
225 if (!gLibGnomeVFS) |
|
226 return NS_ERROR_NOT_AVAILABLE; |
|
227 |
|
228 return NS_OK; |
|
229 } |
|
230 #endif //MOZ_ENABLE_GNOMEUI |
|
231 |
|
232 static GtkIconSize |
|
233 moz_gtk_icon_size(const char *name) |
|
234 { |
|
235 if (strcmp(name, "button") == 0) |
|
236 return GTK_ICON_SIZE_BUTTON; |
|
237 |
|
238 if (strcmp(name, "menu") == 0) |
|
239 return GTK_ICON_SIZE_MENU; |
|
240 |
|
241 if (strcmp(name, "toolbar") == 0) |
|
242 return GTK_ICON_SIZE_LARGE_TOOLBAR; |
|
243 |
|
244 if (strcmp(name, "toolbarsmall") == 0) |
|
245 return GTK_ICON_SIZE_SMALL_TOOLBAR; |
|
246 |
|
247 if (strcmp(name, "dnd") == 0) |
|
248 return GTK_ICON_SIZE_DND; |
|
249 |
|
250 if (strcmp(name, "dialog") == 0) |
|
251 return GTK_ICON_SIZE_DIALOG; |
|
252 |
|
253 return GTK_ICON_SIZE_MENU; |
|
254 } |
|
255 |
|
256 #if defined(MOZ_ENABLE_GNOMEUI) || defined(MOZ_ENABLE_GIO) |
|
257 static int32_t |
|
258 GetIconSize(nsIMozIconURI *aIconURI) |
|
259 { |
|
260 nsAutoCString iconSizeString; |
|
261 |
|
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; |
|
270 |
|
271 GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get()); |
|
272 gtk_icon_size_lookup(icon_size, &size, nullptr); |
|
273 return size; |
|
274 } |
|
275 } |
|
276 |
|
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 |
|
295 |
|
296 #ifdef MOZ_ENABLE_GNOMEUI |
|
297 nsresult |
|
298 nsIconChannel::InitWithGnome(nsIMozIconURI *aIconURI) |
|
299 { |
|
300 nsresult rv; |
|
301 |
|
302 if (NS_FAILED(ensure_libgnomeui()) || NS_FAILED(ensure_libgnome()) || NS_FAILED(ensure_libgnomevfs())) { |
|
303 gTriedToLoadGnomeLibs = true; |
|
304 return NS_ERROR_NOT_AVAILABLE; |
|
305 } |
|
306 |
|
307 gTriedToLoadGnomeLibs = true; |
|
308 |
|
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); |
|
315 |
|
316 NS_ASSERTION(bundleService, "String bundle service must be present!"); |
|
317 |
|
318 nsCOMPtr<nsIStringBundle> bundle; |
|
319 bundleService->CreateBundle("chrome://branding/locale/brand.properties", |
|
320 getter_AddRefs(bundle)); |
|
321 nsAutoString appName; |
|
322 |
|
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 } |
|
330 |
|
331 char* empty[] = { "" }; |
|
332 _gnome_init(NS_ConvertUTF16toUTF8(appName).get(), |
|
333 "1.0", 1, empty, nullptr, 0, nullptr); |
|
334 } |
|
335 |
|
336 uint32_t iconSize = GetIconSize(aIconURI); |
|
337 nsAutoCString type; |
|
338 aIconURI->GetContentType(type); |
|
339 |
|
340 GnomeVFSFileInfo fileInfo = {0}; |
|
341 fileInfo.refcount = 1; // In case some GnomeVFS function addrefs and releases it |
|
342 |
|
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()); |
|
363 |
|
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 } |
|
370 |
|
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(); |
|
384 |
|
385 if (!gIconTheme) { |
|
386 _gnome_vfs_file_info_clear(&fileInfo); |
|
387 return NS_ERROR_NOT_AVAILABLE; |
|
388 } |
|
389 } |
|
390 |
|
391 char* name = _gnome_icon_lookup(gIconTheme, nullptr, spec.get(), nullptr, |
|
392 &fileInfo, type.get(), |
|
393 GNOME_ICON_LOOKUP_FLAGS_NONE, nullptr); |
|
394 |
|
395 _gnome_vfs_file_info_clear(&fileInfo); |
|
396 if (!name) |
|
397 return NS_ERROR_NOT_AVAILABLE; |
|
398 |
|
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 } |
|
406 |
|
407 GError *err = nullptr; |
|
408 GdkPixbuf* buf = gtk_icon_theme_load_icon(theme, name, iconSize, (GtkIconLookupFlags)0, &err); |
|
409 g_free(name); |
|
410 |
|
411 if (!buf) { |
|
412 if (err) |
|
413 g_error_free(err); |
|
414 return NS_ERROR_UNEXPECTED; |
|
415 } |
|
416 |
|
417 rv = ScaleIconBuf(&buf, iconSize); |
|
418 NS_ENSURE_SUCCESS(rv, rv); |
|
419 |
|
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 |
|
426 |
|
427 #ifdef MOZ_ENABLE_GIO |
|
428 nsresult |
|
429 nsIconChannel::InitWithGIO(nsIMozIconURI *aIconURI) |
|
430 { |
|
431 GIcon *icon = nullptr; |
|
432 nsCOMPtr<nsIURL> fileURI; |
|
433 |
|
434 // Read icon content |
|
435 aIconURI->GetIconURL(getter_AddRefs(fileURI)); |
|
436 |
|
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 } |
|
458 |
|
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 } |
|
481 |
|
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); |
|
487 |
|
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 } |
|
495 |
|
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 } |
|
505 |
|
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 } |
|
512 |
|
513 nsresult rv = ScaleIconBuf(&buf, iconSize); |
|
514 NS_ENSURE_SUCCESS(rv, rv); |
|
515 |
|
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 |
|
522 |
|
523 nsresult |
|
524 nsIconChannel::Init(nsIURI* aURI) |
|
525 { |
|
526 nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI); |
|
527 NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI"); |
|
528 |
|
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 } |
|
542 |
|
543 // Search for stockIcon |
|
544 nsAutoCString iconSizeString; |
|
545 iconURI->GetIconSize(iconSizeString); |
|
546 |
|
547 nsAutoCString iconStateString; |
|
548 iconURI->GetIconState(iconStateString); |
|
549 |
|
550 GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get()); |
|
551 GtkStateType state = iconStateString.EqualsLiteral("disabled") ? |
|
552 GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; |
|
553 |
|
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 } |
|
561 |
|
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 } |
|
596 |
|
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 } |
|
603 |
|
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(); |
|
613 |
|
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 } |
|
618 |
|
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 } |
|
625 |
|
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; |
|
631 |
|
632 nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI, |
|
633 getter_AddRefs(mRealChannel)); |
|
634 |
|
635 g_object_unref(icon); |
|
636 |
|
637 return rv; |
|
638 } |
|
639 |
|
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 } |