Repository: electron/electron (language: cpp)

Issue:
[Bug]: libgdk-pixbuf library is not dynamically linked
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

17.x.y

### What operating system are you using?

Ubuntu

### Operating System Version

Xubuntu 20.04, Ubuntu 16.04

### What arch are you using?

x64

### Last Known Working Electron version

_No response_

### Expected Behavior

While porting over Chromium's script to generate dependencies for Debian packages in VS Code (https://github.com/microsoft/vscode/issues/13089), I noticed that libgdk-pixbuf is statically linked rather than dynamically linked. An issue is that the exact name of the library changed over time, so sometimes it is libgdk-pixbuf-2.0-0, and other times it is libgdk-pixbuf2.0-0. Therefore, Electron should look up the library at runtime.

### Actual Behavior

Using the Electron sysroot when searching for Debian dependencies, it seems libgdk-pixbuf-2.0-0 is statically linked: https://github.com/microsoft/vscode/pull/147335/files#diff-b84879f8920b8ee14b70af14c40c90a255472a38795104384f64db32f1c7505dR56

I verified in a separate CI run that the dependency is indeed contributed by Electron, and not by other shared libraries that VS Code uses.

### Testcase Gist URL

_No response_

### Additional Information

Since libgdk is responsible for rendering menus and file dialogs, I think we should log a message and crash the program if the dependency cannot be found during runtime.

CC @deepak1556 


Discussion / hints:
To add some context,  chromium removed the static dependency on the `libgdk-pixbuf` package as part of https://chromium-review.googlesource.com/c/chromium/src/+/2645039. During the `//ui/gtk` refactor in upstream, in one of the chromium rolls I had copied over a utility function that was removed https://github.com/electron/electron/pull/20649/commits/be693fa66c719720bce5e2359fa7253d224090f2 and today's static dependency on the library comes from this `GdkPixbufFromSkBitmap` utility. We can make the library dynamic similar to upstream.

> Since libgdk is responsible for rendering menus and file dialogs, I think we should log a message and crash the program if the dependency cannot be found during runtime.

GTK library depends on `libgdk-pixbuf` and Electron depends on GTK, so there will not be a case where the dependency is not found.

Fix patch:
diff --git a/BUILD.gn b/BUILD.gn
index 9b62fb12029d4..f1ac729440b24 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -87,7 +87,10 @@ if (is_linux) {
   # implementation. In future, this file can be extended to contain
   # gtk4 stubs to switch gtk version in runtime.
   generate_stubs("electron_gtk_stubs") {
-    sigs = [ "shell/browser/ui/electron_gtk.sigs" ]
+    sigs = [
+      "shell/browser/ui/electron_gdk_pixbuf.sigs",
+      "shell/browser/ui/electron_gtk.sigs",
+    ]
     extra_header = "shell/browser/ui/electron_gtk.fragment"
     output_name = "electron_gtk_stubs"
     public_deps = [ "//ui/gtk:gtk_config" ]
diff --git a/patches/chromium/make_gtk_getlibgtk_public.patch b/patches/chromium/make_gtk_getlibgtk_public.patch
index dd4dec37023ec..730a2877e5aa2 100644
--- a/patches/chromium/make_gtk_getlibgtk_public.patch
+++ b/patches/chromium/make_gtk_getlibgtk_public.patch
@@ -1,16 +1,28 @@
 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
 From: deepak1556 <hop2deep@gmail.com>
 Date: Thu, 7 Apr 2022 20:30:16 +0900
-Subject: Make gtk::GetLibGtk public
+Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
 
-Allows embedders to get a handle to the gtk library
-already loaded in the process.
+Allows embedders to get a handle to the gtk and
+gdk_pixbuf libraries already loaded in the process.
 
 diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
-index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e98274025148604760b7477f9 100644
+index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..65b097cfab72b92f301968715eb218ef0e468567 100644
 --- a/ui/gtk/gtk_compat.cc
 +++ b/ui/gtk/gtk_compat.cc
-@@ -86,12 +86,6 @@ void* GetLibGtk4(bool check = true) {
+@@ -66,11 +66,6 @@ void* GetLibGio() {
+   return libgio;
+ }
+ 
+-void* GetLibGdkPixbuf() {
+-  static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
+-  return libgdk_pixbuf;
+-}
+-
+ void* GetLibGdk3() {
+   static void* libgdk3 = DlOpen("libgdk-3.so.0");
+   return libgdk3;
+@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
    return libgtk4;
  }
  
@@ -23,10 +35,15 @@ index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e9827402514860476
  bool LoadGtk3() {
    if (!GetLibGtk3(false))
      return false;
-@@ -134,6 +128,12 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
+@@ -134,6 +123,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
  
  }  // namespace
  
++void* GetLibGdkPixbuf() {
++  static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
++  return libgdk_pixbuf;
++}
++
 +void* GetLibGtk() {
 +  if (GtkCheckVersion(4))
 +    return GetLibGtk4();
@@ -37,13 +54,16 @@ index b5c7af5bdb93b320f95252d35d2d76bae7f8c445..40b706ed7cde206e9827402514860476
    static bool loaded = LoadGtkImpl();
    return loaded;
 diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
-index 57e55b9e749b43d327deff449a530e1f435a8e8b..2245974f91be4a691d82f54b55e12e44ae2000c5 100644
+index 57e55b9e749b43d327deff449a530e1f435a8e8b..37720be9e393d192b3b7db13a007431a9ce77ddc 100644
 --- a/ui/gtk/gtk_compat.h
 +++ b/ui/gtk/gtk_compat.h
-@@ -34,6 +34,9 @@ using SkColor = uint32_t;
+@@ -34,6 +34,12 @@ using SkColor = uint32_t;
  
  namespace gtk {
  
++// Get handle to the currently loaded gdk_pixbuf library in the process.
++void* GetLibGdkPixbuf();
++
 +// Get handle to the currently loaded gtk library in the process.
 +void* GetLibGtk();
 +
diff --git a/shell/browser/electron_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc
index 151fc8d17a758..dd5f187ce9ed5 100644
--- a/shell/browser/electron_browser_main_parts.cc
+++ b/shell/browser/electron_browser_main_parts.cc
@@ -371,6 +371,10 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
     electron::UninitializeElectron_gtk();
   }
 
+  electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
+  CHECK(electron::IsElectron_gdk_pixbufInitialized())
+      << "Failed to initialize libgdk_pixbuf-2.0.so.0";
+
   // Chromium does not respect GTK dark theme setting, but they may change
   // in future and this code might be no longer needed. Check the Chromium
   // issue to keep updated:
diff --git a/shell/browser/ui/electron_gdk_pixbuf.sigs b/shell/browser/ui/electron_gdk_pixbuf.sigs
new file mode 100644
index 0000000000000..c19eb4679265f
--- /dev/null
+++ b/shell/browser/ui/electron_gdk_pixbuf.sigs
@@ -0,0 +1,3 @@
+GdkPixbuf* gdk_pixbuf_new(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height)
+GdkPixbuf* gdk_pixbuf_scale_simple(const GdkPixbuf* src, int dest_width, int dest_height, GdkInterpType interp_type)
+guchar* gdk_pixbuf_get_pixels(const GdkPixbuf* pixbuf)
diff --git a/shell/browser/ui/electron_gtk.fragment b/shell/browser/ui/electron_gtk.fragment
index 74a31b23585ac..677c382087029 100644
--- a/shell/browser/ui/electron_gtk.fragment
+++ b/shell/browser/ui/electron_gtk.fragment
@@ -1,1 +1,2 @@
-#include <gtk/gtk.h>
\ No newline at end of file
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
diff --git a/shell/browser/ui/electron_gtk.sigs b/shell/browser/ui/electron_gtk.sigs
index 451fc74a953d6..41f3890096e09 100644
--- a/shell/browser/ui/electron_gtk.sigs
+++ b/shell/browser/ui/electron_gtk.sigs
@@ -4,4 +4,4 @@ void gtk_native_dialog_show(GtkNativeDialog* self);
 void gtk_native_dialog_hide(GtkNativeDialog* self);
 gint gtk_native_dialog_run(GtkNativeDialog* self);
 void gtk_native_dialog_destroy(GtkNativeDialog* self);
-GType gtk_native_dialog_get_type(void);
\ No newline at end of file
+GType gtk_native_dialog_get_type(void);
diff --git a/shell/browser/ui/gtk_util.cc b/shell/browser/ui/gtk_util.cc
index 3d279485cf505..7fc6154bf99f3 100644
--- a/shell/browser/ui/gtk_util.cc
+++ b/shell/browser/ui/gtk_util.cc
@@ -12,6 +12,7 @@
 
 #include "base/no_destructor.h"
 #include "base/strings/string_number_conversions.h"
+#include "electron/electron_gtk_stubs.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkUnPreMultiply.h"
diff --git a/shell/browser/ui/message_box_gtk.cc b/shell/browser/ui/message_box_gtk.cc
index 3efc453fc97bd..6266dad29ef51 100644
--- a/shell/browser/ui/message_box_gtk.cc
+++ b/shell/browser/ui/message_box_gtk.cc
@@ -11,6 +11,7 @@
 #include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "electron/electron_gtk_stubs.h"
 #include "shell/browser/browser.h"
 #include "shell/browser/native_window_observer.h"
 #include "shell/browser/native_window_views.h"


Test patch: