summaryrefslogtreecommitdiffstats
path: root/image/decoders/icon
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /image/decoders/icon
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--image/decoders/icon/android/moz.build13
-rw-r--r--image/decoders/icon/android/nsIconChannel.cpp125
-rw-r--r--image/decoders/icon/android/nsIconChannel.h45
-rw-r--r--image/decoders/icon/components.conf19
-rw-r--r--image/decoders/icon/gtk/moz.build13
-rw-r--r--image/decoders/icon/gtk/nsIconChannel.cpp390
-rw-r--r--image/decoders/icon/gtk/nsIconChannel.h42
-rw-r--r--image/decoders/icon/mac/moz.build11
-rw-r--r--image/decoders/icon/mac/nsIconChannel.h61
-rw-r--r--image/decoders/icon/mac/nsIconChannelCocoa.mm491
-rw-r--r--image/decoders/icon/moz.build39
-rw-r--r--image/decoders/icon/nsIconProtocolHandler.cpp81
-rw-r--r--image/decoders/icon/nsIconProtocolHandler.h25
-rw-r--r--image/decoders/icon/nsIconURI.cpp659
-rw-r--r--image/decoders/icon/nsIconURI.h118
-rw-r--r--image/decoders/icon/win/moz.build15
-rw-r--r--image/decoders/icon/win/nsIconChannel.cpp950
-rw-r--r--image/decoders/icon/win/nsIconChannel.h83
18 files changed, 3180 insertions, 0 deletions
diff --git a/image/decoders/icon/android/moz.build b/image/decoders/icon/android/moz.build
new file mode 100644
index 0000000000..a99ae228d4
--- /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 0000000000..7599cf2bb7
--- /dev/null
+++ b/image/decoders/icon/android/nsIconChannel.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 20; 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 <stdlib.h>
+#include "mozilla/gfx/Swizzle.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/NullPrincipal.h"
+#include "nsMimeTypes.h"
+#include "nsXULAppAPI.h"
+#include "AndroidBridge.h"
+#include "nsIconChannel.h"
+#include "nsIIconURI.h"
+#include "nsIStringStream.h"
+#include "nsNetUtil.h"
+#include "nsComponentManagerUtils.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
+ nsTArray<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;
+ CheckedInt32 buf_size =
+ 4 + channels * CheckedInt32(height) * CheckedInt32(width);
+ if (!buf_size.isValid()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint8_t* const buf = (uint8_t*)moz_xmalloc(buf_size.value());
+ uint8_t* out = buf;
+
+ *(out++) = width;
+ *(out++) = height;
+ *(out++) = uint8_t(mozilla::gfx::SurfaceFormat::R8G8B8A8);
+
+ // Set all bits to ensure in nsIconDecoder we color manage and premultiply.
+ *(out++) = 0xFF;
+
+ nsresult rv;
+ if (XRE_IsParentProcess()) {
+ rv = GetIconForExtension(aFileExt, aIconSize, out);
+ } else {
+ rv = CallRemoteGetIconForExtension(aFileExt, aIconSize, out);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ 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.value());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIconProtocolHandler::NewChannel 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 opened.
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ NullPrincipal::CreateWithoutOriginAttributes();
+ return NS_NewInputStreamChannel(
+ aChannel, aURI, stream.forget(), nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE, nsLiteralCString(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 0000000000..e25196c6ee
--- /dev/null
+++ b/image/decoders/icon/android/nsIconChannel.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; 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/. */
+
+#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 "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/components.conf b/image/decoders/icon/components.conf
new file mode 100644
index 0000000000..e374e7b60c
--- /dev/null
+++ b/image/decoders/icon/components.conf
@@ -0,0 +1,19 @@
+# -*- 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/.
+
+Classes = [
+ {
+ 'cid': '{d0f9db12-249c-11d5-9905-001083010e9b}',
+ 'contract_ids': ['@mozilla.org/network/protocol;1?name=moz-icon'],
+ 'type': 'nsIconProtocolHandler',
+ 'headers': ['/image/decoders/icon/nsIconProtocolHandler.h'],
+ },
+ {
+ 'cid': '{1460df3b-774c-4205-8349-838e507c3ef9}',
+ 'type': 'nsMozIconURI::Mutator',
+ 'headers': ['/image/decoders/icon/nsIconURI.h'],
+ },
+]
diff --git a/image/decoders/icon/gtk/moz.build b/image/decoders/icon/gtk/moz.build
new file mode 100644
index 0000000000..09acc05231
--- /dev/null
+++ b/image/decoders/icon/gtk/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",
+]
+
+FINAL_LIBRARY = "xul"
+
+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 0000000000..954bd7beb1
--- /dev/null
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -0,0 +1,390 @@
+/* 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 "mozilla/NullPrincipal.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/gfx/Swizzle.h"
+#include <algorithm>
+
+#include <gio/gio.h>
+
+#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 "nsIURL.h"
+#include "prlink.h"
+#include "gfxPlatform.h"
+
+using mozilla::CheckedInt32;
+
+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;
+ CheckedInt32 buf_size =
+ 4 + n_channels * CheckedInt32(height) * CheckedInt32(width);
+ if (!buf_size.isValid()) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint8_t* const buf = (uint8_t*)moz_xmalloc(buf_size.value());
+ uint8_t* out = buf;
+
+ *(out++) = width;
+ *(out++) = height;
+ *(out++) = uint8_t(mozilla::gfx::SurfaceFormat::OS_RGBA);
+
+ // Set all bits to ensure in nsIconDecoder we color manage and premultiply.
+ *(out++) = 0xFF;
+
+ const guchar* const pixels = gdk_pixbuf_get_pixels(aPixbuf);
+ int instride = gdk_pixbuf_get_rowstride(aPixbuf);
+ int outstride = width * n_channels;
+
+ // encode the RGB data and the A data and adjust the stride as necessary.
+ mozilla::gfx::SwizzleData(pixels, instride,
+ mozilla::gfx::SurfaceFormat::R8G8B8A8, out,
+ outstride, mozilla::gfx::SurfaceFormat::OS_RGBA,
+ mozilla::gfx::IntSize(width, height));
+
+ 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.value());
+
+ // If this no longer holds then re-examine buf's lifetime.
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIconProtocolHandler::NewChannel 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 opened.
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ mozilla::NullPrincipal::CreateWithoutOriginAttributes();
+ return NS_NewInputStreamChannel(
+ aChannel, aURI, stream.forget(), nullPrincipal,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
+ nsIContentPolicy::TYPE_INTERNAL_IMAGE, nsLiteralCString(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;
+}
+
+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;
+ }
+ 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) {
+ nsAutoCString spec;
+ fileURI->GetAsciiSpec(spec);
+ if (fileURI->SchemeIs("file")) {
+ 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;
+}
+
+nsresult nsIconChannel::Init(nsIURI* aURI) {
+ nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
+ NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
+
+ if (gfxPlatform::IsHeadless()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsAutoCString stockIcon;
+ iconURI->GetStockIcon(stockIcon);
+ if (stockIcon.IsEmpty()) {
+ return InitWithGIO(iconURI);
+ }
+
+ // 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, "-ltr"_ns)) {
+ direction = GTK_TEXT_DIR_LTR;
+ } else if (StringEndsWith(stockIcon, "-rtl"_ns)) {
+ 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 paths 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 chosen 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 0000000000..5a8bae263a
--- /dev/null
+++ b/image/decoders/icon/gtk/nsIconChannel.h
@@ -0,0 +1,42 @@
+/* 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 "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 0000000000..fee884a473
--- /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 0000000000..dca2a3c51a
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannel.h
@@ -0,0 +1,61 @@
+/* -*- 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/. */
+
+#ifndef mozilla_image_encoders_icon_mac_nsIconChannel_h
+#define mozilla_image_encoders_icon_mac_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.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"
+#include "nsNetUtil.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;
+ bool mCanceled = false;
+
+ [[nodiscard]] 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 0000000000..82ced3becc
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -0,0 +1,491 @@
+/* -*- 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 "nsContentUtils.h"
+#include "nsIconChannel.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/EndianUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIIconURI.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsString.h"
+#include "nsMimeTypes.h"
+#include "nsMemory.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsIPipe.h"
+#include "nsIOutputStream.h"
+#include "nsCExternalHandlerService.h"
+#include "nsILocalFileMac.h"
+#include "nsIFileURL.h"
+#include "nsTArray.h"
+#include "nsObjCExceptions.h"
+#include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
+#include "nsNetUtil.h"
+#include "mozilla/RefPtr.h"
+
+#include <Cocoa/Cocoa.h>
+
+using namespace mozilla;
+
+// nsIconChannel methods
+nsIconChannel::nsIconChannel() {}
+
+nsIconChannel::~nsIconChannel() {
+ if (mLoadInfo) {
+ NS_ReleaseOnMainThread("nsIconChannel::mLoadInfo", 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) {
+ mCanceled = true;
+ return mPump->Cancel(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetCanceled(bool* result) {
+ *result = mCanceled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::Suspend(void) { return mPump->Suspend(); }
+
+NS_IMETHODIMP
+nsIconChannel::Resume(void) { return mPump->Resume(); }
+
+// nsIRequestObserver methods
+NS_IMETHODIMP
+nsIconChannel::OnStartRequest(nsIRequest* aRequest) {
+ if (mListener) {
+ return mListener->OnStartRequest(this);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
+ if (mListener) {
+ mListener->OnStopRequest(this, 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, nsIInputStream* aStream, uint64_t aOffset,
+ uint32_t aCount) {
+ if (mListener) {
+ return mListener->OnDataAvailable(this, 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) {
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return MakeInputStream(_retval, false);
+}
+
+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) {
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ return rv;
+ }
+
+ MOZ_ASSERT(mLoadInfo->GetSecurityMode() == 0 || mLoadInfo->GetInitialSecurityCheckDone() ||
+ (mLoadInfo->GetSecurityMode() ==
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
+ mLoadInfo->GetLoadingPrincipal() &&
+ mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
+ "security flags in loadInfo but doContentSecurityCheck() not called");
+
+ nsCOMPtr<nsIInputStream> inStream;
+ rv = MakeInputStream(getter_AddRefs(inStream), true);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ return rv;
+ }
+
+ // Init our stream pump
+ nsCOMPtr<nsIEventTarget> target =
+ nsContentUtils::GetEventTargetByLoadInfo(mLoadInfo, mozilla::TaskCategory::Other);
+ rv = mPump->Init(inStream, 0, 0, false, target);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ return rv;
+ }
+
+ rv = mPump->AsyncRead(this);
+ if (NS_SUCCEEDED(rv)) {
+ // Store our real listener
+ mListener = aListener;
+ // Add ourself to the load group, if available
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+ } else {
+ mCallbacks = nullptr;
+ }
+
+ return rv;
+}
+
+nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool aNonBlocking) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsCString 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) {
+ 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;
+ }
+
+ if (desiredImageSize > 255) {
+ // The Icon image format represents width and height as u8, so it does not
+ // allow for images sized 256 or more.
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set the actual size to *twice* the requested size.
+ // We do this because our UI doesn't take the window's device pixel ratio into
+ // account when it requests these icons; e.g. it will request an icon with
+ // size 16, place it in a 16x16 CSS pixel sized image, and then display it in
+ // a window on a HiDPI screen where the icon now covers 32x32 physical screen
+ // pixels. So we just always double the size here in order to prevent blurriness.
+ uint32_t size = (desiredImageSize < 128) ? desiredImageSize * 2 : desiredImageSize;
+ uint32_t width = size;
+ uint32_t height = size;
+
+ // The "image format" we're outputting here (and which gets decoded by
+ // nsIconDecoder) has the following format:
+ // - 1 byte for the image width, as u8
+ // - 1 byte for the image height, as u8
+ // - the raw image data as BGRA, width * height * 4 bytes.
+ size_t bufferCapacity = 4 + width * height * 4;
+ UniquePtr<uint8_t[]> fileBuf = MakeUnique<uint8_t[]>(bufferCapacity);
+ fileBuf[0] = uint8_t(width);
+ fileBuf[1] = uint8_t(height);
+ fileBuf[2] = uint8_t(mozilla::gfx::SurfaceFormat::B8G8R8A8);
+
+ // Clear all bits to ensure in nsIconDecoder we assume we are already color
+ // managed and premultiplied.
+ fileBuf[3] = 0;
+
+ uint8_t* imageBuf = &fileBuf[4];
+
+ // Create a CGBitmapContext around imageBuf and draw iconImage to it.
+ // This gives us the image data in the format we want: BGRA, four bytes per
+ // pixel, in host endianness, with premultiplied alpha.
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ CGContextRef ctx =
+ CGBitmapContextCreate(imageBuf, width, height, 8 /* bitsPerComponent */, width * 4, cs,
+ kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+ CGColorSpaceRelease(cs);
+
+ NSGraphicsContext* oldContext = [NSGraphicsContext currentContext];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx
+ flipped:NO]];
+
+ [iconImage drawInRect:NSMakeRect(0, 0, width, height)];
+
+ [NSGraphicsContext setCurrentContext:oldContext];
+
+ CGContextRelease(ctx);
+
+ // 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*)fileBuf.get(), 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::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { return GetTRRModeImpl(aTRRMode); }
+
+NS_IMETHODIMP
+nsIconChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { return SetTRRModeImpl(aTRRMode); }
+
+NS_IMETHODIMP
+nsIconChannel::GetIsDocument(bool* aIsDocument) {
+ return NS_GetIsDocumentChannel(this, aIsDocument);
+}
+
+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) {
+ MOZ_ASSERT_UNREACHABLE("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) {
+ MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
+ 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 0000000000..96cf951b3b
--- /dev/null
+++ b/image/decoders/icon/moz.build
@@ -0,0 +1,39 @@
+# -*- 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 += [
+ "nsIconProtocolHandler.cpp",
+ "nsIconURI.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+FINAL_LIBRARY = "xul"
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+EXPORTS += [
+ "nsIconURI.h",
+]
+
+platform = None
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ 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/nsIconProtocolHandler.cpp b/image/decoders/icon/nsIconProtocolHandler.cpp
new file mode 100644
index 0000000000..035dcd6238
--- /dev/null
+++ b/image/decoders/icon/nsIconProtocolHandler.cpp
@@ -0,0 +1,81 @@
+/* -*- 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 "nsIconProtocolHandler.h"
+
+#include "nsIconChannel.h"
+#include "nsIconURI.h"
+#include "nsCRT.h"
+#include "nsCOMPtr.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::NewChannel(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;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/image/decoders/icon/nsIconProtocolHandler.h b/image/decoders/icon/nsIconProtocolHandler.h
new file mode 100644
index 0000000000..63843eaa4b
--- /dev/null
+++ b/image/decoders/icon/nsIconProtocolHandler.h
@@ -0,0 +1,25 @@
+/* -*- 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/. */
+
+#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 0000000000..29f9c78064
--- /dev/null
+++ b/image/decoders/icon/nsIconURI.cpp
@@ -0,0 +1,659 @@
+/* -*- 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 "nsIClassInfoImpl.h"
+#include "nsIIOService.h"
+#include "nsISerializable.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.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
+
+static NS_DEFINE_CID(kThisIconURIImplementationCID,
+ NS_THIS_ICONURI_IMPLEMENTATION_CID);
+static NS_DEFINE_CID(kIconURICID, NS_ICONURI_CID);
+
+// 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"};
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_CLASSINFO(nsMozIconURI, nullptr, nsIClassInfo::THREADSAFE,
+ NS_ICONURI_CID)
+// Empty CI getter. We only need nsIClassInfo for Serialization
+NS_IMPL_CI_INTERFACE_GETTER0(nsMozIconURI)
+
+nsMozIconURI::nsMozIconURI()
+ : mSize(DEFAULT_IMAGE_SIZE), mIconSize(-1), mIconState(-1) {}
+
+nsMozIconURI::~nsMozIconURI() {}
+
+NS_IMPL_ADDREF(nsMozIconURI)
+NS_IMPL_RELEASE(nsMozIconURI)
+
+NS_INTERFACE_MAP_BEGIN(nsMozIconURI)
+ if (aIID.Equals(kThisIconURIImplementationCID)) {
+ foundInterface = static_cast<nsIURI*>(this);
+ } else
+ NS_INTERFACE_MAP_ENTRY(nsIMozIconURI)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURI)
+ NS_INTERFACE_MAP_ENTRY(nsIURI)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINestedURI, mIconURL)
+ NS_INTERFACE_MAP_ENTRY(nsISerializable)
+ NS_IMPL_QUERY_CLASSINFO(nsMozIconURI)
+NS_INTERFACE_MAP_END
+
+#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::GetDisplaySpec(nsACString& aUnicodeSpec) {
+ return GetSpec(aUnicodeSpec);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetDisplayHostPort(nsACString& aUnicodeHostPort) {
+ return GetHostPort(aUnicodeHostPort);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetDisplayHost(nsACString& aUnicodeHost) {
+ return GetHost(aUnicodeHost);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetDisplayPrePath(nsACString& aPrePath) {
+ return GetPrePath(aPrePath);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHasRef(bool* result) {
+ *result = false;
+ return NS_OK;
+}
+
+NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsMozIconURI::Mutator, nsIURISetters,
+ nsIURIMutator, nsISerializable)
+
+NS_IMETHODIMP
+nsMozIconURI::Mutate(nsIURIMutator** aMutator) {
+ RefPtr<nsMozIconURI::Mutator> mutator = new nsMozIconURI::Mutator();
+ nsresult rv = mutator->InitFromURI(this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ mutator.forget(aMutator);
+ 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
+}
+
+nsresult nsMozIconURI::SetSpecInternal(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) ||
+ (!Substring(iconSpec, MOZICON_SCHEME_LEN, 7).EqualsLiteral("file://") &&
+ // Checking for the leading '//' will match both the '//stock/' and
+ // '//.foo' cases:
+ !Substring(iconSpec, MOZICON_SCHEME_LEN, 2).EqualsLiteral("//"))) {
+ 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)) {
+ // 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) {
+ // The inner URI should be a 'file:' one. If not, bail.
+ if (!mIconURL->SchemeIs("file")) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+ 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;
+}
+
+nsresult 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; }
+
+nsresult nsMozIconURI::SetUsername(const nsACString& aUsername) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPassword(nsACString& aPassword) { return NS_ERROR_FAILURE; }
+
+nsresult nsMozIconURI::SetPassword(const nsACString& aPassword) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetUserPass(nsACString& aUserPass) { return NS_ERROR_FAILURE; }
+
+nsresult nsMozIconURI::SetUserPass(const nsACString& aUserPass) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHostPort(nsACString& aHostPort) { return NS_ERROR_FAILURE; }
+
+nsresult nsMozIconURI::SetHostPort(const nsACString& aHostPort) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetHost(nsACString& aHost) { return NS_ERROR_FAILURE; }
+
+nsresult nsMozIconURI::SetHost(const nsACString& aHost) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetPort(int32_t* aPort) { return NS_ERROR_FAILURE; }
+
+nsresult nsMozIconURI::SetPort(int32_t aPort) { return NS_ERROR_FAILURE; }
+
+NS_IMETHODIMP
+nsMozIconURI::GetPathQueryRef(nsACString& aPath) {
+ aPath.Truncate();
+ return NS_OK;
+}
+
+nsresult nsMozIconURI::SetPathQueryRef(const nsACString& aPath) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetFilePath(nsACString& aFilePath) {
+ aFilePath.Truncate();
+ return NS_OK;
+}
+
+nsresult nsMozIconURI::SetFilePath(const nsACString& aFilePath) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetQuery(nsACString& aQuery) {
+ aQuery.Truncate();
+ return NS_OK;
+}
+
+nsresult nsMozIconURI::SetQuery(const nsACString& aQuery) {
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsMozIconURI::SetQueryWithEncoding(const nsACString& aQuery,
+ const Encoding* aEncoding) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetRef(nsACString& aRef) {
+ aRef.Truncate();
+ return NS_OK;
+}
+
+nsresult nsMozIconURI::SetRef(const nsACString& aRef) {
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::Equals(nsIURI* other, bool* result) {
+ *result = false;
+ NS_ENSURE_ARG_POINTER(other);
+ MOZ_ASSERT(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) {
+ MOZ_ASSERT(aEquals, "null pointer");
+ if (!aScheme) {
+ *aEquals = false;
+ return NS_OK;
+ }
+
+ *aEquals = PL_strcasecmp("moz-icon", aScheme) ? false : true;
+ return NS_OK;
+}
+
+nsresult nsMozIconURI::Clone(nsIURI** result) {
+ nsCOMPtr<nsIURL> newIconURL;
+ if (mIconURL) {
+ newIconURL = mIconURL;
+ }
+
+ RefPtr<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;
+ uri.forget(result);
+
+ return NS_OK;
+}
+
+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); }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIIconUri methods:
+
+NS_IMETHODIMP
+nsMozIconURI::GetIconURL(nsIURL** aFileUrl) {
+ *aFileUrl = mIconURL;
+ NS_IF_ADDREF(*aFileUrl);
+ return NS_OK;
+}
+
+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::GetContentType(nsACString& aContentType) {
+ aContentType = mContentType;
+ 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;
+}
+
+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() = Some(std::move(iconURLParams));
+ } else {
+ params.uri() = Nothing();
+ }
+
+ 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().isSome()) {
+ nsCOMPtr<nsIURI> uri = DeserializeURI(params.uri().ref());
+ 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();
+
+ if (params.iconSize() < -1 ||
+ params.iconSize() >= (int32_t)ArrayLength(kSizeStrings)) {
+ return false;
+ }
+ mIconSize = params.iconSize();
+
+ if (params.iconState() < -1 ||
+ params.iconState() >= (int32_t)ArrayLength(kStateStrings)) {
+ return false;
+ }
+ mIconState = params.iconState();
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetInnerURI(nsIURI** aURI) {
+ nsCOMPtr<nsIURI> iconURL = mIconURL;
+ if (!iconURL) {
+ *aURI = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ iconURL.forget(aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMozIconURI::GetInnermostURI(nsIURI** aURI) {
+ return NS_ImplGetInnermostURI(this, aURI);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::Read(nsIObjectInputStream* aStream) {
+ MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult nsMozIconURI::ReadPrivate(nsIObjectInputStream* aStream) {
+ nsAutoCString spec;
+ nsresult rv = aStream->ReadCString(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return SetSpecInternal(spec);
+}
+
+NS_IMETHODIMP
+nsMozIconURI::Write(nsIObjectOutputStream* aStream) {
+ nsAutoCString spec;
+ nsresult rv = GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return aStream->WriteStringZ(spec.get());
+}
diff --git a/image/decoders/icon/nsIconURI.h b/image/decoders/icon/nsIconURI.h
new file mode 100644
index 0000000000..1f55bc686c
--- /dev/null
+++ b/image/decoders/icon/nsIconURI.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef mozilla_image_decoders_icon_nsIconURI_h
+#define mozilla_image_decoders_icon_nsIconURI_h
+
+#include "nsIIconURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsINestedURI.h"
+#include "nsIURIMutator.h"
+#include "nsISerializable.h"
+
+#define NS_THIS_ICONURI_IMPLEMENTATION_CID \
+ { /* 0b9bb0c2-fee6-470b-b9b9-9fd9462b5e19 */ \
+ 0x5c3e417f, 0xb686, 0x4105, { \
+ 0x86, 0xe7, 0xf9, 0x1b, 0xac, 0x97, 0x4d, 0x5c \
+ } \
+ }
+
+namespace mozilla {
+class Encoding;
+}
+
+class nsMozIconURI final : public nsIMozIconURI,
+ public nsINestedURI,
+ public nsISerializable {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSIMOZICONURI
+ NS_DECL_NSINESTEDURI
+ NS_DECL_NSISERIALIZABLE
+
+ protected:
+ nsMozIconURI();
+ 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
+
+ private:
+ nsresult Clone(nsIURI** aURI);
+ nsresult SetSpecInternal(const nsACString& input);
+ nsresult SetScheme(const nsACString& input);
+ nsresult SetUserPass(const nsACString& input);
+ nsresult SetUsername(const nsACString& input);
+ nsresult SetPassword(const nsACString& input);
+ nsresult SetHostPort(const nsACString& aValue);
+ nsresult SetHost(const nsACString& input);
+ nsresult SetPort(int32_t port);
+ nsresult SetPathQueryRef(const nsACString& input);
+ nsresult SetRef(const nsACString& input);
+ nsresult SetFilePath(const nsACString& input);
+ nsresult SetQuery(const nsACString& input);
+ nsresult SetQueryWithEncoding(const nsACString& input,
+ const mozilla::Encoding* encoding);
+ nsresult ReadPrivate(nsIObjectInputStream* stream);
+ bool Deserialize(const mozilla::ipc::URIParams&);
+
+ public:
+ class Mutator final : public nsIURIMutator,
+ public BaseURIMutator<nsMozIconURI>,
+ public nsISerializable {
+ NS_DECL_ISUPPORTS
+ NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
+
+ NS_IMETHOD
+ Write(nsIObjectOutputStream* aOutputStream) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ [[nodiscard]] NS_IMETHOD Read(nsIObjectInputStream* aStream) override {
+ return InitFromInputStream(aStream);
+ }
+
+ NS_IMETHOD Deserialize(const mozilla::ipc::URIParams& aParams) override {
+ return InitFromIPCParams(aParams);
+ }
+
+ NS_IMETHOD Finalize(nsIURI** aURI) override {
+ mURI.forget(aURI);
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetSpec(const nsACString& aSpec,
+ nsIURIMutator** aMutator) override {
+ if (aMutator) {
+ nsCOMPtr<nsIURIMutator> mutator = this;
+ mutator.forget(aMutator);
+ }
+ return InitFromSpec(aSpec);
+ }
+
+ explicit Mutator() {}
+
+ private:
+ virtual ~Mutator() {}
+
+ friend class nsMozIconURI;
+ };
+
+ friend BaseURIMutator<nsMozIconURI>;
+};
+
+#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 0000000000..2a06f68fec
--- /dev/null
+++ b/image/decoders/icon/win/moz.build
@@ -0,0 +1,15 @@
+# -*- 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",
+]
+
+LOCAL_INCLUDES += [
+ "/image",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/image/decoders/icon/win/nsIconChannel.cpp b/image/decoders/icon/win/nsIconChannel.cpp
new file mode 100644
index 0000000000..28d2ccc873
--- /dev/null
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -0,0 +1,950 @@
+/* -*- 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/ArrayUtils.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Monitor.h"
+
+#include "nsComponentManagerUtils.h"
+#include "nsIconChannel.h"
+#include "nsIIconURI.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsMimeTypes.h"
+#include "nsMemory.h"
+#include "nsIURL.h"
+#include "nsIPipe.h"
+#include "nsNetCID.h"
+#include "nsIFile.h"
+#include "nsIFileURL.h"
+#include "nsIInputStream.h"
+#include "nsIMIMEService.h"
+#include "nsCExternalHandlerService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
+#include "nsContentUtils.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+
+#include "Decoder.h"
+#include "DecodePool.h"
+
+// 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;
+using namespace mozilla::image;
+
+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;
+}
+
+class nsIconChannel::IconAsyncOpenTask final : public Runnable {
+ public:
+ IconAsyncOpenTask(nsIconChannel* aChannel, nsIEventTarget* aTarget,
+ nsCOMPtr<nsIFile>&& aLocalFile, nsAutoString& aPath,
+ UINT aInfoFlags)
+ : Runnable("IconAsyncOpenTask"),
+ mChannel(aChannel),
+
+ mTarget(aTarget),
+ mLocalFile(std::move(aLocalFile)),
+ mPath(aPath),
+ mInfoFlags(aInfoFlags) {}
+
+ NS_IMETHOD Run() override;
+
+ private:
+ RefPtr<nsIconChannel> mChannel;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ nsCOMPtr<nsIFile> mLocalFile;
+ nsAutoString mPath;
+ UINT mInfoFlags;
+};
+
+NS_IMETHODIMP nsIconChannel::IconAsyncOpenTask::Run() {
+ HICON hIcon = nullptr;
+ nsresult rv =
+ mChannel->GetHIconFromFile(mLocalFile, mPath, mInfoFlags, &hIcon);
+ // Effectively give ownership of mChannel to the runnable so it get released
+ // on the main thread.
+ RefPtr<nsIconChannel> channel = mChannel.forget();
+ nsCOMPtr<nsIRunnable> task = NewRunnableMethod<HICON, nsresult>(
+ "nsIconChannel::FinishAsyncOpen", channel,
+ &nsIconChannel::FinishAsyncOpen, hIcon, rv);
+ mTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+ return NS_OK;
+}
+
+class nsIconChannel::IconSyncOpenTask final : public Runnable {
+ public:
+ IconSyncOpenTask(nsIconChannel* aChannel, nsIEventTarget* aTarget,
+ nsCOMPtr<nsIFile>&& aLocalFile, nsAutoString& aPath,
+ UINT aInfoFlags)
+ : Runnable("IconSyncOpenTask"),
+ mMonitor("IconSyncOpenTask"),
+ mDone(false),
+ mChannel(aChannel),
+ mTarget(aTarget),
+ mLocalFile(std::move(aLocalFile)),
+ mPath(aPath),
+ mInfoFlags(aInfoFlags),
+ mHIcon(nullptr),
+ mRv(NS_OK) {}
+
+ NS_IMETHOD Run() override;
+
+ Monitor& GetMonitor() { return mMonitor; }
+ bool Done() const {
+ mMonitor.AssertCurrentThreadOwns();
+ return mDone;
+ }
+ HICON GetHIcon() const {
+ mMonitor.AssertCurrentThreadOwns();
+ return mHIcon;
+ }
+ nsresult GetRv() const {
+ mMonitor.AssertCurrentThreadOwns();
+ return mRv;
+ }
+
+ private:
+ Monitor mMonitor;
+ bool mDone;
+ // Parameters in
+ RefPtr<nsIconChannel> mChannel;
+ nsCOMPtr<nsIEventTarget> mTarget;
+ nsCOMPtr<nsIFile> mLocalFile;
+ nsAutoString mPath;
+ UINT mInfoFlags;
+ // Return values
+ HICON mHIcon;
+ nsresult mRv;
+};
+
+NS_IMETHODIMP
+nsIconChannel::IconSyncOpenTask::Run() {
+ MonitorAutoLock lock(mMonitor);
+ mRv = mChannel->GetHIconFromFile(mLocalFile, mPath, mInfoFlags, &mHIcon);
+ mDone = true;
+ mMonitor.NotifyAll();
+ // Do this little dance because nsIconChannel multiple inherits from
+ // nsISupports.
+ nsCOMPtr<nsIChannel> channel = mChannel.forget();
+ NS_ProxyRelease("IconSyncOpenTask::mChannel", mTarget, channel.forget());
+ return NS_OK;
+}
+
+// nsIconChannel methods
+nsIconChannel::nsIconChannel() {}
+
+nsIconChannel::~nsIconChannel() {
+ if (mLoadInfo) {
+ NS_ReleaseOnMainThread("nsIconChannel::mLoadInfo", mLoadInfo.forget());
+ }
+ if (mLoadGroup) {
+ NS_ReleaseOnMainThread("nsIconChannel::mLoadGroup", mLoadGroup.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) {
+ mCanceled = true;
+ return mPump->Cancel(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetCanceled(bool* result) {
+ *result = mCanceled;
+ return NS_OK;
+}
+
+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);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
+ return GetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
+ return SetTRRModeImpl(aTRRMode);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetIsDocument(bool* aIsDocument) {
+ return NS_GetIsDocumentChannel(this, aIsDocument);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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** aStream) {
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv =
+ nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ HICON hIcon = nullptr;
+ rv = GetHIcon(/* aNonBlocking */ false, &hIcon);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return MakeInputStream(aStream, /* aNonBlocking */ false, hIcon);
+}
+
+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);
+}
+
+void nsIconChannel::OnAsyncError(nsresult aStatus) {
+ OnStartRequest(this);
+ OnStopRequest(this, aStatus);
+}
+
+void nsIconChannel::FinishAsyncOpen(HICON aIcon, nsresult aStatus) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (NS_FAILED(aStatus)) {
+ OnAsyncError(aStatus);
+ return;
+ }
+
+ nsCOMPtr<nsIInputStream> inStream;
+ nsresult rv = MakeInputStream(getter_AddRefs(inStream),
+ /* aNonBlocking */ true, aIcon);
+ if (NS_FAILED(rv)) {
+ OnAsyncError(rv);
+ return;
+ }
+
+ rv = mPump->Init(inStream, 0, 0, false, mListenerTarget);
+ if (NS_FAILED(rv)) {
+ OnAsyncError(rv);
+ return;
+ }
+
+ rv = mPump->AsyncRead(this);
+ if (NS_FAILED(rv)) {
+ OnAsyncError(rv);
+ }
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen(nsIStreamListener* aListener) {
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv =
+ nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ return rv;
+ }
+
+ MOZ_ASSERT(
+ mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone() ||
+ (mLoadInfo->GetSecurityMode() ==
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
+ mLoadInfo->GetLoadingPrincipal() &&
+ mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
+ "security flags in loadInfo but doContentSecurityCheck() not called");
+
+ nsCOMPtr<nsIInputStream> inStream;
+ rv = EnsurePipeCreated(/* aIconSize */ 0, /* aNonBlocking */ true);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ return rv;
+ }
+
+ mListenerTarget = nsContentUtils::GetEventTargetByLoadInfo(
+ mLoadInfo, mozilla::TaskCategory::Other);
+ if (!mListenerTarget) {
+ mListenerTarget = do_GetMainThread();
+ }
+
+ // If we pass aNonBlocking as true, GetHIcon will always have dispatched
+ // upon success.
+ HICON hIcon = nullptr;
+ rv = GetHIcon(/* aNonBlocking */ true, &hIcon);
+ if (NS_FAILED(rv)) {
+ mCallbacks = nullptr;
+ mInputStream = nullptr;
+ mOutputStream = nullptr;
+ mListenerTarget = nullptr;
+ return rv;
+ }
+
+ // We shouldn't have the icon yet if it is non-blocking.
+ MOZ_ASSERT(!hIcon);
+
+ // Add ourself to the load group, if available
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+
+ // Store our real listener
+ mListener = aListener;
+ return NS_OK;
+}
+
+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(bool aNonBlocking, HICON* hIcon) {
+ nsCString 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...
+ 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 = u"."_ns + NS_ConvertUTF8toUTF16(defFileExt);
+ }
+
+ if (aNonBlocking) {
+ RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
+ RefPtr<IconAsyncOpenTask> task = new IconAsyncOpenTask(
+ this, mListenerTarget, std::move(localFile), filePath, infoFlags);
+ target->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+ return NS_OK;
+ }
+
+ // We cannot call SHGetFileInfo on more than one thread (at a time), so it
+ // must be called on the same thread every time, even now when we need sync
+ // behaviour. So we synchronously wait on the other thread to finish.
+ RefPtr<nsIEventTarget> target = DecodePool::Singleton()->GetIOEventTarget();
+ RefPtr<IconSyncOpenTask> task = new IconSyncOpenTask(
+ this, mListenerTarget, std::move(localFile), filePath, infoFlags);
+ MonitorAutoLock lock(task->GetMonitor());
+ target->Dispatch(task, NS_DISPATCH_NORMAL);
+ do {
+ task->GetMonitor().Wait();
+ } while (!task->Done());
+ *hIcon = task->GetHIcon();
+ return task->GetRv();
+}
+
+nsresult nsIconChannel::GetHIconFromFile(nsIFile* aLocalFile,
+ const nsAutoString& aPath,
+ UINT aInfoFlags, HICON* hIcon) {
+ SHFILEINFOW sfi;
+
+ // Is this the "Desktop" folder?
+ DWORD shellResult =
+ GetSpecialFolderIcon(aLocalFile, CSIDL_DESKTOP, &sfi, aInfoFlags);
+ if (!shellResult) {
+ // Is this the "My Documents" folder?
+ shellResult =
+ GetSpecialFolderIcon(aLocalFile, CSIDL_PERSONAL, &sfi, aInfoFlags);
+ }
+
+ // 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(aPath.get(), FILE_ATTRIBUTE_ARCHIVE, &sfi,
+ sizeof(sfi), aInfoFlags);
+ }
+
+ if (!shellResult || !sfi.hIcon) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *hIcon = sfi.hIcon;
+ return NS_OK;
+}
+
+nsresult nsIconChannel::GetStockHIcon(nsIMozIconURI* aIconURI, HICON* hIcon) {
+ nsresult rv = NS_OK;
+
+ 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 = SHGetStockIconInfo(stockIconID, infoFlags, &sii);
+
+ if (SUCCEEDED(hr)) {
+ *hIcon = sii.hIcon;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ 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::EnsurePipeCreated(uint32_t aIconSize,
+ bool aNonBlocking) {
+ if (mInputStream || mOutputStream) {
+ return NS_OK;
+ }
+
+ return NS_NewPipe(getter_AddRefs(mInputStream), getter_AddRefs(mOutputStream),
+ aIconSize, aIconSize > 0 ? aIconSize : UINT32_MAX,
+ aNonBlocking);
+}
+
+nsresult nsIconChannel::GetHIcon(bool aNonBlocking, HICON* aIcon) {
+ // 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.
+ nsCOMPtr<nsIMozIconURI> iconURI(do_QueryInterface(mUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString stockIcon;
+ iconURI->GetStockIcon(stockIcon);
+ if (!stockIcon.IsEmpty()) {
+ return GetStockHIcon(iconURI, aIcon);
+ }
+
+ return GetHIconFromFile(aNonBlocking, aIcon);
+}
+
+nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval,
+ bool aNonBlocking, HICON aIcon) {
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (aIcon) {
+ // we got a handle to an icon. Now we want to get a bitmap for the icon
+ // using GetIconInfo....
+ ICONINFO iconInfo;
+ if (GetIconInfo(aIcon, &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
+ rv = EnsurePipeCreated(iconSize, aNonBlocking);
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t written;
+ rv = mOutputStream->Write(buffer.get(), iconSize, &written);
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*_retval = mInputStream);
+ }
+ }
+
+ } // 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(aIcon);
+ } // if we got an hIcon
+
+ // If we didn't make a stream, then fail.
+ if (!*_retval && NS_SUCCEEDED(rv)) {
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mInputStream = nullptr;
+ mOutputStream = nullptr;
+ 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) {
+ MOZ_ASSERT_UNREACHABLE("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) {
+ MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
+ 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) {
+ if (mListener) {
+ return mListener->OnStartRequest(this);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
+ if (mListener) {
+ mListener->OnStopRequest(this, aStatus);
+ mListener = nullptr;
+ }
+
+ // Remove from load group
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+ }
+
+ // Drop notification callbacks to prevent cycles.
+ mCallbacks = nullptr;
+ mListenerTarget = nullptr;
+
+ return NS_OK;
+}
+
+// nsIStreamListener methods
+NS_IMETHODIMP
+nsIconChannel::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream,
+ uint64_t aOffset, uint32_t aCount) {
+ if (mListener) {
+ return mListener->OnDataAvailable(this, 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 0000000000..f3bb93f348
--- /dev/null
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -0,0 +1,83 @@
+/* -*- 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/. */
+
+#ifndef mozilla_image_encoders_icon_win_nsIconChannel_h
+#define mozilla_image_encoders_icon_win_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIChannel.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIURI.h"
+#include "nsIInputStreamPump.h"
+#include "nsIOutputStream.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:
+ class IconAsyncOpenTask;
+ class IconSyncOpenTask;
+
+ void OnAsyncError(nsresult aStatus);
+ void FinishAsyncOpen(HICON aIcon, nsresult aStatus);
+ nsresult EnsurePipeCreated(uint32_t aIconSize, bool aNonBlocking);
+
+ nsCOMPtr<nsIURI> mUrl;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+ nsCOMPtr<nsISupports> mOwner;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+
+ nsCOMPtr<nsIInputStreamPump> mPump;
+ nsCOMPtr<nsIInputStream> mInputStream;
+ nsCOMPtr<nsIOutputStream> mOutputStream;
+ nsCOMPtr<nsIStreamListener> mListener;
+ nsCOMPtr<nsIEventTarget> mListenerTarget;
+
+ nsresult ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsCString& aContentType,
+ nsCString& aFileExtension);
+ nsresult GetHIcon(bool aNonBlocking, HICON* hIcon);
+ nsresult GetHIconFromFile(bool aNonBlocking, HICON* hIcon);
+ nsresult GetHIconFromFile(nsIFile* aLocalFile, const nsAutoString& aPath,
+ UINT aInfoFlags, HICON* hIcon);
+ [[nodiscard]] nsresult MakeInputStream(nsIInputStream** _retval,
+ bool aNonBlocking, HICON aIcon);
+
+ // Functions specific to Vista and above
+ protected:
+ nsresult GetStockHIcon(nsIMozIconURI* aIconURI, HICON* hIcon);
+
+ private:
+ bool mCanceled = false;
+};
+
+#endif // mozilla_image_encoders_icon_win_nsIconChannel_h