summaryrefslogtreecommitdiffstats
path: root/image/decoders/icon
diff options
context:
space:
mode:
Diffstat (limited to 'image/decoders/icon')
-rw-r--r--image/decoders/icon/android/moz.build13
-rw-r--r--image/decoders/icon/android/nsIconChannel.cpp145
-rw-r--r--image/decoders/icon/android/nsIconChannel.h46
-rw-r--r--image/decoders/icon/gtk/moz.build16
-rw-r--r--image/decoders/icon/gtk/nsIconChannel.cpp427
-rw-r--r--image/decoders/icon/gtk/nsIconChannel.h43
-rw-r--r--image/decoders/icon/mac/moz.build11
-rw-r--r--image/decoders/icon/mac/nsIconChannel.h59
-rw-r--r--image/decoders/icon/mac/nsIconChannelCocoa.mm565
-rw-r--r--image/decoders/icon/moz.build32
-rw-r--r--image/decoders/icon/nsIconModule.cpp60
-rw-r--r--image/decoders/icon/nsIconProtocolHandler.cpp126
-rw-r--r--image/decoders/icon/nsIconProtocolHandler.h26
-rw-r--r--image/decoders/icon/nsIconURI.cpp699
-rw-r--r--image/decoders/icon/nsIconURI.h63
-rw-r--r--image/decoders/icon/win/moz.build11
-rw-r--r--image/decoders/icon/win/nsIconChannel.cpp841
-rw-r--r--image/decoders/icon/win/nsIconChannel.h66
18 files changed, 3249 insertions, 0 deletions
diff --git a/image/decoders/icon/android/moz.build b/image/decoders/icon/android/moz.build
new file mode 100644
index 000000000..5e58ff0b6
--- /dev/null
+++ b/image/decoders/icon/android/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsIconChannel.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
diff --git a/image/decoders/icon/android/nsIconChannel.cpp b/image/decoders/icon/android/nsIconChannel.cpp
new file mode 100644
index 000000000..5670bf2f9
--- /dev/null
+++ b/image/decoders/icon/android/nsIconChannel.cpp
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include "mozilla/dom/ContentChild.h"
+#include "nsMimeTypes.h"
+#include "nsIURL.h"
+#include "nsXULAppAPI.h"
+#include "AndroidBridge.h"
+#include "nsIconChannel.h"
+#include "nsIStringStream.h"
+#include "nsNetUtil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNullPrincipal.h"
+
+NS_IMPL_ISUPPORTS(nsIconChannel,
+ nsIRequest,
+ nsIChannel)
+
+using namespace mozilla;
+using mozilla::dom::ContentChild;
+
+static nsresult
+GetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize,
+ uint8_t* const aBuf)
+{
+ if (!AndroidBridge::Bridge()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, aBuf);
+
+ return NS_OK;
+}
+
+static nsresult
+CallRemoteGetIconForExtension(const nsACString& aFileExt, uint32_t aIconSize,
+ uint8_t* const aBuf)
+{
+ NS_ENSURE_TRUE(aBuf != nullptr, NS_ERROR_NULL_POINTER);
+
+ // An array has to be used to get data from remote process
+ InfallibleTArray<uint8_t> bits;
+ uint32_t bufSize = aIconSize * aIconSize * 4;
+
+ if (!ContentChild::GetSingleton()->SendGetIconForExtension(
+ PromiseFlatCString(aFileExt), aIconSize, &bits)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(bits.Length() == bufSize, "Pixels array is incomplete");
+ if (bits.Length() != bufSize) {
+ return NS_ERROR_FAILURE;
+ }
+
+ memcpy(aBuf, bits.Elements(), bufSize);
+
+ return NS_OK;
+}
+
+static nsresult
+moz_icon_to_channel(nsIURI* aURI, const nsACString& aFileExt,
+ uint32_t aIconSize, nsIChannel** aChannel)
+{
+ NS_ENSURE_TRUE(aIconSize < 256 && aIconSize > 0, NS_ERROR_UNEXPECTED);
+
+ int width = aIconSize;
+ int height = aIconSize;
+
+ // moz-icon data should have two bytes for the size,
+ // then the ARGB pixel values with pre-multiplied Alpha
+ const int channels = 4;
+ long int buf_size = 2 + channels * height * width;
+ uint8_t* const buf = (uint8_t*)moz_xmalloc(buf_size);
+ NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
+ uint8_t* out = buf;
+
+ *(out++) = width;
+ *(out++) = height;
+
+ nsresult rv;
+ if (XRE_IsParentProcess()) {
+ rv = GetIconForExtension(aFileExt, aIconSize, out);
+ } else {
+ rv = CallRemoteGetIconForExtension(aFileExt, aIconSize, out);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Encode the RGBA data
+ const uint8_t* in = out;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ uint8_t r = *(in++);
+ uint8_t g = *(in++);
+ uint8_t b = *(in++);
+ uint8_t a = *(in++);
+#define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255))
+ *(out++) = DO_PREMULTIPLY(b);
+ *(out++) = DO_PREMULTIPLY(g);
+ *(out++) = DO_PREMULTIPLY(r);
+ *(out++) = a;
+#undef DO_PREMULTIPLY
+ }
+ }
+
+ nsCOMPtr<nsIStringInputStream> stream =
+ do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = stream->AdoptData((char*)buf, buf_size);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
+ // this iconChannel. Use the most restrictive security settings for the
+ // temporary loadInfo to make sure the channel can not be openend.
+ nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
+ return NS_NewInputStreamChannel(aChannel,
+ aURI,
+ stream,
+ nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE,
+ NS_LITERAL_CSTRING(IMAGE_ICON_MS));
+}
+
+nsresult
+nsIconChannel::Init(nsIURI* aURI)
+{
+ nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
+ NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
+
+ nsAutoCString stockIcon;
+ iconURI->GetStockIcon(stockIcon);
+
+ uint32_t desiredImageSize;
+ iconURI->GetImageSize(&desiredImageSize);
+
+ nsAutoCString iconFileExt;
+ iconURI->GetFileExtension(iconFileExt);
+
+ return moz_icon_to_channel(iconURI, iconFileExt, desiredImageSize,
+ getter_AddRefs(mRealChannel));
+}
diff --git a/image/decoders/icon/android/nsIconChannel.h b/image/decoders/icon/android/nsIconChannel.h
new file mode 100644
index 000000000..be5542998
--- /dev/null
+++ b/image/decoders/icon/android/nsIconChannel.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_decoders_icon_android_nsIconChannel_h
+#define mozilla_image_decoders_icon_android_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsIChannel.h"
+#include "nsIURI.h"
+#include "nsIIconURI.h"
+#include "nsCOMPtr.h"
+
+/**
+ * This class is the Android implementation of nsIconChannel.
+ * It asks Android for an icon, and creates a new channel for
+ * that file to which all calls will be proxied.
+ */
+class nsIconChannel final : public nsIChannel {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIREQUEST(mRealChannel->)
+ NS_FORWARD_NSICHANNEL(mRealChannel->)
+
+ nsIconChannel() { }
+
+ /**
+ * Called by nsIconProtocolHandler after it creates this channel.
+ * Must be called before calling any other function on this object.
+ * If this method fails, no other function must be called on this object.
+ */
+ nsresult Init(nsIURI* aURI);
+
+ private:
+ ~nsIconChannel() { }
+
+ /**
+ * The channel to the temp icon file (e.g. to /tmp/2qy9wjqw.html).
+ * Will always be non-null after a successful Init.
+ */
+ nsCOMPtr<nsIChannel> mRealChannel;
+};
+
+#endif // mozilla_image_decoders_icon_android_nsIconChannel_h
diff --git a/image/decoders/icon/gtk/moz.build b/image/decoders/icon/gtk/moz.build
new file mode 100644
index 000000000..a8116ca87
--- /dev/null
+++ b/image/decoders/icon/gtk/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsIconChannel.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_ENABLE_GNOMEUI']:
+ CXXFLAGS += CONFIG['MOZ_GNOMEUI_CFLAGS']
+else:
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/image/decoders/icon/gtk/nsIconChannel.cpp b/image/decoders/icon/gtk/nsIconChannel.cpp
new file mode 100644
index 000000000..b014e4c04
--- /dev/null
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -0,0 +1,427 @@
+/* vim:set ts=2 sw=2 sts=2 cin et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIconChannel.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/EndianUtils.h"
+#include <algorithm>
+
+#ifdef MOZ_ENABLE_GIO
+#include <gio/gio.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "nsMimeTypes.h"
+#include "nsIMIMEService.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsNetUtil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIStringStream.h"
+#include "nsServiceManagerUtils.h"
+#include "nsNullPrincipal.h"
+#include "nsIURL.h"
+#include "prlink.h"
+
+NS_IMPL_ISUPPORTS(nsIconChannel,
+ nsIRequest,
+ nsIChannel)
+
+static nsresult
+moz_gdk_pixbuf_to_channel(GdkPixbuf* aPixbuf, nsIURI* aURI,
+ nsIChannel** aChannel)
+{
+ int width = gdk_pixbuf_get_width(aPixbuf);
+ int height = gdk_pixbuf_get_height(aPixbuf);
+ NS_ENSURE_TRUE(height < 256 && width < 256 && height > 0 && width > 0 &&
+ gdk_pixbuf_get_colorspace(aPixbuf) == GDK_COLORSPACE_RGB &&
+ gdk_pixbuf_get_bits_per_sample(aPixbuf) == 8 &&
+ gdk_pixbuf_get_has_alpha(aPixbuf) &&
+ gdk_pixbuf_get_n_channels(aPixbuf) == 4,
+ NS_ERROR_UNEXPECTED);
+
+ const int n_channels = 4;
+ gsize buf_size = 2 + n_channels * height * width;
+ uint8_t* const buf = (uint8_t*)moz_xmalloc(buf_size);
+ NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
+ uint8_t* out = buf;
+
+ *(out++) = width;
+ *(out++) = height;
+
+ const guchar* const pixels = gdk_pixbuf_get_pixels(aPixbuf);
+ int rowextra = gdk_pixbuf_get_rowstride(aPixbuf) - width * n_channels;
+
+ // encode the RGB data and the A data
+ const guchar* in = pixels;
+ for (int y = 0; y < height; ++y, in += rowextra) {
+ for (int x = 0; x < width; ++x) {
+ uint8_t r = *(in++);
+ uint8_t g = *(in++);
+ uint8_t b = *(in++);
+ uint8_t a = *(in++);
+#define DO_PREMULTIPLY(c_) uint8_t(uint16_t(c_) * uint16_t(a) / uint16_t(255))
+#if MOZ_LITTLE_ENDIAN
+ *(out++) = DO_PREMULTIPLY(b);
+ *(out++) = DO_PREMULTIPLY(g);
+ *(out++) = DO_PREMULTIPLY(r);
+ *(out++) = a;
+#else
+ *(out++) = a;
+ *(out++) = DO_PREMULTIPLY(r);
+ *(out++) = DO_PREMULTIPLY(g);
+ *(out++) = DO_PREMULTIPLY(b);
+#endif
+#undef DO_PREMULTIPLY
+ }
+ }
+
+ NS_ASSERTION(out == buf + buf_size, "size miscalculation");
+
+ nsresult rv;
+ nsCOMPtr<nsIStringInputStream> stream =
+ do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+
+ // Prevent the leaking of buf
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ free(buf);
+ return rv;
+ }
+
+ // stream takes ownership of buf and will free it on destruction.
+ // This function cannot fail.
+ rv = stream->AdoptData((char*)buf, buf_size);
+
+ // If this no longer holds then re-examine buf's lifetime.
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
+ // this iconChannel. Use the most restrictive security settings for the
+ // temporary loadInfo to make sure the channel can not be openend.
+ nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
+ return NS_NewInputStreamChannel(aChannel,
+ aURI,
+ stream,
+ nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE,
+ NS_LITERAL_CSTRING(IMAGE_ICON_MS));
+}
+
+static GtkWidget* gProtoWindow = nullptr;
+static GtkWidget* gStockImageWidget = nullptr;
+
+static void
+ensure_stock_image_widget()
+{
+ // Only the style of the GtkImage needs to be used, but the widget is kept
+ // to track dynamic style changes.
+ if (!gProtoWindow) {
+ gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP);
+ GtkWidget* protoLayout = gtk_fixed_new();
+ gtk_container_add(GTK_CONTAINER(gProtoWindow), protoLayout);
+
+ gStockImageWidget = gtk_image_new();
+ gtk_container_add(GTK_CONTAINER(protoLayout), gStockImageWidget);
+
+ gtk_widget_ensure_style(gStockImageWidget);
+ }
+}
+
+static GtkIconSize
+moz_gtk_icon_size(const char* name)
+{
+ if (strcmp(name, "button") == 0) {
+ return GTK_ICON_SIZE_BUTTON;
+ }
+
+ if (strcmp(name, "menu") == 0) {
+ return GTK_ICON_SIZE_MENU;
+ }
+
+ if (strcmp(name, "toolbar") == 0) {
+ return GTK_ICON_SIZE_LARGE_TOOLBAR;
+ }
+
+ if (strcmp(name, "toolbarsmall") == 0) {
+ return GTK_ICON_SIZE_SMALL_TOOLBAR;
+ }
+
+ if (strcmp(name, "dnd") == 0) {
+ return GTK_ICON_SIZE_DND;
+ }
+
+ if (strcmp(name, "dialog") == 0) {
+ return GTK_ICON_SIZE_DIALOG;
+ }
+
+ return GTK_ICON_SIZE_MENU;
+}
+
+#ifdef MOZ_ENABLE_GIO
+static int32_t
+GetIconSize(nsIMozIconURI* aIconURI)
+{
+ nsAutoCString iconSizeString;
+
+ aIconURI->GetIconSize(iconSizeString);
+ if (iconSizeString.IsEmpty()) {
+ uint32_t size;
+ mozilla::DebugOnly<nsresult> rv = aIconURI->GetImageSize(&size);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "GetImageSize failed");
+ return size;
+ } else {
+ int size;
+
+ GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
+ gtk_icon_size_lookup(icon_size, &size, nullptr);
+ return size;
+ }
+}
+
+/* Scale icon buffer to preferred size */
+static nsresult
+ScaleIconBuf(GdkPixbuf** aBuf, int32_t iconSize)
+{
+ // Scale buffer only if width or height differ from preferred size
+ if (gdk_pixbuf_get_width(*aBuf) != iconSize &&
+ gdk_pixbuf_get_height(*aBuf) != iconSize) {
+ GdkPixbuf* scaled = gdk_pixbuf_scale_simple(*aBuf, iconSize, iconSize,
+ GDK_INTERP_BILINEAR);
+ // replace original buffer by scaled
+ g_object_unref(*aBuf);
+ *aBuf = scaled;
+ if (!scaled) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsIconChannel::InitWithGIO(nsIMozIconURI* aIconURI)
+{
+ GIcon *icon = nullptr;
+ nsCOMPtr<nsIURL> fileURI;
+
+ // Read icon content
+ aIconURI->GetIconURL(getter_AddRefs(fileURI));
+
+ // Get icon for file specified by URI
+ if (fileURI) {
+ bool isFile;
+ nsAutoCString spec;
+ fileURI->GetAsciiSpec(spec);
+ if (NS_SUCCEEDED(fileURI->SchemeIs("file", &isFile)) && isFile) {
+ GFile* file = g_file_new_for_uri(spec.get());
+ GFileInfo* fileInfo = g_file_query_info(file,
+ G_FILE_ATTRIBUTE_STANDARD_ICON,
+ G_FILE_QUERY_INFO_NONE,
+ nullptr, nullptr);
+ g_object_unref(file);
+ if (fileInfo) {
+ // icon from g_content_type_get_icon doesn't need unref
+ icon = g_file_info_get_icon(fileInfo);
+ if (icon) {
+ g_object_ref(icon);
+ }
+ g_object_unref(fileInfo);
+ }
+ }
+ }
+
+ // Try to get icon by using MIME type
+ if (!icon) {
+ nsAutoCString type;
+ aIconURI->GetContentType(type);
+ // Try to get MIME type from file extension by using nsIMIMEService
+ if (type.IsEmpty()) {
+ nsCOMPtr<nsIMIMEService> ms(do_GetService("@mozilla.org/mime;1"));
+ if (ms) {
+ nsAutoCString fileExt;
+ aIconURI->GetFileExtension(fileExt);
+ ms->GetTypeFromExtension(fileExt, type);
+ }
+ }
+ char* ctype = nullptr; // character representation of content type
+ if (!type.IsEmpty()) {
+ ctype = g_content_type_from_mime_type(type.get());
+ }
+ if (ctype) {
+ icon = g_content_type_get_icon(ctype);
+ g_free(ctype);
+ }
+ }
+
+ // Get default icon theme
+ GtkIconTheme* iconTheme = gtk_icon_theme_get_default();
+ GtkIconInfo* iconInfo = nullptr;
+ // Get icon size
+ int32_t iconSize = GetIconSize(aIconURI);
+
+ if (icon) {
+ // Use icon and theme to get GtkIconInfo
+ iconInfo = gtk_icon_theme_lookup_by_gicon(iconTheme,
+ icon, iconSize,
+ (GtkIconLookupFlags)0);
+ g_object_unref(icon);
+ }
+
+ if (!iconInfo) {
+ // Mozilla's mimetype lookup failed. Try the "unknown" icon.
+ iconInfo = gtk_icon_theme_lookup_icon(iconTheme,
+ "unknown", iconSize,
+ (GtkIconLookupFlags)0);
+ if (!iconInfo) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+
+ // Create a GdkPixbuf buffer containing icon and scale it
+ GdkPixbuf* buf = gtk_icon_info_load_icon(iconInfo, nullptr);
+ gtk_icon_info_free(iconInfo);
+ if (!buf) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = ScaleIconBuf(&buf, iconSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = moz_gdk_pixbuf_to_channel(buf, aIconURI,
+ getter_AddRefs(mRealChannel));
+ g_object_unref(buf);
+ return rv;
+}
+#endif // MOZ_ENABLE_GIO
+
+nsresult
+nsIconChannel::Init(nsIURI* aURI)
+{
+ nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
+ NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
+
+ nsAutoCString stockIcon;
+ iconURI->GetStockIcon(stockIcon);
+ if (stockIcon.IsEmpty()) {
+#ifdef MOZ_ENABLE_GIO
+ return InitWithGIO(iconURI);
+#else
+ return NS_ERROR_NOT_AVAILABLE;
+#endif
+ }
+
+ // Search for stockIcon
+ nsAutoCString iconSizeString;
+ iconURI->GetIconSize(iconSizeString);
+
+ nsAutoCString iconStateString;
+ iconURI->GetIconState(iconStateString);
+
+ GtkIconSize icon_size = moz_gtk_icon_size(iconSizeString.get());
+ GtkStateType state = iconStateString.EqualsLiteral("disabled") ?
+ GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
+
+ // First lookup the icon by stock id and text direction.
+ GtkTextDirection direction = GTK_TEXT_DIR_NONE;
+ if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-ltr"))) {
+ direction = GTK_TEXT_DIR_LTR;
+ } else if (StringEndsWith(stockIcon, NS_LITERAL_CSTRING("-rtl"))) {
+ direction = GTK_TEXT_DIR_RTL;
+ }
+
+ bool forceDirection = direction != GTK_TEXT_DIR_NONE;
+ nsAutoCString stockID;
+ bool useIconName = false;
+ if (!forceDirection) {
+ direction = gtk_widget_get_default_direction();
+ stockID = stockIcon;
+ } else {
+ // GTK versions < 2.22 use icon names from concatenating stock id with
+ // -(rtl|ltr), which is how the moz-icon stock name is interpreted here.
+ stockID = Substring(stockIcon, 0, stockIcon.Length() - 4);
+ // However, if we lookup bidi icons by the stock name, then GTK versions
+ // >= 2.22 will use a bidi lookup convention that most icon themes do not
+ // yet follow. Therefore, we first check to see if the theme supports the
+ // old icon name as this will have bidi support (if found).
+ GtkIconTheme* icon_theme = gtk_icon_theme_get_default();
+ // Micking what gtk_icon_set_render_icon does with sizes, though it's not
+ // critical as icons will be scaled to suit size. It just means we follow
+ // the same pathes and so share caches.
+ gint width, height;
+ if (gtk_icon_size_lookup(icon_size, &width, &height)) {
+ gint size = std::min(width, height);
+ // We use gtk_icon_theme_lookup_icon() without
+ // GTK_ICON_LOOKUP_USE_BUILTIN instead of gtk_icon_theme_has_icon() so
+ // we don't pick up fallback icons added by distributions for backward
+ // compatibility.
+ GtkIconInfo* icon =
+ gtk_icon_theme_lookup_icon(icon_theme, stockIcon.get(),
+ size, (GtkIconLookupFlags)0);
+ if (icon) {
+ useIconName = true;
+ gtk_icon_info_free(icon);
+ }
+ }
+ }
+
+ ensure_stock_image_widget();
+ GtkStyle* style = gtk_widget_get_style(gStockImageWidget);
+ GtkIconSet* icon_set = nullptr;
+ if (!useIconName) {
+ icon_set = gtk_style_lookup_icon_set(style, stockID.get());
+ }
+
+ if (!icon_set) {
+ // Either we have choosen icon-name lookup for a bidi icon, or stockIcon is
+ // not a stock id so we assume it is an icon name.
+ useIconName = true;
+ // Creating a GtkIconSet is a convenient way to allow the style to
+ // render the icon, possibly with variations suitable for insensitive
+ // states.
+ icon_set = gtk_icon_set_new();
+ GtkIconSource* icon_source = gtk_icon_source_new();
+
+ gtk_icon_source_set_icon_name(icon_source, stockIcon.get());
+ gtk_icon_set_add_source(icon_set, icon_source);
+ gtk_icon_source_free(icon_source);
+ }
+
+ GdkPixbuf* icon =
+ gtk_icon_set_render_icon(icon_set, style, direction, state,
+ icon_size, gStockImageWidget, nullptr);
+ if (useIconName) {
+ gtk_icon_set_unref(icon_set);
+ }
+
+ // According to documentation, gtk_icon_set_render_icon() never returns
+ // nullptr, but it does return nullptr when we have the problem reported
+ // here: https://bugzilla.gnome.org/show_bug.cgi?id=629878#c13
+ if (!icon) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv = moz_gdk_pixbuf_to_channel(icon, iconURI,
+ getter_AddRefs(mRealChannel));
+
+ g_object_unref(icon);
+
+ return rv;
+}
+
+void
+nsIconChannel::Shutdown() {
+ if (gProtoWindow) {
+ gtk_widget_destroy(gProtoWindow);
+ gProtoWindow = nullptr;
+ gStockImageWidget = nullptr;
+ }
+}
diff --git a/image/decoders/icon/gtk/nsIconChannel.h b/image/decoders/icon/gtk/nsIconChannel.h
new file mode 100644
index 000000000..5d59272d5
--- /dev/null
+++ b/image/decoders/icon/gtk/nsIconChannel.h
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_decoders_icon_gtk_nsIconChannel_h
+#define mozilla_image_decoders_icon_gtk_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsIChannel.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+#include "nsIIconURI.h"
+#include "nsCOMPtr.h"
+
+/// This class is the gnome implementation of nsIconChannel. It basically asks
+/// gtk/gnome for an icon, saves it as a tmp icon, and creates a new channel for
+/// that file to which all calls will be proxied.
+class nsIconChannel final : public nsIChannel
+{
+ public:
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_NSIREQUEST(mRealChannel->)
+ NS_FORWARD_NSICHANNEL(mRealChannel->)
+
+ nsIconChannel() { }
+
+ static void Shutdown();
+
+ /// Called by nsIconProtocolHandler after it creates this channel.
+ /// Must be called before calling any other function on this object.
+ /// If this method fails, no other function must be called on this object.
+ nsresult Init(nsIURI* aURI);
+ private:
+ ~nsIconChannel() { }
+ /// The channel to the temp icon file (e.g. to /tmp/2qy9wjqw.html).
+ /// Will always be non-null after a successful Init.
+ nsCOMPtr<nsIChannel> mRealChannel;
+
+ nsresult InitWithGIO(nsIMozIconURI* aIconURI);
+};
+
+#endif // mozilla_image_decoders_icon_gtk_nsIconChannel_h
diff --git a/image/decoders/icon/mac/moz.build b/image/decoders/icon/mac/moz.build
new file mode 100644
index 000000000..158c326ea
--- /dev/null
+++ b/image/decoders/icon/mac/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsIconChannelCocoa.mm',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/image/decoders/icon/mac/nsIconChannel.h b/image/decoders/icon/mac/nsIconChannel.h
new file mode 100644
index 000000000..9fef17119
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannel.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_encoders_icon_mac_nsIconChannel_h
+#define mozilla_image_encoders_icon_mac_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "nsXPIDLString.h"
+#include "nsIChannel.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIInputStreamPump.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+
+class nsIFile;
+
+class nsIconChannel final : public nsIChannel, public nsIStreamListener
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIREQUEST
+ NS_DECL_NSICHANNEL
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsIconChannel();
+
+ nsresult Init(nsIURI* uri);
+
+protected:
+ virtual ~nsIconChannel();
+
+ nsCOMPtr<nsIURI> mUrl;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+ nsCOMPtr<nsISupports> mOwner;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+
+ nsCOMPtr<nsIInputStreamPump> mPump;
+ nsCOMPtr<nsIStreamListener> mListener;
+
+ nsresult MakeInputStream(nsIInputStream** _retval, bool nonBlocking);
+
+ nsresult ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsACString& aContentType,
+ nsACString& aFileExtension);
+};
+
+#endif // mozilla_image_encoders_icon_mac_nsIconChannel_h
diff --git a/image/decoders/icon/mac/nsIconChannelCocoa.mm b/image/decoders/icon/mac/nsIconChannelCocoa.mm
new file mode 100644
index 000000000..9c2686cdd
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsContentUtils.h"
+#include "nsIconChannel.h"
+#include "mozilla/EndianUtils.h"
+#include "nsIIconURI.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsXPIDLString.h"
+#include "nsMimeTypes.h"
+#include "nsMemory.h"
+#include "nsIStringStream.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsIPipe.h"
+#include "nsIOutputStream.h"
+#include "nsIMIMEService.h"
+#include "nsCExternalHandlerService.h"
+#include "nsILocalFileMac.h"
+#include "nsIFileURL.h"
+#include "nsTArray.h"
+#include "nsObjCExceptions.h"
+#include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
+
+#include <Cocoa/Cocoa.h>
+
+// nsIconChannel methods
+nsIconChannel::nsIconChannel()
+{
+}
+
+nsIconChannel::~nsIconChannel()
+{
+ if (mLoadInfo) {
+ NS_ReleaseOnMainThread(mLoadInfo.forget());
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsIconChannel,
+ nsIChannel,
+ nsIRequest,
+ nsIRequestObserver,
+ nsIStreamListener)
+
+nsresult
+nsIconChannel::Init(nsIURI* uri)
+{
+ NS_ASSERTION(uri, "no uri");
+ mUrl = uri;
+ mOriginalURI = uri;
+ nsresult rv;
+ mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRequest methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetName(nsACString& result)
+{
+ return mUrl->GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::IsPending(bool* result)
+{
+ return mPump->IsPending(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetStatus(nsresult* status)
+{
+ return mPump->GetStatus(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Cancel(nsresult status)
+{
+ return mPump->Cancel(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Suspend(void)
+{
+ return mPump->Suspend();
+}
+
+NS_IMETHODIMP
+nsIconChannel::Resume(void)
+{
+ return mPump->Resume();
+}
+
+// nsIRequestObserver methods
+NS_IMETHODIMP
+nsIconChannel::OnStartRequest(nsIRequest* aRequest,
+ nsISupports* aContext)
+{
+ if (mListener) {
+ return mListener->OnStartRequest(this, aContext);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::OnStopRequest(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsresult aStatus)
+{
+ if (mListener) {
+ mListener->OnStopRequest(this, aContext, aStatus);
+ mListener = nullptr;
+ }
+
+ // Remove from load group
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+ }
+
+ return NS_OK;
+}
+
+// nsIStreamListener methods
+NS_IMETHODIMP
+nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream* aStream,
+ uint64_t aOffset,
+ uint32_t aCount)
+{
+ if (mListener) {
+ return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIChannel methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetOriginalURI(nsIURI** aURI)
+{
+ *aURI = mOriginalURI;
+ NS_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOriginalURI(nsIURI* aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ mOriginalURI = aURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetURI(nsIURI** aURI)
+{
+ *aURI = mUrl;
+ NS_IF_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open(nsIInputStream** _retval)
+{
+ return MakeInputStream(_retval, false);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open2(nsIInputStream** aStream)
+{
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return Open(aStream);
+}
+
+nsresult
+nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsACString& aContentType,
+ nsACString& aFileExtension)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ iconURI->GetImageSize(aDesiredImageSize);
+ iconURI->GetContentType(aContentType);
+ iconURI->GetFileExtension(aFileExtension);
+
+ nsCOMPtr<nsIURL> url;
+ rv = iconURI->GetIconURL(getter_AddRefs(url));
+ if (NS_FAILED(rv) || !url) return NS_OK;
+
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
+ if (NS_FAILED(rv) || !fileURL) return NS_OK;
+
+ nsCOMPtr<nsIFile> file;
+ rv = fileURL->GetFile(getter_AddRefs(file));
+ if (NS_FAILED(rv) || !file) return NS_OK;
+
+ nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(file, &rv));
+ if (NS_FAILED(rv) || !localFileMac) return NS_OK;
+
+ *aLocalFile = file;
+ NS_IF_ADDREF(*aLocalFile);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
+ nsISupports* ctxt)
+{
+ MOZ_ASSERT(!mLoadInfo ||
+ mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone() ||
+ (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
+ nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
+ "security flags in loadInfo but asyncOpen2() not called");
+
+ nsCOMPtr<nsIInputStream> inStream;
+ nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Init our stream pump
+ rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mPump->AsyncRead(this, ctxt);
+ if (NS_SUCCEEDED(rv)) {
+ // Store our real listener
+ mListener = aListener;
+ // Add ourself to the load group, if available
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return AsyncOpen(listener, nullptr);
+}
+
+nsresult
+nsIconChannel::MakeInputStream(nsIInputStream** _retval,
+ bool aNonBlocking)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsXPIDLCString contentType;
+ nsAutoCString fileExt;
+ nsCOMPtr<nsIFile> fileloc; // file we want an icon for
+ uint32_t desiredImageSize;
+ nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(fileloc),
+ &desiredImageSize, contentType, fileExt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool fileExists = false;
+ if (fileloc) {
+ // ensure that we DO NOT resolve aliases, very important for file views
+ fileloc->SetFollowLinks(false);
+ fileloc->Exists(&fileExists);
+ }
+
+ NSImage* iconImage = nil;
+
+ // first try to get the icon from the file if it exists
+ if (fileExists) {
+ nsCOMPtr<nsILocalFileMac> localFileMac(do_QueryInterface(fileloc, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CFURLRef macURL;
+ if (NS_SUCCEEDED(localFileMac->GetCFURL(&macURL))) {
+ iconImage = [[NSWorkspace sharedWorkspace]
+ iconForFile:[(NSURL*)macURL path]];
+ ::CFRelease(macURL);
+ }
+ }
+
+ // if we don't have an icon yet try to get one by extension
+ if (!iconImage && !fileExt.IsEmpty()) {
+ NSString* fileExtension = [NSString stringWithUTF8String:fileExt.get()];
+ iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:fileExtension];
+ }
+
+ // If we still don't have an icon, get the generic document icon.
+ if (!iconImage) {
+ iconImage = [[NSWorkspace sharedWorkspace]
+ iconForFileType:NSFileTypeUnknown];
+ }
+
+ if (!iconImage) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // we have an icon now, size it
+ NSRect desiredSizeRect = NSMakeRect(0, 0, desiredImageSize, desiredImageSize);
+ [iconImage setSize:desiredSizeRect.size];
+
+ [iconImage lockFocus];
+ NSBitmapImageRep* bitmapRep = [[[NSBitmapImageRep alloc]
+ initWithFocusedViewRect:desiredSizeRect]
+ autorelease];
+ [iconImage unlockFocus];
+
+ // we expect the following things to be true about our bitmapRep
+ NS_ENSURE_TRUE(![bitmapRep isPlanar] &&
+ // Not necessarily: on a HiDPI-capable system, we'll get
+ // a 2x bitmap
+ // (unsigned int)[bitmapRep bytesPerPlane] ==
+ // desiredImageSize * desiredImageSize * 4 &&
+ [bitmapRep bitsPerPixel] == 32 &&
+ [bitmapRep samplesPerPixel] == 4 &&
+ [bitmapRep hasAlpha] == YES,
+ NS_ERROR_UNEXPECTED);
+
+ // check what size we actually got, and ensure it isn't too big to return
+ uint32_t actualImageSize = [bitmapRep bytesPerRow] / 4;
+ NS_ENSURE_TRUE(actualImageSize < 256, NS_ERROR_UNEXPECTED);
+
+ // now we can validate the amount of data
+ NS_ENSURE_TRUE((unsigned int)[bitmapRep bytesPerPlane] ==
+ actualImageSize * actualImageSize * 4,
+ NS_ERROR_UNEXPECTED);
+
+ // rgba, pre-multiplied data
+ uint8_t* bitmapRepData = (uint8_t*)[bitmapRep bitmapData];
+
+ // create our buffer
+ int32_t bufferCapacity = 2 + [bitmapRep bytesPerPlane];
+ AutoTArray<uint8_t, 3 + 16 * 16 * 5> iconBuffer; // initial size is for
+ // 16x16
+ iconBuffer.SetLength(bufferCapacity);
+
+ uint8_t* iconBufferPtr = iconBuffer.Elements();
+
+ // write header data into buffer
+ *iconBufferPtr++ = actualImageSize;
+ *iconBufferPtr++ = actualImageSize;
+
+ uint32_t dataCount = [bitmapRep bytesPerPlane];
+ uint32_t index = 0;
+ while (index < dataCount) {
+ // get data from the bitmap
+ uint8_t r = bitmapRepData[index++];
+ uint8_t g = bitmapRepData[index++];
+ uint8_t b = bitmapRepData[index++];
+ uint8_t a = bitmapRepData[index++];
+
+ // write data out to our buffer
+ // non-cairo uses native image format, but the A channel is ignored.
+ // cairo uses ARGB (highest to lowest bits)
+#if MOZ_LITTLE_ENDIAN
+ *iconBufferPtr++ = b;
+ *iconBufferPtr++ = g;
+ *iconBufferPtr++ = r;
+ *iconBufferPtr++ = a;
+#else
+ *iconBufferPtr++ = a;
+ *iconBufferPtr++ = r;
+ *iconBufferPtr++ = g;
+ *iconBufferPtr++ = b;
+#endif
+ }
+
+ NS_ASSERTION(iconBufferPtr == iconBuffer.Elements() + bufferCapacity,
+ "buffer size miscalculation");
+
+ // Now, create a pipe and stuff our data into it
+ nsCOMPtr<nsIInputStream> inStream;
+ nsCOMPtr<nsIOutputStream> outStream;
+ rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
+ bufferCapacity, bufferCapacity, aNonBlocking);
+
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t written;
+ rv = outStream->Write((char*)iconBuffer.Elements(), bufferCapacity,
+ &written);
+ if (NS_SUCCEEDED(rv)) {
+ NS_IF_ADDREF(*_retval = inStream);
+ }
+ }
+
+ // Drop notification callbacks to prevent cycles.
+ mCallbacks = nullptr;
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadFlags(uint32_t* aLoadAttributes)
+{
+ return mPump->GetLoadFlags(aLoadAttributes);
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
+{
+ return mPump->SetLoadFlags(aLoadAttributes);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentType(nsACString& aContentType)
+{
+ aContentType.AssignLiteral(IMAGE_ICON_MS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentType(const nsACString& aContentType)
+{
+ //It doesn't make sense to set the content-type on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentCharset(nsACString& aContentCharset)
+{
+ aContentCharset.AssignLiteral(IMAGE_ICON_MS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentCharset(const nsACString& aContentCharset)
+{
+ //It doesn't make sense to set the content-type on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentDisposition(uint32_t* aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionFilename(nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionHeader(nsACString& aContentDispositionHeader)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentLength(int64_t* aContentLength)
+{
+ *aContentLength = 0;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentLength(int64_t aContentLength)
+{
+ NS_NOTREACHED("nsIconChannel::SetContentLength");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+ *aLoadGroup = mLoadGroup;
+ NS_IF_ADDREF(*aLoadGroup);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+ mLoadGroup = aLoadGroup;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetOwner(nsISupports** aOwner)
+{
+ *aOwner = mOwner.get();
+ NS_IF_ADDREF(*aOwner);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOwner(nsISupports* aOwner)
+{
+ mOwner = aOwner;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
+{
+ NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+ mLoadInfo = aLoadInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetNotificationCallbacks(nsIInterfaceRequestor** aNotificationCallbacks)
+{
+ *aNotificationCallbacks = mCallbacks.get();
+ NS_IF_ADDREF(*aNotificationCallbacks);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
+{
+ mCallbacks = aNotificationCallbacks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetSecurityInfo(nsISupports** aSecurityInfo)
+{
+ *aSecurityInfo = nullptr;
+ return NS_OK;
+}
+
diff --git a/image/decoders/icon/moz.build b/image/decoders/icon/moz.build
new file mode 100644
index 000000000..9c6106fa7
--- /dev/null
+++ b/image/decoders/icon/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'nsIconModule.cpp',
+ 'nsIconProtocolHandler.cpp',
+ 'nsIconURI.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+platform = None
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ platform = 'gtk'
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ platform = 'win'
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ platform = 'mac'
+
+if CONFIG['OS_TARGET'] == 'Android':
+ platform = 'android'
+
+if platform:
+ LOCAL_INCLUDES += [platform]
diff --git a/image/decoders/icon/nsIconModule.cpp b/image/decoders/icon/nsIconModule.cpp
new file mode 100644
index 000000000..36225374d
--- /dev/null
+++ b/image/decoders/icon/nsIconModule.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIconProtocolHandler.h"
+#include "nsIconURI.h"
+#include "nsIconChannel.h"
+
+// objects that just require generic constructors
+//*****************************************************************************
+// Protocol CIDs
+
+#define NS_ICONPROTOCOL_CID { 0xd0f9db12, 0x249c, 0x11d5, \
+ { 0x99, 0x5, 0x0, 0x10, 0x83, 0x1, 0xe, 0x9b } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsIconProtocolHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMozIconURI)
+
+NS_DEFINE_NAMED_CID(NS_ICONPROTOCOL_CID);
+NS_DEFINE_NAMED_CID(NS_MOZICONURI_CID);
+
+static const mozilla::Module::CIDEntry kIconCIDs[] = {
+ { &kNS_ICONPROTOCOL_CID, false, nullptr, nsIconProtocolHandlerConstructor },
+ { &kNS_MOZICONURI_CID, false, nullptr, nsMozIconURIConstructor },
+ { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kIconContracts[] = {
+ { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-icon", &kNS_ICONPROTOCOL_CID },
+ { nullptr }
+};
+
+static const mozilla::Module::CategoryEntry kIconCategories[] = {
+ { nullptr }
+};
+
+static void
+IconDecoderModuleDtor()
+{
+#if (MOZ_WIDGET_GTK == 2)
+ nsIconChannel::Shutdown();
+#endif
+}
+
+static const mozilla::Module kIconModule = {
+ mozilla::Module::kVersion,
+ kIconCIDs,
+ kIconContracts,
+ kIconCategories,
+ nullptr,
+ nullptr,
+ IconDecoderModuleDtor
+};
+
+NSMODULE_DEFN(nsIconDecoderModule) = &kIconModule;
diff --git a/image/decoders/icon/nsIconProtocolHandler.cpp b/image/decoders/icon/nsIconProtocolHandler.cpp
new file mode 100644
index 000000000..03aaa2a14
--- /dev/null
+++ b/image/decoders/icon/nsIconProtocolHandler.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIconProtocolHandler.h"
+
+#include "nsIconChannel.h"
+#include "nsIconURI.h"
+#include "nsIURL.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsNetCID.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+nsIconProtocolHandler::nsIconProtocolHandler()
+{ }
+
+nsIconProtocolHandler::~nsIconProtocolHandler()
+{ }
+
+NS_IMPL_ISUPPORTS(nsIconProtocolHandler, nsIProtocolHandler,
+ nsISupportsWeakReference)
+
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIProtocolHandler methods:
+
+NS_IMETHODIMP
+nsIconProtocolHandler::GetScheme(nsACString& result)
+{
+ result = "moz-icon";
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::GetDefaultPort(int32_t* result)
+{
+ *result = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::AllowPort(int32_t port,
+ const char* scheme,
+ bool* _retval)
+{
+ // don't override anything.
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::GetProtocolFlags(uint32_t* result)
+{
+ *result = URI_NORELATIVE | URI_NOAUTH | URI_IS_UI_RESOURCE |
+ URI_IS_LOCAL_RESOURCE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::NewURI(const nsACString& aSpec,
+ const char* aOriginCharset, // ignored
+ nsIURI* aBaseURI,
+ nsIURI** result)
+{
+ nsCOMPtr<nsIMozIconURI> uri = new nsMozIconURI();
+ if (!uri) return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = uri->SetSpec(aSpec);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIURL> iconURL;
+ uri->GetIconURL(getter_AddRefs(iconURL));
+ if (iconURL) {
+ uri = new nsNestedMozIconURI();
+ rv = uri->SetSpec(aSpec);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ NS_ADDREF(*result = uri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::NewChannel2(nsIURI* url,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** result)
+{
+ NS_ENSURE_ARG_POINTER(url);
+ nsIconChannel* channel = new nsIconChannel;
+ if (!channel) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ NS_ADDREF(channel);
+
+ nsresult rv = channel->Init(url);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(channel);
+ return rv;
+ }
+
+ // set the loadInfo on the new channel
+ rv = channel->SetLoadInfo(aLoadInfo);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(channel);
+ return rv;
+ }
+
+ *result = channel;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconProtocolHandler::NewChannel(nsIURI* url, nsIChannel** result)
+{
+ return NewChannel2(url, nullptr, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/image/decoders/icon/nsIconProtocolHandler.h b/image/decoders/icon/nsIconProtocolHandler.h
new file mode 100644
index 000000000..9653d36f6
--- /dev/null
+++ b/image/decoders/icon/nsIconProtocolHandler.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_decoders_icon_nsIconProtocolHandler_h
+#define mozilla_image_decoders_icon_nsIconProtocolHandler_h
+
+#include "nsWeakReference.h"
+#include "nsIProtocolHandler.h"
+
+class nsIconProtocolHandler : public nsIProtocolHandler,
+ public nsSupportsWeakReference
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROTOCOLHANDLER
+
+ // nsIconProtocolHandler methods:
+ nsIconProtocolHandler();
+
+protected:
+ virtual ~nsIconProtocolHandler();
+};
+
+#endif // mozilla_image_decoders_icon_nsIconProtocolHandler_h
diff --git a/image/decoders/icon/nsIconURI.cpp b/image/decoders/icon/nsIconURI.cpp
new file mode 100644
index 000000000..2c2788c8f
--- /dev/null
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -0,0 +1,699 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set sw=2 sts=2 ts=2 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIconURI.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Sprintf.h"
+
+#include "nsIIOService.h"
+#include "nsIURL.h"
+#include "nsNetUtil.h"
+#include "plstr.h"
+#include <stdlib.h>
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+
+#define DEFAULT_IMAGE_SIZE 16
+
+#if defined(MAX_PATH)
+#define SANE_FILE_NAME_LEN MAX_PATH
+#elif defined(PATH_MAX)
+#define SANE_FILE_NAME_LEN PATH_MAX
+#else
+#define SANE_FILE_NAME_LEN 1024
+#endif
+
+// helper function for parsing out attributes like size, and contentType
+// from the icon url.
+static void extractAttributeValue(const char* aSearchString,
+ const char* aAttributeName,
+ nsCString& aResult);
+
+static const char* kSizeStrings[] =
+{
+ "button",
+ "toolbar",
+ "toolbarsmall",
+ "menu",
+ "dnd",
+ "dialog"
+};
+
+static const char* kStateStrings[] =
+{
+ "normal",
+ "disabled"
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsMozIconURI::nsMozIconURI()
+ : mSize(DEFAULT_IMAGE_SIZE),
+ mIconSize(-1),
+ mIconState(-1)
+{ }
+
+nsMozIconURI::~nsMozIconURI()
+{ }
+
+NS_IMPL_ISUPPORTS(nsMozIconURI, nsIMozIconURI, nsIURI, nsIIPCSerializableURI)
+
+#define MOZICON_SCHEME "moz-icon:"
+#define MOZICON_SCHEME_LEN (sizeof(MOZICON_SCHEME) - 1)
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIURI methods:
+
+NS_IMETHODIMP
+nsMozIconURI::GetSpec(nsACString& aSpec)
+{
+ aSpec = MOZICON_SCHEME;
+
+ if (mIconURL) {
+ nsAutoCString fileIconSpec;
+ nsresult rv = mIconURL->GetSpec(fileIconSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aSpec += fileIconSpec;
+ } else if (!mStockIcon.IsEmpty()) {
+ aSpec += "//stock/";
+ aSpec += mStockIcon;
+ } else {
+ aSpec += "//";
+ aSpec += mFileName;
+ }
+
+ aSpec += "?size=";
+ if (mIconSize >= 0) {
+ aSpec += kSizeStrings[mIconSize];
+ } else {
+ char buf[20];
+ SprintfLiteral(buf, "%d", mSize);
+ aSpec.Append(buf);
+ }
+
+ if (mIconState >= 0) {
+ aSpec += "&state=";
+ aSpec += kStateStrings[mIconState];
+ }
+
+ if (!mContentType.IsEmpty()) {
+ aSpec += "&contentType=";
+ aSpec += mContentType.get();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetSpecIgnoringRef(nsACString& result)
+{
+ return GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHasRef(bool* result)
+{
+ *result = false;
+ return NS_OK;
+}
+
+// takes a string like ?size=32&contentType=text/html and returns a new string
+// containing just the attribute value. i.e you could pass in this string with
+// an attribute name of 'size=', this will return 32
+// Assumption: attribute pairs in the string are separated by '&'.
+void
+extractAttributeValue(const char* aSearchString,
+ const char* aAttributeName,
+ nsCString& aResult)
+{
+ //NS_ENSURE_ARG_POINTER(extractAttributeValue);
+
+ aResult.Truncate();
+
+ if (aSearchString && aAttributeName) {
+ // search the string for attributeName
+ uint32_t attributeNameSize = strlen(aAttributeName);
+ const char* startOfAttribute = PL_strcasestr(aSearchString, aAttributeName);
+ if (startOfAttribute &&
+ ( *(startOfAttribute-1) == '?' || *(startOfAttribute-1) == '&') ) {
+ startOfAttribute += attributeNameSize; // skip over the attributeName
+ // is there something after the attribute name
+ if (*startOfAttribute) {
+ const char* endofAttribute = strchr(startOfAttribute, '&');
+ if (endofAttribute) {
+ aResult.Assign(Substring(startOfAttribute, endofAttribute));
+ } else {
+ aResult.Assign(startOfAttribute);
+ }
+ } // if we have a attribute value
+ } // if we have a attribute name
+ } // if we got non-null search string and attribute name values
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetSpec(const nsACString& aSpec)
+{
+ // Reset everything to default values.
+ mIconURL = nullptr;
+ mSize = DEFAULT_IMAGE_SIZE;
+ mContentType.Truncate();
+ mFileName.Truncate();
+ mStockIcon.Truncate();
+ mIconSize = -1;
+ mIconState = -1;
+
+ nsAutoCString iconSpec(aSpec);
+ if (!Substring(iconSpec, 0,
+ MOZICON_SCHEME_LEN).EqualsLiteral(MOZICON_SCHEME)) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ int32_t questionMarkPos = iconSpec.Find("?");
+ if (questionMarkPos != -1 &&
+ static_cast<int32_t>(iconSpec.Length()) > (questionMarkPos + 1)) {
+ extractAttributeValue(iconSpec.get(), "contentType=", mContentType);
+
+ nsAutoCString sizeString;
+ extractAttributeValue(iconSpec.get(), "size=", sizeString);
+ if (!sizeString.IsEmpty()) {
+ const char* sizeStr = sizeString.get();
+ for (uint32_t i = 0; i < ArrayLength(kSizeStrings); i++) {
+ if (PL_strcasecmp(sizeStr, kSizeStrings[i]) == 0) {
+ mIconSize = i;
+ break;
+ }
+ }
+
+ int32_t sizeValue = atoi(sizeString.get());
+ if (sizeValue > 0) {
+ mSize = sizeValue;
+ }
+ }
+
+ nsAutoCString stateString;
+ extractAttributeValue(iconSpec.get(), "state=", stateString);
+ if (!stateString.IsEmpty()) {
+ const char* stateStr = stateString.get();
+ for (uint32_t i = 0; i < ArrayLength(kStateStrings); i++) {
+ if (PL_strcasecmp(stateStr, kStateStrings[i]) == 0) {
+ mIconState = i;
+ break;
+ }
+ }
+ }
+ }
+
+ int32_t pathLength = iconSpec.Length() - MOZICON_SCHEME_LEN;
+ if (questionMarkPos != -1) {
+ pathLength = questionMarkPos - MOZICON_SCHEME_LEN;
+ }
+ if (pathLength < 3) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ nsAutoCString iconPath(Substring(iconSpec, MOZICON_SCHEME_LEN, pathLength));
+
+ // Icon URI path can have three forms:
+ // (1) //stock/<icon-identifier>
+ // (2) //<some dummy file with an extension>
+ // (3) a valid URL
+
+ if (!strncmp("//stock/", iconPath.get(), 8)) {
+ mStockIcon.Assign(Substring(iconPath, 8));
+ // An icon identifier must always be specified.
+ if (mStockIcon.IsEmpty()) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+ return NS_OK;
+ }
+
+ if (StringBeginsWith(iconPath, NS_LITERAL_CSTRING("//"))) {
+ // Sanity check this supposed dummy file name.
+ if (iconPath.Length() > SANE_FILE_NAME_LEN) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+ iconPath.Cut(0, 2);
+ mFileName.Assign(iconPath);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> uri;
+ ioService->NewURI(iconPath, nullptr, nullptr, getter_AddRefs(uri));
+ mIconURL = do_QueryInterface(uri);
+ if (mIconURL) {
+ mFileName.Truncate();
+ } else if (mFileName.IsEmpty()) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPrePath(nsACString& prePath)
+{
+ prePath = MOZICON_SCHEME;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetScheme(nsACString& aScheme)
+{
+ aScheme = "moz-icon";
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetScheme(const nsACString& aScheme)
+{
+ // doesn't make sense to set the scheme of a moz-icon URL
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetUsername(nsACString& aUsername)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetUsername(const nsACString& aUsername)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPassword(nsACString& aPassword)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetPassword(const nsACString& aPassword)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetUserPass(nsACString& aUserPass)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetUserPass(const nsACString& aUserPass)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHostPort(nsACString& aHostPort)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetHostPort(const nsACString& aHostPort)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetHostAndPort(const nsACString& aHostPort)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHost(nsACString& aHost)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetHost(const nsACString& aHost)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPort(int32_t* aPort)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetPort(int32_t aPort)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPath(nsACString& aPath)
+{
+ aPath.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetPath(const nsACString& aPath)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetRef(nsACString& aRef)
+{
+ aRef.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetRef(const nsACString& aRef)
+{
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::Equals(nsIURI* other, bool* result)
+{
+ *result = false;
+ NS_ENSURE_ARG_POINTER(other);
+ NS_PRECONDITION(result, "null pointer");
+
+ nsAutoCString spec1;
+ nsAutoCString spec2;
+
+ nsresult rv = GetSpec(spec1);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = other->GetSpec(spec2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!PL_strcasecmp(spec1.get(), spec2.get())) {
+ *result = true;
+ } else {
+ *result = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::EqualsExceptRef(nsIURI* other, bool* result)
+{
+ // GetRef/SetRef not supported by nsMozIconURI, so
+ // EqualsExceptRef() is the same as Equals().
+ return Equals(other, result);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SchemeIs(const char* aScheme, bool* aEquals)
+{
+ NS_ENSURE_ARG_POINTER(aEquals);
+ if (!aScheme) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ *aEquals = PL_strcasecmp("moz-icon", aScheme) ? false : true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::Clone(nsIURI** result)
+{
+ nsCOMPtr<nsIURL> newIconURL;
+ if (mIconURL) {
+ nsCOMPtr<nsIURI> newURI;
+ nsresult rv = mIconURL->Clone(getter_AddRefs(newURI));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ newIconURL = do_QueryInterface(newURI, &rv);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ nsMozIconURI* uri = new nsMozIconURI();
+ newIconURL.swap(uri->mIconURL);
+ uri->mSize = mSize;
+ uri->mContentType = mContentType;
+ uri->mFileName = mFileName;
+ uri->mStockIcon = mStockIcon;
+ uri->mIconSize = mIconSize;
+ uri->mIconState = mIconState;
+ NS_ADDREF(*result = uri);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::CloneIgnoringRef(nsIURI** result)
+{
+ // GetRef/SetRef not supported by nsMozIconURI, so
+ // CloneIgnoringRef() is the same as Clone().
+ return Clone(result);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::CloneWithNewRef(const nsACString& newRef, nsIURI** result)
+{
+ // GetRef/SetRef not supported by nsMozIconURI, so
+ // CloneWithNewRef() is the same as Clone().
+ return Clone(result);
+}
+
+
+NS_IMETHODIMP
+nsMozIconURI::Resolve(const nsACString& relativePath, nsACString& result)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetAsciiSpec(nsACString& aSpecA)
+{
+ return GetSpec(aSpecA);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetAsciiHostPort(nsACString& aHostPortA)
+{
+ return GetHostPort(aHostPortA);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetAsciiHost(nsACString& aHostA)
+{
+ return GetHost(aHostA);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetOriginCharset(nsACString& result)
+{
+ result.Truncate();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIIconUri methods:
+
+NS_IMETHODIMP
+nsMozIconURI::GetIconURL(nsIURL** aFileUrl)
+{
+ *aFileUrl = mIconURL;
+ NS_IF_ADDREF(*aFileUrl);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetIconURL(nsIURL* aFileUrl)
+{
+ // this isn't called anywhere, needs to go through SetSpec parsing
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetImageSize(uint32_t* aImageSize)
+ // measured by # of pixels in a row. defaults to 16.
+{
+ *aImageSize = mSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetImageSize(uint32_t aImageSize)
+ // measured by # of pixels in a row. defaults to 16.
+{
+ mSize = aImageSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetContentType(nsACString& aContentType)
+{
+ aContentType = mContentType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::SetContentType(const nsACString& aContentType)
+{
+ mContentType = aContentType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetFileExtension(nsACString& aFileExtension)
+{
+ // First, try to get the extension from mIconURL if we have one
+ if (mIconURL) {
+ nsAutoCString fileExt;
+ if (NS_SUCCEEDED(mIconURL->GetFileExtension(fileExt))) {
+ if (!fileExt.IsEmpty()) {
+ // unfortunately, this code doesn't give us the required '.' in
+ // front of the extension so we have to do it ourselves.
+ aFileExtension.Assign('.');
+ aFileExtension.Append(fileExt);
+ }
+ }
+ return NS_OK;
+ }
+
+ if (!mFileName.IsEmpty()) {
+ // truncate the extension out of the file path...
+ const char* chFileName = mFileName.get(); // get the underlying buffer
+ const char* fileExt = strrchr(chFileName, '.');
+ if (!fileExt) {
+ return NS_OK;
+ }
+ aFileExtension = fileExt;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetStockIcon(nsACString& aStockIcon)
+{
+ aStockIcon = mStockIcon;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetIconSize(nsACString& aSize)
+{
+ if (mIconSize >= 0) {
+ aSize = kSizeStrings[mIconSize];
+ } else {
+ aSize.Truncate();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetIconState(nsACString& aState)
+{
+ if (mIconState >= 0) {
+ aState = kStateStrings[mIconState];
+ } else {
+ aState.Truncate();
+ }
+ return NS_OK;
+}
+////////////////////////////////////////////////////////////////////////////////
+// nsIIPCSerializableURI methods:
+
+void
+nsMozIconURI::Serialize(URIParams& aParams)
+{
+ IconURIParams params;
+
+ if (mIconURL) {
+ URIParams iconURLParams;
+ SerializeURI(mIconURL, iconURLParams);
+ if (iconURLParams.type() == URIParams::T__None) {
+ // Serialization failed, bail.
+ return;
+ }
+
+ params.uri() = iconURLParams;
+ } else {
+ params.uri() = void_t();
+ }
+
+ params.size() = mSize;
+ params.fileName() = mFileName;
+ params.stockIcon() = mStockIcon;
+ params.iconSize() = mIconSize;
+ params.iconState() = mIconState;
+
+ aParams = params;
+}
+
+bool
+nsMozIconURI::Deserialize(const URIParams& aParams)
+{
+ if (aParams.type() != URIParams::TIconURIParams) {
+ MOZ_ASSERT_UNREACHABLE("Received unknown URI from other process!");
+ return false;
+ }
+
+ const IconURIParams& params = aParams.get_IconURIParams();
+ if (params.uri().type() != OptionalURIParams::Tvoid_t) {
+ nsCOMPtr<nsIURI> uri = DeserializeURI(params.uri().get_URIParams());
+ mIconURL = do_QueryInterface(uri);
+ if (!mIconURL) {
+ MOZ_ASSERT_UNREACHABLE("bad nsIURI passed");
+ return false;
+ }
+ }
+
+ mSize = params.size();
+ mContentType = params.contentType();
+ mFileName = params.fileName();
+ mStockIcon = params.stockIcon();
+ mIconSize = params.iconSize();
+ mIconState = params.iconState();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////
+// Nested version of nsIconURI
+
+nsNestedMozIconURI::nsNestedMozIconURI()
+{ }
+
+nsNestedMozIconURI::~nsNestedMozIconURI()
+{ }
+
+NS_IMPL_ISUPPORTS_INHERITED(nsNestedMozIconURI, nsMozIconURI, nsINestedURI)
+
+NS_IMETHODIMP
+nsNestedMozIconURI::GetInnerURI(nsIURI** aURI)
+{
+ nsCOMPtr<nsIURI> iconURL = do_QueryInterface(mIconURL);
+ if (iconURL) {
+ iconURL.forget(aURI);
+ } else {
+ *aURI = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNestedMozIconURI::GetInnermostURI(nsIURI** aURI)
+{
+ return NS_ImplGetInnermostURI(this, aURI);
+}
+
diff --git a/image/decoders/icon/nsIconURI.h b/image/decoders/icon/nsIconURI.h
new file mode 100644
index 000000000..1c0310bec
--- /dev/null
+++ b/image/decoders/icon/nsIconURI.h
@@ -0,0 +1,63 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_decoders_icon_nsIconURI_h
+#define mozilla_image_decoders_icon_nsIconURI_h
+
+#include "nsIIconURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIIPCSerializableURI.h"
+#include "nsINestedURI.h"
+
+class nsMozIconURI : public nsIMozIconURI
+ , public nsIIPCSerializableURI
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSIMOZICONURI
+ NS_DECL_NSIIPCSERIALIZABLEURI
+
+ // nsMozIconURI
+ nsMozIconURI();
+
+protected:
+ virtual ~nsMozIconURI();
+ nsCOMPtr<nsIURL> mIconURL; // a URL that we want the icon for
+ uint32_t mSize; // the # of pixels in a row that we want for this image.
+ // Typically 16, 32, 128, etc.
+ nsCString mContentType; // optional field explicitly specifying the content
+ // type
+ nsCString mFileName; // for if we don't have an actual file path, we're just
+ // given a filename with an extension
+ nsCString mStockIcon;
+ int32_t mIconSize; // -1 if not specified, otherwise index into
+ // kSizeStrings
+ int32_t mIconState; // -1 if not specified, otherwise index into
+ // kStateStrings
+};
+
+// For moz-icon URIs that point to an actual file on disk and are
+// therefore nested URIs
+class nsNestedMozIconURI final : public nsMozIconURI
+ , public nsINestedURI
+{
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_FORWARD_NSIURI(nsMozIconURI::)
+ NS_FORWARD_NSIMOZICONURI(nsMozIconURI::)
+ NS_FORWARD_NSIIPCSERIALIZABLEURI(nsMozIconURI::)
+
+ NS_DECL_NSINESTEDURI
+
+ nsNestedMozIconURI();
+
+protected:
+ virtual ~nsNestedMozIconURI();
+
+};
+
+#endif // mozilla_image_decoders_icon_nsIconURI_h
diff --git a/image/decoders/icon/win/moz.build b/image/decoders/icon/win/moz.build
new file mode 100644
index 000000000..6f763d953
--- /dev/null
+++ b/image/decoders/icon/win/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'nsIconChannel.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp
new file mode 100644
index 000000000..9ddcbbc48
--- /dev/null
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -0,0 +1,841 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsIconChannel.h"
+#include "nsIIconURI.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+#include "nsMimeTypes.h"
+#include "nsMemory.h"
+#include "nsIStringStream.h"
+#include "nsIURL.h"
+#include "nsIOutputStream.h"
+#include "nsIPipe.h"
+#include "nsNetCID.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIMIMEService.h"
+#include "nsCExternalHandlerService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
+#include "nsContentUtils.h"
+
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+
+// we need windows.h to read out registry information...
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <objbase.h>
+#include <wchar.h>
+
+using namespace mozilla;
+
+struct ICONFILEHEADER {
+ uint16_t ifhReserved;
+ uint16_t ifhType;
+ uint16_t ifhCount;
+};
+
+struct ICONENTRY {
+ int8_t ieWidth;
+ int8_t ieHeight;
+ uint8_t ieColors;
+ uint8_t ieReserved;
+ uint16_t iePlanes;
+ uint16_t ieBitCount;
+ uint32_t ieSizeImage;
+ uint32_t ieFileOffset;
+};
+
+// Match stock icons with names
+static SHSTOCKICONID
+GetStockIconIDForName(const nsACString& aStockName)
+{
+ return aStockName.EqualsLiteral("uac-shield") ? SIID_SHIELD :
+ SIID_INVALID;
+}
+
+// nsIconChannel methods
+nsIconChannel::nsIconChannel()
+{
+}
+
+nsIconChannel::~nsIconChannel()
+{
+ if (mLoadInfo) {
+ NS_ReleaseOnMainThread(mLoadInfo.forget());
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsIconChannel,
+ nsIChannel,
+ nsIRequest,
+ nsIRequestObserver,
+ nsIStreamListener)
+
+nsresult
+nsIconChannel::Init(nsIURI* uri)
+{
+ NS_ASSERTION(uri, "no uri");
+ mUrl = uri;
+ mOriginalURI = uri;
+ nsresult rv;
+ mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRequest methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetName(nsACString& result)
+{
+ return mUrl->GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::IsPending(bool* result)
+{
+ return mPump->IsPending(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetStatus(nsresult* status)
+{
+ return mPump->GetStatus(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Cancel(nsresult status)
+{
+ return mPump->Cancel(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Suspend(void)
+{
+ return mPump->Suspend();
+}
+
+NS_IMETHODIMP
+nsIconChannel::Resume(void)
+{
+ return mPump->Resume();
+}
+NS_IMETHODIMP
+nsIconChannel::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+ *aLoadGroup = mLoadGroup;
+ NS_IF_ADDREF(*aLoadGroup);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+ mLoadGroup = aLoadGroup;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadFlags(uint32_t* aLoadAttributes)
+{
+ return mPump->GetLoadFlags(aLoadAttributes);
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
+{
+ return mPump->SetLoadFlags(aLoadAttributes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIChannel methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetOriginalURI(nsIURI** aURI)
+{
+ *aURI = mOriginalURI;
+ NS_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOriginalURI(nsIURI* aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ mOriginalURI = aURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetURI(nsIURI** aURI)
+{
+ *aURI = mUrl;
+ NS_IF_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open(nsIInputStream** _retval)
+{
+ return MakeInputStream(_retval, false);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open2(nsIInputStream** aStream)
+{
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return Open(aStream);
+}
+
+nsresult
+nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize, nsCString& aContentType,
+ nsCString& aFileExtension)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ iconURI->GetImageSize(aDesiredImageSize);
+ iconURI->GetContentType(aContentType);
+ iconURI->GetFileExtension(aFileExtension);
+
+ nsCOMPtr<nsIURL> url;
+ rv = iconURI->GetIconURL(getter_AddRefs(url));
+ if (NS_FAILED(rv) || !url) return NS_OK;
+
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
+ if (NS_FAILED(rv) || !fileURL) return NS_OK;
+
+ nsCOMPtr<nsIFile> file;
+ rv = fileURL->GetFile(getter_AddRefs(file));
+ if (NS_FAILED(rv) || !file) return NS_OK;
+
+ return file->Clone(aLocalFile);
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
+ nsISupports* ctxt)
+{
+ MOZ_ASSERT(!mLoadInfo ||
+ mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone() ||
+ (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
+ nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
+ "security flags in loadInfo but asyncOpen2() not called");
+
+ nsCOMPtr<nsIInputStream> inStream;
+ nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Init our streampump
+ rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ rv = mPump->AsyncRead(this, ctxt);
+ if (NS_SUCCEEDED(rv)) {
+ // Store our real listener
+ mListener = aListener;
+ // Add ourself to the load group, if available
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return AsyncOpen(listener, nullptr);
+}
+
+static DWORD
+GetSpecialFolderIcon(nsIFile* aFile, int aFolder,
+ SHFILEINFOW* aSFI, UINT aInfoFlags)
+{
+ DWORD shellResult = 0;
+
+ if (!aFile) {
+ return shellResult;
+ }
+
+ wchar_t fileNativePath[MAX_PATH];
+ nsAutoString fileNativePathStr;
+ aFile->GetPath(fileNativePathStr);
+ ::GetShortPathNameW(fileNativePathStr.get(), fileNativePath,
+ ArrayLength(fileNativePath));
+
+ LPITEMIDLIST idList;
+ HRESULT hr = ::SHGetSpecialFolderLocation(nullptr, aFolder, &idList);
+ if (SUCCEEDED(hr)) {
+ wchar_t specialNativePath[MAX_PATH];
+ ::SHGetPathFromIDListW(idList, specialNativePath);
+ ::GetShortPathNameW(specialNativePath, specialNativePath,
+ ArrayLength(specialNativePath));
+
+ if (!wcsicmp(fileNativePath, specialNativePath)) {
+ aInfoFlags |= (SHGFI_PIDL | SHGFI_SYSICONINDEX);
+ shellResult = ::SHGetFileInfoW((LPCWSTR)(LPCITEMIDLIST)idList, 0,
+ aSFI,
+ sizeof(*aSFI), aInfoFlags);
+ }
+ }
+ CoTaskMemFree(idList);
+ return shellResult;
+}
+
+static UINT
+GetSizeInfoFlag(uint32_t aDesiredImageSize)
+{
+ return
+ (UINT) (aDesiredImageSize > 16 ? SHGFI_SHELLICONSIZE : SHGFI_SMALLICON);
+}
+
+nsresult
+nsIconChannel::GetHIconFromFile(HICON* hIcon)
+{
+ nsXPIDLCString contentType;
+ nsCString fileExt;
+ nsCOMPtr<nsIFile> localFile; // file we want an icon for
+ uint32_t desiredImageSize;
+ nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(localFile),
+ &desiredImageSize, contentType,
+ fileExt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if the file exists, we are going to use it's real attributes...
+ // otherwise we only want to use it for it's extension...
+ SHFILEINFOW sfi;
+ UINT infoFlags = SHGFI_ICON;
+
+ bool fileExists = false;
+
+ nsAutoString filePath;
+ CopyASCIItoUTF16(fileExt, filePath);
+ if (localFile) {
+ rv = localFile->Normalize();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ localFile->GetPath(filePath);
+ if (filePath.Length() < 2 || filePath[1] != ':') {
+ return NS_ERROR_MALFORMED_URI; // UNC
+ }
+
+ if (filePath.Last() == ':') {
+ filePath.Append('\\');
+ } else {
+ localFile->Exists(&fileExists);
+ if (!fileExists) {
+ localFile->GetLeafName(filePath);
+ }
+ }
+ }
+
+ if (!fileExists) {
+ infoFlags |= SHGFI_USEFILEATTRIBUTES;
+ }
+
+ infoFlags |= GetSizeInfoFlag(desiredImageSize);
+
+ // if we have a content type... then use it! but for existing files,
+ // we want to show their real icon.
+ if (!fileExists && !contentType.IsEmpty()) {
+ nsCOMPtr<nsIMIMEService> mimeService
+ (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString defFileExt;
+ mimeService->GetPrimaryExtension(contentType, fileExt, defFileExt);
+ // If the mime service does not know about this mime type, we show
+ // the generic icon.
+ // In any case, we need to insert a '.' before the extension.
+ filePath = NS_LITERAL_STRING(".") +
+ NS_ConvertUTF8toUTF16(defFileExt);
+ }
+
+ // Is this the "Desktop" folder?
+ DWORD shellResult = GetSpecialFolderIcon(localFile, CSIDL_DESKTOP,
+ &sfi, infoFlags);
+ if (!shellResult) {
+ // Is this the "My Documents" folder?
+ shellResult = GetSpecialFolderIcon(localFile, CSIDL_PERSONAL,
+ &sfi, infoFlags);
+ }
+
+ // There are other "Special Folders" and Namespace entities that we
+ // are not fetching icons for, see:
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
+ // shellcc/platform/shell/reference/enums/csidl.asp
+ // If we ever need to get them, code to do so would be inserted here.
+
+ // Not a special folder, or something else failed above.
+ if (!shellResult) {
+ shellResult = ::SHGetFileInfoW(filePath.get(),
+ FILE_ATTRIBUTE_ARCHIVE,
+ &sfi, sizeof(sfi), infoFlags);
+ }
+
+ if (shellResult && sfi.hIcon) {
+ *hIcon = sfi.hIcon;
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return rv;
+}
+
+nsresult
+nsIconChannel::GetStockHIcon(nsIMozIconURI* aIconURI,
+ HICON* hIcon)
+{
+ nsresult rv = NS_OK;
+
+ // We can only do this on Vista or above
+ HMODULE hShellDLL = ::LoadLibraryW(L"shell32.dll");
+ decltype(SHGetStockIconInfo)* pSHGetStockIconInfo =
+ (decltype(SHGetStockIconInfo)*) ::GetProcAddress(hShellDLL,
+ "SHGetStockIconInfo");
+
+ if (pSHGetStockIconInfo) {
+ uint32_t desiredImageSize;
+ aIconURI->GetImageSize(&desiredImageSize);
+ nsAutoCString stockIcon;
+ aIconURI->GetStockIcon(stockIcon);
+
+ SHSTOCKICONID stockIconID = GetStockIconIDForName(stockIcon);
+ if (stockIconID == SIID_INVALID) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ UINT infoFlags = SHGSI_ICON;
+ infoFlags |= GetSizeInfoFlag(desiredImageSize);
+
+ SHSTOCKICONINFO sii = {0};
+ sii.cbSize = sizeof(sii);
+ HRESULT hr = pSHGetStockIconInfo(stockIconID, infoFlags, &sii);
+
+ if (SUCCEEDED(hr)) {
+ *hIcon = sii.hIcon;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+ } else {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (hShellDLL) {
+ ::FreeLibrary(hShellDLL);
+ }
+
+ return rv;
+}
+
+// Given a BITMAPINFOHEADER, returns the size of the color table.
+static int
+GetColorTableSize(BITMAPINFOHEADER* aHeader)
+{
+ int colorTableSize = -1;
+
+ // http://msdn.microsoft.com/en-us/library/dd183376%28v=VS.85%29.aspx
+ switch (aHeader->biBitCount) {
+ case 0:
+ colorTableSize = 0;
+ break;
+ case 1:
+ colorTableSize = 2 * sizeof(RGBQUAD);
+ break;
+ case 4:
+ case 8: {
+ // The maximum possible size for the color table is 2**bpp, so check for
+ // that and fail if we're not in those bounds
+ unsigned int maxEntries = 1 << (aHeader->biBitCount);
+ if (aHeader->biClrUsed > 0 && aHeader->biClrUsed <= maxEntries) {
+ colorTableSize = aHeader->biClrUsed * sizeof(RGBQUAD);
+ } else if (aHeader->biClrUsed == 0) {
+ colorTableSize = maxEntries * sizeof(RGBQUAD);
+ }
+ break;
+ }
+ case 16:
+ case 32:
+ // If we have BI_BITFIELDS compression, we would normally need 3 DWORDS for
+ // the bitfields mask which would be stored in the color table; However,
+ // we instead force the bitmap to request data of type BI_RGB so the color
+ // table should be of size 0.
+ // Setting aHeader->biCompression = BI_RGB forces the later call to
+ // GetDIBits to return to us BI_RGB data.
+ if (aHeader->biCompression == BI_BITFIELDS) {
+ aHeader->biCompression = BI_RGB;
+ }
+ colorTableSize = 0;
+ break;
+ case 24:
+ colorTableSize = 0;
+ break;
+ }
+
+ if (colorTableSize < 0) {
+ NS_WARNING("Unable to figure out the color table size for this bitmap");
+ }
+
+ return colorTableSize;
+}
+
+// Given a header and a size, creates a freshly allocated BITMAPINFO structure.
+// It is the caller's responsibility to null-check and delete the structure.
+static BITMAPINFO*
+CreateBitmapInfo(BITMAPINFOHEADER* aHeader, size_t aColorTableSize)
+{
+ BITMAPINFO* bmi = (BITMAPINFO*) ::operator new(sizeof(BITMAPINFOHEADER) +
+ aColorTableSize,
+ mozilla::fallible);
+ if (bmi) {
+ memcpy(bmi, aHeader, sizeof(BITMAPINFOHEADER));
+ memset(bmi->bmiColors, 0, aColorTableSize);
+ }
+ return bmi;
+}
+
+nsresult
+nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool aNonBlocking)
+{
+ // Check whether the icon requested's a file icon or a stock icon
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+
+ // GetDIBits does not exist on windows mobile.
+ HICON hIcon = nullptr;
+
+ nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString stockIcon;
+ iconURI->GetStockIcon(stockIcon);
+ if (!stockIcon.IsEmpty()) {
+ rv = GetStockHIcon(iconURI, &hIcon);
+ } else {
+ rv = GetHIconFromFile(&hIcon);
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (hIcon) {
+ // we got a handle to an icon. Now we want to get a bitmap for the icon
+ // using GetIconInfo....
+ ICONINFO iconInfo;
+ if (GetIconInfo(hIcon, &iconInfo)) {
+ // we got the bitmaps, first find out their size
+ HDC hDC = CreateCompatibleDC(nullptr); // get a device context for
+ // the screen.
+ BITMAPINFOHEADER maskHeader = {sizeof(BITMAPINFOHEADER)};
+ BITMAPINFOHEADER colorHeader = {sizeof(BITMAPINFOHEADER)};
+ int colorTableSize, maskTableSize;
+ if (GetDIBits(hDC, iconInfo.hbmMask, 0, 0, nullptr,
+ (BITMAPINFO*)&maskHeader, DIB_RGB_COLORS) &&
+ GetDIBits(hDC, iconInfo.hbmColor, 0, 0, nullptr,
+ (BITMAPINFO*)&colorHeader, DIB_RGB_COLORS) &&
+ maskHeader.biHeight == colorHeader.biHeight &&
+ maskHeader.biWidth == colorHeader.biWidth &&
+ colorHeader.biBitCount > 8 &&
+ colorHeader.biSizeImage > 0 &&
+ colorHeader.biWidth >= 0 && colorHeader.biWidth <= 255 &&
+ colorHeader.biHeight >= 0 && colorHeader.biHeight <= 255 &&
+ maskHeader.biSizeImage > 0 &&
+ (colorTableSize = GetColorTableSize(&colorHeader)) >= 0 &&
+ (maskTableSize = GetColorTableSize(&maskHeader)) >= 0) {
+ uint32_t iconSize = sizeof(ICONFILEHEADER) +
+ sizeof(ICONENTRY) +
+ sizeof(BITMAPINFOHEADER) +
+ colorHeader.biSizeImage +
+ maskHeader.biSizeImage;
+
+ UniquePtr<char[]> buffer = MakeUnique<char[]>(iconSize);
+ if (!buffer) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ } else {
+ char* whereTo = buffer.get();
+ int howMuch;
+
+ // the data starts with an icon file header
+ ICONFILEHEADER iconHeader;
+ iconHeader.ifhReserved = 0;
+ iconHeader.ifhType = 1;
+ iconHeader.ifhCount = 1;
+ howMuch = sizeof(ICONFILEHEADER);
+ memcpy(whereTo, &iconHeader, howMuch);
+ whereTo += howMuch;
+
+ // followed by the single icon entry
+ ICONENTRY iconEntry;
+ iconEntry.ieWidth = static_cast<int8_t>(colorHeader.biWidth);
+ iconEntry.ieHeight = static_cast<int8_t>(colorHeader.biHeight);
+ iconEntry.ieColors = 0;
+ iconEntry.ieReserved = 0;
+ iconEntry.iePlanes = 1;
+ iconEntry.ieBitCount = colorHeader.biBitCount;
+ iconEntry.ieSizeImage = sizeof(BITMAPINFOHEADER) +
+ colorHeader.biSizeImage +
+ maskHeader.biSizeImage;
+ iconEntry.ieFileOffset = sizeof(ICONFILEHEADER) + sizeof(ICONENTRY);
+ howMuch = sizeof(ICONENTRY);
+ memcpy(whereTo, &iconEntry, howMuch);
+ whereTo += howMuch;
+
+ // followed by the bitmap info header
+ // (doubling the height because icons have two bitmaps)
+ colorHeader.biHeight *= 2;
+ colorHeader.biSizeImage += maskHeader.biSizeImage;
+ howMuch = sizeof(BITMAPINFOHEADER);
+ memcpy(whereTo, &colorHeader, howMuch);
+ whereTo += howMuch;
+ colorHeader.biHeight /= 2;
+ colorHeader.biSizeImage -= maskHeader.biSizeImage;
+
+ // followed by the XOR bitmap data (colorHeader)
+ // (you'd expect the color table to come here, but it apparently
+ // doesn't)
+ BITMAPINFO* colorInfo = CreateBitmapInfo(&colorHeader,
+ colorTableSize);
+ if (colorInfo && GetDIBits(hDC, iconInfo.hbmColor, 0,
+ colorHeader.biHeight, whereTo, colorInfo,
+ DIB_RGB_COLORS)) {
+ whereTo += colorHeader.biSizeImage;
+
+ // and finally the AND bitmap data (maskHeader)
+ BITMAPINFO* maskInfo = CreateBitmapInfo(&maskHeader, maskTableSize);
+ if (maskInfo && GetDIBits(hDC, iconInfo.hbmMask, 0,
+ maskHeader.biHeight, whereTo, maskInfo,
+ DIB_RGB_COLORS)) {
+ // Now, create a pipe and stuff our data into it
+ nsCOMPtr<nsIInputStream> inStream;
+ nsCOMPtr<nsIOutputStream> outStream;
+ rv = NS_NewPipe(getter_AddRefs(inStream),
+ getter_AddRefs(outStream),
+ iconSize, iconSize, aNonBlocking);
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t written;
+ rv = outStream->Write(buffer.get(), iconSize, &written);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*_retval = inStream);
+ }
+ }
+
+ } // if we got bitmap bits
+ delete maskInfo;
+ } // if we got mask bits
+ delete colorInfo;
+ } // if we allocated the buffer
+ } // if we got mask size
+
+ DeleteDC(hDC);
+ DeleteObject(iconInfo.hbmColor);
+ DeleteObject(iconInfo.hbmMask);
+ } // if we got icon info
+ DestroyIcon(hIcon);
+ } // if we got an hIcon
+
+ // If we didn't make a stream, then fail.
+ if (!*_retval && NS_SUCCEEDED(rv)) {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentType(nsACString& aContentType)
+{
+ aContentType.AssignLiteral(IMAGE_ICO);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentType(const nsACString& aContentType)
+{
+ // It doesn't make sense to set the content-type on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString& aContentCharset)
+{
+ aContentCharset.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentCharset(const nsACString& aContentCharset)
+{
+ // It doesn't make sense to set the content-charset on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentDisposition(uint32_t* aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionFilename(nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionHeader(nsACString& aContentDispositionHeader)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentLength(int64_t* aContentLength)
+{
+ *aContentLength = 0;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentLength(int64_t aContentLength)
+{
+ NS_NOTREACHED("nsIconChannel::SetContentLength");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetOwner(nsISupports** aOwner)
+{
+ *aOwner = mOwner.get();
+ NS_IF_ADDREF(*aOwner);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOwner(nsISupports* aOwner)
+{
+ mOwner = aOwner;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
+{
+ NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+ mLoadInfo = aLoadInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetNotificationCallbacks(nsIInterfaceRequestor** aNotificationCallbacks)
+{
+ *aNotificationCallbacks = mCallbacks.get();
+ NS_IF_ADDREF(*aNotificationCallbacks);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
+{
+ mCallbacks = aNotificationCallbacks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetSecurityInfo(nsISupports** aSecurityInfo)
+{
+ *aSecurityInfo = nullptr;
+ return NS_OK;
+}
+
+// nsIRequestObserver methods
+NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest,
+ nsISupports* aContext)
+{
+ if (mListener) {
+ return mListener->OnStartRequest(this, aContext);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::OnStopRequest(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsresult aStatus)
+{
+ if (mListener) {
+ mListener->OnStopRequest(this, aContext, aStatus);
+ mListener = nullptr;
+ }
+
+ // Remove from load group
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+ }
+
+ // Drop notification callbacks to prevent cycles.
+ mCallbacks = nullptr;
+
+ return NS_OK;
+}
+
+// nsIStreamListener methods
+NS_IMETHODIMP
+nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream* aStream,
+ uint64_t aOffset,
+ uint32_t aCount)
+{
+ if (mListener) {
+ return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
+ }
+ return NS_OK;
+}
diff --git a/image/decoders/icon/win/nsIconChannel.h b/image/decoders/icon/win/nsIconChannel.h
new file mode 100644
index 000000000..94df5a52e
--- /dev/null
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_image_encoders_icon_win_nsIconChannel_h
+#define mozilla_image_encoders_icon_win_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "nsXPIDLString.h"
+#include "nsIChannel.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIURI.h"
+#include "nsIInputStreamPump.h"
+#include "nsIStreamListener.h"
+#include "nsIIconURI.h"
+
+#include <windows.h>
+
+class nsIFile;
+
+class nsIconChannel final : public nsIChannel, public nsIStreamListener
+{
+ ~nsIconChannel();
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIREQUEST
+ NS_DECL_NSICHANNEL
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsIconChannel();
+
+ nsresult Init(nsIURI* uri);
+
+protected:
+ nsCOMPtr<nsIURI> mUrl;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+ nsCOMPtr<nsISupports> mOwner;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+
+ nsCOMPtr<nsIInputStreamPump> mPump;
+ nsCOMPtr<nsIStreamListener> mListener;
+
+ nsresult ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsCString& aContentType,
+ nsCString& aFileExtension);
+ nsresult GetHIconFromFile(HICON* hIcon);
+ nsresult MakeInputStream(nsIInputStream** _retval, bool nonBlocking);
+
+ // Functions specific to Vista and above
+protected:
+ nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* hIcon);
+};
+
+#endif // mozilla_image_encoders_icon_win_nsIconChannel_h