diff mbox series

[bug#66408,v3] gnu: icecat: Add support for Guix packaged extensions.

Message ID 43b86867b651d877c805d7e5963b42319606e0d1.1697027198.git.clement@lassieur.org
State New
Headers show
Series [bug#66408,v3] gnu: icecat: Add support for Guix packaged extensions. | expand

Commit Message

Clément Lassieur Oct. 11, 2023, 12:26 p.m. UTC
* gnu/build/icecat-extension.scm: New file with a MAKE-ICECAT-EXTENSION
procedure that makes sure the add-on directory is a symlink, so that Icecat
can normalize it into a package store path.
* gnu/local.mk (dist_patch_DATA): Register it, as well as new patches.
* gnu/packages/browser-extensions.scm (ublock-origin)[properties]: Store the
add-on ID so that it is accessible in MAKE-ICECAT-EXTENSION.
[arguments]: Use the add-on ID as root directory.
(ublock-origin/icecat): New procedure.
* gnu/packages/gnuzilla.scm (icecat-minimal)[arguments]: Rewrite the unused
'apply-guix-specific-patches' phase so that it applies the following two
patches.
[native-search-paths]: New field.
* gnu/packages/patches/icecat-compare-paths.patch: New patch that compares
add-on paths (which are package store paths) to detect package changes.
* gnu/packages/patches/icecat-use-guix-extensions.patch: New patch that
replaces "/usr/share/mozilla/extensions" with "$ICECAT_EXTENSION_DIR".
---
 gnu/build/icecat-extension.scm                | 71 +++++++++++++++++++
 gnu/local.mk                                  |  3 +
 gnu/packages/browser-extensions.scm           | 13 +++-
 gnu/packages/gnuzilla.scm                     | 22 +++---
 .../patches/icecat-compare-paths.patch        | 19 +++++
 .../patches/icecat-use-guix-extensions.patch  | 52 ++++++++++++++
 6 files changed, 167 insertions(+), 13 deletions(-)
 create mode 100644 gnu/build/icecat-extension.scm
 create mode 100644 gnu/packages/patches/icecat-compare-paths.patch
 create mode 100644 gnu/packages/patches/icecat-use-guix-extensions.patch


base-commit: 9ad9113fc238ee8de5191a5e15b5153fd149e9fa

Comments

Clément Lassieur Oct. 11, 2023, 2:52 p.m. UTC | #1
Hello,

Now that this patch looks stable to me, please let me introduce it
shortly.  I believe that a lot of people use Icecat extensions
downloaded from Mozilla store[2].  Mozzarella[1] is really just a
"plugin finder", so the extension is really downloaded from
https://addons.mozilla.org/, which means we have no control over the
software we are running, except for the bundled extensions.  An upgrade
could very well import non-free software when the initial version was
free.

I think it would be great to do like we do with Emacs and a lot of other
softwares: package our own plugins.  That would give users the assurance
that the software is free and can be trusted as they trust any Guix
packaged software.  That would also allow to do Guix-specific changes,
like removing the non-free bits, or adapting the package for technical
reasons.  And that would allow for a declarative deployment of the
browser, because the extensions could be added, say, to the Guix home
config.scm.

So this is what this patch is about.  And it works with the only add-on
that is currently packaged: uBlock Origin.  To test it one needs to
install both "icecat" and "ublock-origin-icecat".  This is related to
the work Marius did on ungoogled-chromium, and of course to the work
Mark did on Icecat, so both of you are CCed.  As for the technical
details, there is a long comment in "icecat-extensions.scm" explaining
what I did.

Thank you,
Clément Lassieur

[1]: https://gnuzilla.gnu.org/mozzarella/
[2]: https://addons.mozilla.org/

> * gnu/build/icecat-extension.scm: New file with a MAKE-ICECAT-EXTENSION
> procedure that makes sure the add-on directory is a symlink, so that Icecat
> can normalize it into a package store path.
> * gnu/local.mk (dist_patch_DATA): Register it, as well as new patches.
> * gnu/packages/browser-extensions.scm (ublock-origin)[properties]: Store the
> add-on ID so that it is accessible in MAKE-ICECAT-EXTENSION.
> [arguments]: Use the add-on ID as root directory.
> (ublock-origin/icecat): New procedure.
> * gnu/packages/gnuzilla.scm (icecat-minimal)[arguments]: Rewrite the unused
> 'apply-guix-specific-patches' phase so that it applies the following two
> patches.
> [native-search-paths]: New field.
> * gnu/packages/patches/icecat-compare-paths.patch: New patch that compares
> add-on paths (which are package store paths) to detect package changes.
> * gnu/packages/patches/icecat-use-guix-extensions.patch: New patch that
> replaces "/usr/share/mozilla/extensions" with "$ICECAT_EXTENSION_DIR".
Andrew Tropin Oct. 23, 2023, 7:37 a.m. UTC | #2
On 2023-10-11 14:26, Clément Lassieur wrote:

> * gnu/build/icecat-extension.scm: New file with a MAKE-ICECAT-EXTENSION
> procedure that makes sure the add-on directory is a symlink, so that Icecat
> can normalize it into a package store path.
> * gnu/local.mk (dist_patch_DATA): Register it, as well as new patches.
> * gnu/packages/browser-extensions.scm (ublock-origin)[properties]: Store the
> add-on ID so that it is accessible in MAKE-ICECAT-EXTENSION.
> [arguments]: Use the add-on ID as root directory.
> (ublock-origin/icecat): New procedure.
> * gnu/packages/gnuzilla.scm (icecat-minimal)[arguments]: Rewrite the unused
> 'apply-guix-specific-patches' phase so that it applies the following two
> patches.
> [native-search-paths]: New field.
> * gnu/packages/patches/icecat-compare-paths.patch: New patch that compares
> add-on paths (which are package store paths) to detect package changes.
> * gnu/packages/patches/icecat-use-guix-extensions.patch: New patch that
> replaces "/usr/share/mozilla/extensions" with "$ICECAT_EXTENSION_DIR".
> ---
>  gnu/build/icecat-extension.scm                | 71 +++++++++++++++++++
>  gnu/local.mk                                  |  3 +
>  gnu/packages/browser-extensions.scm           | 13 +++-
>  gnu/packages/gnuzilla.scm                     | 22 +++---
>  .../patches/icecat-compare-paths.patch        | 19 +++++
>  .../patches/icecat-use-guix-extensions.patch  | 52 ++++++++++++++
>  6 files changed, 167 insertions(+), 13 deletions(-)
>  create mode 100644 gnu/build/icecat-extension.scm
>  create mode 100644 gnu/packages/patches/icecat-compare-paths.patch
>  create mode 100644 gnu/packages/patches/icecat-use-guix-extensions.patch
>
> diff --git a/gnu/build/icecat-extension.scm b/gnu/build/icecat-extension.scm
> new file mode 100644
> index 000000000000..8ecd8a505f72
> --- /dev/null
> +++ b/gnu/build/icecat-extension.scm
> @@ -0,0 +1,71 @@
> +;;; GNU Guix --- Functional package management for GNU
> +;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
> +;;; Copyright © 2023 Clément Lassieur <clement@lassieur.org>
> +;;;
> +;;; This file is part of GNU Guix.
> +;;;
> +;;; GNU Guix is free software; you can redistribute it and/or modify it
> +;;; under the terms of the GNU General Public License as published by
> +;;; the Free Software Foundation; either version 3 of the License, or (at
> +;;; your option) any later version.
> +;;;
> +;;; GNU Guix is distributed in the hope that it will be useful, but
> +;;; WITHOUT ANY WARRANTY; without even the implied warranty of
> +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +;;; GNU General Public License for more details.
> +;;;
> +;;; You should have received a copy of the GNU General Public License
> +;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
> +
> +(define-module (gnu build icecat-extension)
> +  #:use-module (guix gexp)
> +  #:use-module (guix packages)
> +  #:use-module (guix build-system trivial)
> +  #:export (make-icecat-extension))
> +
> +(define* (make-icecat-extension pkg #:optional (pkg-output "out"))
> +  "Create an Icecat extension from package PKG and return a package that,
> +when installed, will make the extension contained in PKG available as an
> +Icecat browser extension.  PKG-OUTPUT specifies which output of PKG to use."
> +  (package
> +    (inherit pkg)
> +    (name (string-append (package-name pkg) "-icecat"))
> +    (native-inputs '())
> +    (inputs '())
> +    (propagated-inputs '())
> +    (outputs '("out"))
> +    (build-system trivial-build-system)
> +    (arguments
> +     (list
> +      #:modules '((guix build utils))
> +      #:builder
> +      #~(begin
> +          (use-modules (guix build utils))
> +          (let* ((id #$(assq-ref (package-properties pkg) 'id))
> +                 (moz-app-id "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
> +                 (search-dir (string-append #$output
> +                                            "/share/icecat/extensions/"
> +                                            moz-app-id)))
> +            ;; Icecat's iterates over `search-dir` for directories.  If a
> +            ;; directory's name is not a valid add-on ID, it is ignored.  See
> +            ;; `DirectoryLocation::readAddons()` in XPIProvider.jsm.
> +
> +            ;; This directory has to be a symlink, because Icecat's
> +            ;; `_readLinkFile(aFile)` calls `normalize()` only if `aFile` is a
> +            ;; symlink.
> +
> +            ;; Normalizing is required because Icecat compares the add-on path
> +            ;; against its local database to know if there is an extension
> +            ;; update.  We want the add-on path to be the package store path,
> +            ;; so that a path change is detected every time the package is
> +            ;; updated.  See `updateExistingAddon()` in XPIDatabase.jsm, with
> +            ;; our patch `icecat-compare-paths.patch`.
> +
> +            ;; We don't want the add-on path to be the profile store path,
> +            ;; which would change too often.  We don't want the add-on path to
> +            ;; be hard-coded ("/usr/share/mozilla/extensions") either because
> +            ;; it would never change.
> +
> +            (mkdir-p search-dir)
> +            (symlink (in-vicinity (ungexp pkg pkg-output) id)
> +                     (in-vicinity search-dir id))))))))
> diff --git a/gnu/local.mk b/gnu/local.mk
> index 9fa52833cbc5..1bf925a57d9e 100644
> --- a/gnu/local.mk
> +++ b/gnu/local.mk
> @@ -761,6 +761,7 @@ GNU_SYSTEM_MODULES =				\
>    %D%/build/chromium-extension.scm		\
>    %D%/build/cross-toolchain.scm			\
>    %D%/build/dbus-service.scm			\
> +  %D%/build/icecat-extension.scm		\
>    %D%/build/image.scm				\
>    %D%/build/jami-service.scm			\
>    %D%/build/file-systems.scm			\
> @@ -1415,6 +1416,8 @@ dist_patch_DATA =						\
>    %D%/packages/patches/icecat-makeicecat.patch			\
>    %D%/packages/patches/icecat-102-makeicecat.patch		\
>    %D%/packages/patches/icecat-avoid-bundled-libraries.patch	\
> +  %D%/packages/patches/icecat-compare-paths.patch		\
> +  %D%/packages/patches/icecat-use-guix-extensions.patch		\
>    %D%/packages/patches/icecat-use-system-graphite2+harfbuzz.patch	\
>    %D%/packages/patches/icecat-use-system-media-libs.patch	\
>    %D%/packages/patches/icedtea-7-hotspot-aarch64-use-c++98.patch	\
> diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
> index 3f6da8d77a3f..8a76f3a3e042 100644
> --- a/gnu/packages/browser-extensions.scm
> +++ b/gnu/packages/browser-extensions.scm
> @@ -1,6 +1,7 @@
>  ;;; GNU Guix --- Functional package management for GNU
>  ;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
>  ;;; Copyright © 2023 Nicolas Graves <ngraves@ngraves.fr>
> +;;; Copyright © 2023 Clément Lassieur <clement@lassieur.org>
>  ;;;
>  ;;; This file is part of GNU Guix.
>  ;;;
> @@ -25,6 +26,7 @@ (define-module (gnu packages browser-extensions)
>    #:use-module (guix build-system gnu)
>    #:use-module ((guix licenses) #:prefix license:)
>    #:use-module (gnu build chromium-extension)
> +  #:use-module (gnu build icecat-extension)
>    #:use-module (gnu packages compression)
>    #:use-module (gnu packages python))
>  
> @@ -98,6 +100,7 @@ (define ublock-origin
>                  "1i8rnij3sbwg6vj6znprrsca0n5xjzhmhppaa8v6jyxg6wrrfch1"))))
>      (build-system gnu-build-system)
>      (outputs '("xpi" "firefox" "chromium"))
> +    (properties '((id . "uBlock0@raymondhill.net")))
>      (arguments
>       (list
>        #:tests? #f                      ;no tests
> @@ -125,9 +128,10 @@ (define ublock-origin
>                (invoke "./tools/make-chromium.sh")))
>            (add-after 'build-chromium 'install
>              (lambda* (#:key outputs #:allow-other-keys)
> -              (let ((firefox (assoc-ref outputs "firefox"))
> -                    (xpi (assoc-ref outputs "xpi"))
> -                    (chromium (assoc-ref outputs "chromium")))
> +              (let* ((id #$(assq-ref (package-properties this-package) 'id))
> +                     (firefox (in-vicinity (assoc-ref outputs "firefox") id))
> +                     (xpi (assoc-ref outputs "xpi"))
> +                     (chromium (assoc-ref outputs "chromium")))
>                  (install-file "dist/build/uBlock0.firefox.xpi"
>                                (string-append xpi "/lib/mozilla/extensions"))
>                  (copy-recursively "dist/build/uBlock0.firefox" firefox)
> @@ -142,3 +146,6 @@ (define ublock-origin
>  
>  (define-public ublock-origin/chromium
>    (make-chromium-extension ublock-origin "chromium"))
> +
> +(define-public ublock-origin/icecat
> +  (make-icecat-extension ublock-origin "firefox"))
> diff --git a/gnu/packages/gnuzilla.scm b/gnu/packages/gnuzilla.scm
> index a367d7a00ff6..608219c97303 100644
> --- a/gnu/packages/gnuzilla.scm
> +++ b/gnu/packages/gnuzilla.scm
> @@ -885,16 +885,13 @@ (define-public icecat-minimal
>        #:phases
>        #~(modify-phases %standard-phases
>            (add-after 'unpack 'apply-guix-specific-patches
> -            (lambda* (#:key inputs native-inputs #:allow-other-keys)
> -              (let ((patch (search-input-file inputs "bin/patch")))
> -                (for-each (match-lambda
> -                            ((label . file)
> -                             (when (and (string-prefix? "icecat-" label)
> -                                        (string-suffix? ".patch" label))
> -                               (format #t "applying '~a'...~%" file)
> -                               (invoke patch "--force" "--no-backup-if-mismatch"
> -                                       "-p1" "--input" file))))
> -                          (or native-inputs inputs)))))
> +            (lambda _
> +              (for-each
> +               (lambda (file) (invoke "patch" "--force" "-p1" "-i" file))
> +               '(#$(local-file
> +                    (search-patch "icecat-compare-paths.patch"))
> +                 #$(local-file
> +                    (search-patch "icecat-use-guix-extensions.patch"))))))
>            (add-after 'apply-guix-specific-patches 'remove-bundled-libraries
>              (lambda _
>                ;; Remove bundled libraries that we don't use, since they may
> @@ -1138,6 +1135,11 @@ (define-public icecat-minimal
>                   '("default16.png" "default22.png" "default24.png"
>                     "default32.png" "default48.png" "content/icon64.png"
>                     "mozicon128.png" "default256.png"))))))))
> +    (native-search-paths
> +     (list (search-path-specification
> +            (variable "ICECAT_EXTENSION_DIR")
> +            (separator #f)              ;single entry
> +            (files '("share/icecat/extensions")))))
>      (home-page "https://www.gnu.org/software/gnuzilla/")
>      (synopsis "Entirely free browser derived from Mozilla Firefox")
>      (description
> diff --git a/gnu/packages/patches/icecat-compare-paths.patch b/gnu/packages/patches/icecat-compare-paths.patch
> new file mode 100644
> index 000000000000..9205899dc0c4
> --- /dev/null
> +++ b/gnu/packages/patches/icecat-compare-paths.patch
> @@ -0,0 +1,19 @@
> +--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
> ++++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
> +@@ -3452,6 +3452,7 @@ const XPIDatabaseReconcile = {
> +     if (
> +       newAddon ||
> +       oldAddon.updateDate != xpiState.mtime ||
> ++      oldAddon.path != xpiState.path ||
> +       (aUpdateCompatibility && this.isAppBundledLocation(installLocation))
> +     ) {
> +       newAddon = this.updateMetadata(
> +@@ -3460,8 +3461,6 @@ const XPIDatabaseReconcile = {
> +         xpiState,
> +         newAddon
> +       );
> +-    } else if (oldAddon.path != xpiState.path) {
> +-      newAddon = this.updatePath(installLocation, oldAddon, xpiState);
> +     } else if (aUpdateCompatibility || aSchemaChange) {
> +       newAddon = this.updateCompatibility(
> +         installLocation,
> diff --git a/gnu/packages/patches/icecat-use-guix-extensions.patch b/gnu/packages/patches/icecat-use-guix-extensions.patch
> new file mode 100644
> index 000000000000..f2fe4260c2cd
> --- /dev/null
> +++ b/gnu/packages/patches/icecat-use-guix-extensions.patch
> @@ -0,0 +1,52 @@
> +--- a/toolkit/xre/nsXREDirProvider.cpp
> ++++ b/toolkit/xre/nsXREDirProvider.cpp
> +@@ -415,13 +415,7 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
> + #if defined(XP_UNIX) && !defined(XP_MACOSX)
> +   else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
> + #  ifdef ENABLE_SYSTEM_EXTENSION_DIRS
> +-#    if defined(__OpenBSD__) || defined(__FreeBSD__)
> +-    static const char* const sysLExtDir = "/usr/local/share/mozilla/extensions";
> +-#    else
> +-    static const char* const sysLExtDir = "/usr/share/mozilla/extensions";
> +-#    endif
> +-    rv = NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false,
> +-                               getter_AddRefs(file));
> ++    rv = GetGuixExtensionDir(getter_AddRefs(file));
> + #  endif
> +   }
> + #endif  // defined(XP_UNIX) && !defined(XP_MACOSX)
> +@@ -1198,6 +1192,24 @@ nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile,
> +   return NS_OK;
> + }
> + 
> ++nsresult nsXREDirProvider::GetGuixExtensionDir(nsIFile** aFile) {
> ++  nsresult rv;
> ++  nsCOMPtr<nsIFile> localDir;
> ++
> ++#if defined(XP_UNIX)
> ++  const char* extensionDir = getenv("ICECAT_EXTENSION_DIR");
> ++  if (!extensionDir || !*extensionDir) return NS_ERROR_FAILURE;
> ++
> ++  rv = NS_NewNativeLocalFile(nsDependentCString(extensionDir), true,
> ++                             getter_AddRefs(localDir));
> ++#else
> ++#  error "Don't know how to get product dir on your platform"
> ++#endif
> ++
> ++  NS_IF_ADDREF(*aFile = localDir);
> ++  return rv;
> ++}
> ++
> + nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
> +                                                     bool aLocal) {
> +   // Copied from nsAppFileLocationProvider (more or less)
> +--- a/toolkit/xre/nsXREDirProvider.h
> ++++ b/toolkit/xre/nsXREDirProvider.h
> +@@ -112,6 +112,7 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2,
> +  private:
> +   nsresult GetFilesInternal(const char* aProperty,
> +                             nsISimpleEnumerator** aResult);
> ++  static nsresult GetGuixExtensionDir(nsIFile** aFile);
> +   static nsresult GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal);
> +   static nsresult GetSysUserExtensionsDirectory(nsIFile** aFile);
> + #if defined(XP_UNIX) || defined(XP_MACOSX)
>
> base-commit: 9ad9113fc238ee8de5191a5e15b5153fd149e9fa

Looks good!
diff mbox series

Patch

diff --git a/gnu/build/icecat-extension.scm b/gnu/build/icecat-extension.scm
new file mode 100644
index 000000000000..8ecd8a505f72
--- /dev/null
+++ b/gnu/build/icecat-extension.scm
@@ -0,0 +1,71 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
+;;; Copyright © 2023 Clément Lassieur <clement@lassieur.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu build icecat-extension)
+  #:use-module (guix gexp)
+  #:use-module (guix packages)
+  #:use-module (guix build-system trivial)
+  #:export (make-icecat-extension))
+
+(define* (make-icecat-extension pkg #:optional (pkg-output "out"))
+  "Create an Icecat extension from package PKG and return a package that,
+when installed, will make the extension contained in PKG available as an
+Icecat browser extension.  PKG-OUTPUT specifies which output of PKG to use."
+  (package
+    (inherit pkg)
+    (name (string-append (package-name pkg) "-icecat"))
+    (native-inputs '())
+    (inputs '())
+    (propagated-inputs '())
+    (outputs '("out"))
+    (build-system trivial-build-system)
+    (arguments
+     (list
+      #:modules '((guix build utils))
+      #:builder
+      #~(begin
+          (use-modules (guix build utils))
+          (let* ((id #$(assq-ref (package-properties pkg) 'id))
+                 (moz-app-id "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
+                 (search-dir (string-append #$output
+                                            "/share/icecat/extensions/"
+                                            moz-app-id)))
+            ;; Icecat's iterates over `search-dir` for directories.  If a
+            ;; directory's name is not a valid add-on ID, it is ignored.  See
+            ;; `DirectoryLocation::readAddons()` in XPIProvider.jsm.
+
+            ;; This directory has to be a symlink, because Icecat's
+            ;; `_readLinkFile(aFile)` calls `normalize()` only if `aFile` is a
+            ;; symlink.
+
+            ;; Normalizing is required because Icecat compares the add-on path
+            ;; against its local database to know if there is an extension
+            ;; update.  We want the add-on path to be the package store path,
+            ;; so that a path change is detected every time the package is
+            ;; updated.  See `updateExistingAddon()` in XPIDatabase.jsm, with
+            ;; our patch `icecat-compare-paths.patch`.
+
+            ;; We don't want the add-on path to be the profile store path,
+            ;; which would change too often.  We don't want the add-on path to
+            ;; be hard-coded ("/usr/share/mozilla/extensions") either because
+            ;; it would never change.
+
+            (mkdir-p search-dir)
+            (symlink (in-vicinity (ungexp pkg pkg-output) id)
+                     (in-vicinity search-dir id))))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 9fa52833cbc5..1bf925a57d9e 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -761,6 +761,7 @@  GNU_SYSTEM_MODULES =				\
   %D%/build/chromium-extension.scm		\
   %D%/build/cross-toolchain.scm			\
   %D%/build/dbus-service.scm			\
+  %D%/build/icecat-extension.scm		\
   %D%/build/image.scm				\
   %D%/build/jami-service.scm			\
   %D%/build/file-systems.scm			\
@@ -1415,6 +1416,8 @@  dist_patch_DATA =						\
   %D%/packages/patches/icecat-makeicecat.patch			\
   %D%/packages/patches/icecat-102-makeicecat.patch		\
   %D%/packages/patches/icecat-avoid-bundled-libraries.patch	\
+  %D%/packages/patches/icecat-compare-paths.patch		\
+  %D%/packages/patches/icecat-use-guix-extensions.patch		\
   %D%/packages/patches/icecat-use-system-graphite2+harfbuzz.patch	\
   %D%/packages/patches/icecat-use-system-media-libs.patch	\
   %D%/packages/patches/icedtea-7-hotspot-aarch64-use-c++98.patch	\
diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
index 3f6da8d77a3f..8a76f3a3e042 100644
--- a/gnu/packages/browser-extensions.scm
+++ b/gnu/packages/browser-extensions.scm
@@ -1,6 +1,7 @@ 
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2020, 2021 Marius Bakke <marius@gnu.org>
 ;;; Copyright © 2023 Nicolas Graves <ngraves@ngraves.fr>
+;;; Copyright © 2023 Clément Lassieur <clement@lassieur.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -25,6 +26,7 @@  (define-module (gnu packages browser-extensions)
   #:use-module (guix build-system gnu)
   #:use-module ((guix licenses) #:prefix license:)
   #:use-module (gnu build chromium-extension)
+  #:use-module (gnu build icecat-extension)
   #:use-module (gnu packages compression)
   #:use-module (gnu packages python))
 
@@ -98,6 +100,7 @@  (define ublock-origin
                 "1i8rnij3sbwg6vj6znprrsca0n5xjzhmhppaa8v6jyxg6wrrfch1"))))
     (build-system gnu-build-system)
     (outputs '("xpi" "firefox" "chromium"))
+    (properties '((id . "uBlock0@raymondhill.net")))
     (arguments
      (list
       #:tests? #f                      ;no tests
@@ -125,9 +128,10 @@  (define ublock-origin
               (invoke "./tools/make-chromium.sh")))
           (add-after 'build-chromium 'install
             (lambda* (#:key outputs #:allow-other-keys)
-              (let ((firefox (assoc-ref outputs "firefox"))
-                    (xpi (assoc-ref outputs "xpi"))
-                    (chromium (assoc-ref outputs "chromium")))
+              (let* ((id #$(assq-ref (package-properties this-package) 'id))
+                     (firefox (in-vicinity (assoc-ref outputs "firefox") id))
+                     (xpi (assoc-ref outputs "xpi"))
+                     (chromium (assoc-ref outputs "chromium")))
                 (install-file "dist/build/uBlock0.firefox.xpi"
                               (string-append xpi "/lib/mozilla/extensions"))
                 (copy-recursively "dist/build/uBlock0.firefox" firefox)
@@ -142,3 +146,6 @@  (define ublock-origin
 
 (define-public ublock-origin/chromium
   (make-chromium-extension ublock-origin "chromium"))
+
+(define-public ublock-origin/icecat
+  (make-icecat-extension ublock-origin "firefox"))
diff --git a/gnu/packages/gnuzilla.scm b/gnu/packages/gnuzilla.scm
index a367d7a00ff6..608219c97303 100644
--- a/gnu/packages/gnuzilla.scm
+++ b/gnu/packages/gnuzilla.scm
@@ -885,16 +885,13 @@  (define-public icecat-minimal
       #:phases
       #~(modify-phases %standard-phases
           (add-after 'unpack 'apply-guix-specific-patches
-            (lambda* (#:key inputs native-inputs #:allow-other-keys)
-              (let ((patch (search-input-file inputs "bin/patch")))
-                (for-each (match-lambda
-                            ((label . file)
-                             (when (and (string-prefix? "icecat-" label)
-                                        (string-suffix? ".patch" label))
-                               (format #t "applying '~a'...~%" file)
-                               (invoke patch "--force" "--no-backup-if-mismatch"
-                                       "-p1" "--input" file))))
-                          (or native-inputs inputs)))))
+            (lambda _
+              (for-each
+               (lambda (file) (invoke "patch" "--force" "-p1" "-i" file))
+               '(#$(local-file
+                    (search-patch "icecat-compare-paths.patch"))
+                 #$(local-file
+                    (search-patch "icecat-use-guix-extensions.patch"))))))
           (add-after 'apply-guix-specific-patches 'remove-bundled-libraries
             (lambda _
               ;; Remove bundled libraries that we don't use, since they may
@@ -1138,6 +1135,11 @@  (define-public icecat-minimal
                  '("default16.png" "default22.png" "default24.png"
                    "default32.png" "default48.png" "content/icon64.png"
                    "mozicon128.png" "default256.png"))))))))
+    (native-search-paths
+     (list (search-path-specification
+            (variable "ICECAT_EXTENSION_DIR")
+            (separator #f)              ;single entry
+            (files '("share/icecat/extensions")))))
     (home-page "https://www.gnu.org/software/gnuzilla/")
     (synopsis "Entirely free browser derived from Mozilla Firefox")
     (description
diff --git a/gnu/packages/patches/icecat-compare-paths.patch b/gnu/packages/patches/icecat-compare-paths.patch
new file mode 100644
index 000000000000..9205899dc0c4
--- /dev/null
+++ b/gnu/packages/patches/icecat-compare-paths.patch
@@ -0,0 +1,19 @@ 
+--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
++++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+@@ -3452,6 +3452,7 @@ const XPIDatabaseReconcile = {
+     if (
+       newAddon ||
+       oldAddon.updateDate != xpiState.mtime ||
++      oldAddon.path != xpiState.path ||
+       (aUpdateCompatibility && this.isAppBundledLocation(installLocation))
+     ) {
+       newAddon = this.updateMetadata(
+@@ -3460,8 +3461,6 @@ const XPIDatabaseReconcile = {
+         xpiState,
+         newAddon
+       );
+-    } else if (oldAddon.path != xpiState.path) {
+-      newAddon = this.updatePath(installLocation, oldAddon, xpiState);
+     } else if (aUpdateCompatibility || aSchemaChange) {
+       newAddon = this.updateCompatibility(
+         installLocation,
diff --git a/gnu/packages/patches/icecat-use-guix-extensions.patch b/gnu/packages/patches/icecat-use-guix-extensions.patch
new file mode 100644
index 000000000000..f2fe4260c2cd
--- /dev/null
+++ b/gnu/packages/patches/icecat-use-guix-extensions.patch
@@ -0,0 +1,52 @@ 
+--- a/toolkit/xre/nsXREDirProvider.cpp
++++ b/toolkit/xre/nsXREDirProvider.cpp
+@@ -415,13 +415,7 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
+ #if defined(XP_UNIX) && !defined(XP_MACOSX)
+   else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
+ #  ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+-#    if defined(__OpenBSD__) || defined(__FreeBSD__)
+-    static const char* const sysLExtDir = "/usr/local/share/mozilla/extensions";
+-#    else
+-    static const char* const sysLExtDir = "/usr/share/mozilla/extensions";
+-#    endif
+-    rv = NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false,
+-                               getter_AddRefs(file));
++    rv = GetGuixExtensionDir(getter_AddRefs(file));
+ #  endif
+   }
+ #endif  // defined(XP_UNIX) && !defined(XP_MACOSX)
+@@ -1198,6 +1192,24 @@ nsresult nsXREDirProvider::SetUserDataProfileDirectory(nsCOMPtr<nsIFile>& aFile,
+   return NS_OK;
+ }
+ 
++nsresult nsXREDirProvider::GetGuixExtensionDir(nsIFile** aFile) {
++  nsresult rv;
++  nsCOMPtr<nsIFile> localDir;
++
++#if defined(XP_UNIX)
++  const char* extensionDir = getenv("ICECAT_EXTENSION_DIR");
++  if (!extensionDir || !*extensionDir) return NS_ERROR_FAILURE;
++
++  rv = NS_NewNativeLocalFile(nsDependentCString(extensionDir), true,
++                             getter_AddRefs(localDir));
++#else
++#  error "Don't know how to get product dir on your platform"
++#endif
++
++  NS_IF_ADDREF(*aFile = localDir);
++  return rv;
++}
++
+ nsresult nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile,
+                                                     bool aLocal) {
+   // Copied from nsAppFileLocationProvider (more or less)
+--- a/toolkit/xre/nsXREDirProvider.h
++++ b/toolkit/xre/nsXREDirProvider.h
+@@ -112,6 +112,7 @@ class nsXREDirProvider final : public nsIDirectoryServiceProvider2,
+  private:
+   nsresult GetFilesInternal(const char* aProperty,
+                             nsISimpleEnumerator** aResult);
++  static nsresult GetGuixExtensionDir(nsIFile** aFile);
+   static nsresult GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal);
+   static nsresult GetSysUserExtensionsDirectory(nsIFile** aFile);
+ #if defined(XP_UNIX) || defined(XP_MACOSX)