[PATCH 0/2] Install Chromium extensions with Guix!

  • Done
  • quality assurance status badge
Details
2 participants
  • Leo Famulari
  • Marius Bakke
Owner
unassigned
Submitted by
Marius Bakke
Severity
normal
M
M
Marius Bakke wrote on 30 Oct 2020 19:29
(address . guix-patches@gnu.org)
20201030182925.5151-1-marius@gnu.org
Greetings Guix,

These patches lay the groundwork for packaging Chromium extensions with
Guix. Currently only uBlock Origin has been packaged, but adding others
should be technically straight forward. Practically it turns out
somewhat difficult because many extensions rely on the node.js ecosystem.

Patch 1/2 adds a search path mechanism to ungoogled-chromium such that
it looks for local extensions in $CHROMIUM_EXTENSION_DIRECTORY instead
of the default "/usr/share/chromium/extensions".

Patch 2/2 adds a generic MAKE-CHROMIUM-EXTENSION procedure that turns
a <package> object containing an uncompressed extension directory into
a signed .crx file visible to Chromium through a crafted JSON file.

There is one problem with the proposed patches: extensions are not
automatically updated. That is, updating the "ublock-origin-chromium"
package will not update the browser profile even after a browser
restart. Worse, uninstalling the extension through Chromium will leave
you no way to install it again even after it has been updated.

Not sure if that should be a blocking issue.

I'm also not sure whether (gnu packages browser-extensions) is a good
home for the helper procedures that create signing key, etc. Thoughts?

To aid testing I have started a build of the patched ungoogled-chromium
on ci.guix.gnu.org[*]. It should be finished by tomorrow(!).

(testers can use "chromium --user-data-dir=/tmp/foo" to avoid messing
with their regular profile)

I'm currently relocating to a new city and won't be able to follow up
on this until next week. In the meantime, feedback very welcome!

[*] on commit eee35fe3b7cb21792b90ecbf5a834b5a3fdb6f04.

Marius Bakke (2):
gnu: ungoogled-chromium: Add search path for installed extensions.
gnu: Add ublock-origin-chromium.

gnu/local.mk | 2 +
gnu/packages/browser-extensions.scm | 274 ++++++++++++++++++
gnu/packages/chromium.scm | 18 +-
.../ungoogled-chromium-search-path.patch | 28 ++
4 files changed, 314 insertions(+), 8 deletions(-)
create mode 100644 gnu/packages/browser-extensions.scm
create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

--
2.28.0
M
M
Marius Bakke wrote on 30 Oct 2020 19:32
[PATCH 1/2] gnu: ungoogled-chromium: Add search path for installed extensions.
(address . 44335@debbugs.gnu.org)
20201030183232.5497-1-marius@gnu.org
* gnu/packages/patches/ungoogled-chromium-search-path.patch: New file.
* gnu/local.mk (dist_patch_DATA): Adjust accordingly.
* gnu/packages/chromium.scm (%guix-patches): New variable.
(ungoogled-chromium-snippet): Apply %GUIX-PATCHES.
(ungoogled-chromium)[arguments]: Don't hard-code extensions directory.
[native-search-paths]: New field.
---
gnu/local.mk | 1 +
gnu/packages/chromium.scm | 18 ++++++------
.../ungoogled-chromium-search-path.patch | 28 +++++++++++++++++++
3 files changed, 39 insertions(+), 8 deletions(-)
create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

Toggle diff (98 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 3332f10df8..4643d05049 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1641,6 +1641,7 @@ dist_patch_DATA = \
%D%/packages/patches/u-boot-riscv64-fix-extlinux.patch \
%D%/packages/patches/ucx-tcp-iface-ioctl.patch \
%D%/packages/patches/udiskie-no-appindicator.patch \
+ %D%/packages/patches/ungoogled-chromium-search-path.patch \
%D%/packages/patches/ungoogled-chromium-system-nspr.patch \
%D%/packages/patches/unison-fix-ocaml-4.08.patch \
%D%/packages/patches/unknown-horizons-python-3.8-distro.patch \
diff --git a/gnu/packages/chromium.scm b/gnu/packages/chromium.scm
index f32a779805..06c57acd28 100644
--- a/gnu/packages/chromium.scm
+++ b/gnu/packages/chromium.scm
@@ -334,6 +334,10 @@
(base32
"18p9a7qffmy8m03nqva7maalgil13lj2mn0s56v3crbs4wk4lalj"))))
+(define %guix-patches
+ (list (local-file (search-patch "ungoogled-chromium-system-nspr.patch"))
+ (local-file (search-patch "ungoogled-chromium-search-path.patch"))))
+
;; This is a source 'snippet' that does the following:
;; *) Applies various patches for unbundling purposes and libstdc++ compatibility.
;; *) Runs the ungoogled patch-, domain substitution-, and scrubbing scripts.
@@ -356,9 +360,7 @@
(invoke "patch" "-p1" "--force" "--input"
patch "--no-backup-if-mismatch"))
(append '#+%debian-patches '#+%arch-patches
- '#+(list (local-file
- (search-patch
- "ungoogled-chromium-system-nspr.patch")))))
+ '#+%guix-patches))
(with-directory-excursion #+%ungoogled-origin
(format #t "Ungooglifying...~%")
@@ -571,11 +573,6 @@
(find-files (string-append "third_party/webrtc/modules"
"/audio_coding/codecs/opus")))
- (substitute* "chrome/common/chrome_paths.cc"
- (("/usr/share/chromium/extensions")
- ;; TODO: Add ~/.guix-profile.
- "/run/current-system/profile/share/chromium/extensions"))
-
;; Many files try to include ICU headers from "third_party/icu/...".
;; Remove the "third_party/" prefix to use system headers instead.
(substitute* (find-files "chrome" "\\.cc$")
@@ -840,6 +837,11 @@
("udev" ,eudev)
("valgrind" ,valgrind)
("vulkan-headers" ,vulkan-headers)))
+ (native-search-paths
+ (list (search-path-specification
+ (variable "CHROMIUM_EXTENSION_DIRECTORY")
+ (separator #f) ;single entry
+ (files '("share/chromium/extensions")))))
;; Building Chromium takes ... a very long time. On a single core, a busy
;; mid-end x86 system may need more than 24 hours to complete the build.
diff --git a/gnu/packages/patches/ungoogled-chromium-search-path.patch b/gnu/packages/patches/ungoogled-chromium-search-path.patch
new file mode 100644
index 0000000000..5ef9c56b52
--- /dev/null
+++ b/gnu/packages/patches/ungoogled-chromium-search-path.patch
@@ -0,0 +1,28 @@
+Look for system-installed extensions in CHROMIUM_EXTENSION_DIRECTORY instead
+of the hard-coded /usr/share/chromium/extensions.
+
+--- a/chrome/common/chrome_paths.cc
++++ b/chrome/common/chrome_paths.cc
+@@ -4,6 +4,7 @@
+
+ #include "chrome/common/chrome_paths.h"
+
++#include "base/environment.h"
+ #include "base/files/file_util.h"
+ #include "base/logging.h"
+ #include "base/native_library.h"
+@@ -511,7 +512,13 @@
+ #endif
+ #if defined(OS_LINUX) || defined(OS_CHROMEOS)
+ case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: {
+- cur = base::FilePath(kFilepathSinglePrefExtensions);
++ std::unique_ptr<base::Environment> environment(base::Environment::Create());
++ std::string extension_dir;
++ if (environment->GetVar("CHROMIUM_EXTENSION_DIRECTORY", &extension_dir)) {
++ cur = base::FilePath(extension_dir);
++ } else {
++ cur = base::FilePath(kFilepathSinglePrefExtensions);
++ }
+ break;
+ }
+ #endif
--
2.28.0
M
M
Marius Bakke wrote on 30 Oct 2020 19:32
[PATCH 2/2] gnu: Add ublock-origin-chromium.
(address . 44335@debbugs.gnu.org)
20201030183232.5497-2-marius@gnu.org
* gnu/packages/browser-extensions.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.
---
gnu/local.mk | 1 +
gnu/packages/browser-extensions.scm | 278 ++++++++++++++++++++++++++++
2 files changed, 279 insertions(+)
create mode 100644 gnu/packages/browser-extensions.scm

Toggle diff (298 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index 4643d05049..f97a0cda78 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -111,6 +111,7 @@ GNU_SYSTEM_MODULES = \
%D%/packages/boost.scm \
%D%/packages/bootloaders.scm \
%D%/packages/bootstrap.scm \
+ %D%/packages/browser-extensions.scm \
%D%/packages/build-tools.scm \
%D%/packages/busybox.scm \
%D%/packages/c.scm \
diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
new file mode 100644
index 0000000000..b6d160bee7
--- /dev/null
+++ b/gnu/packages/browser-extensions.scm
@@ -0,0 +1,278 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.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 packages browser-extensions)
+ #:use-module (gcrypt base16)
+ #:use-module ((gcrypt hash) #:prefix hash:)
+ #:use-module (ice-9 iconv)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages check)
+ #:use-module (gnu packages chromium)
+ #:use-module (gnu packages gnupg)
+ #:use-module (gnu packages tls)
+ #:use-module (gnu packages xorg)
+ #:use-module (guix build-system trivial)
+
+ ;; Imports for extension packages.
+ #:use-module (guix git-download)
+ #:use-module (guix build-system gnu)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages python)
+
+ #:export (make-chromium-extension))
+
+;; Chromium requires that each package is signed with a unique key, from which
+;; the "extension ID" derives. Currently the package name is used as seed.
+(define (make-signing-key seed)
+ "Return a derivation for a deterministic PKCS #8 private key using SEED."
+
+ (define sha256sum
+ (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
+
+ ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+ (define size 2048)
+ (define normalized-seed (string-take sha256sum 56))
+
+ (computed-file (string-append seed "-signing-key.pem")
+ #~(system* #$(file-append gnutls "/bin/certtool")
+ "--generate-privkey"
+ "--key-type=rsa"
+ "--pkcs8"
+ ;; Use the provable FIPS-PUB186-4 algorithm for
+ ;; deterministic results.
+ "--provable"
+ "--password="
+ "--no-text"
+ (string-append "--bits=" #$(number->string size))
+ (string-append "--seed=" #$normalized-seed)
+ "--outfile" #$output)))
+
+(define* (make-crx package #:optional (chromium-output "out"))
+ ;; XXX: This is terrible. We pull Xorg and Chromium just to pack and
+ ;; sign an extension. This should be implemented with something lighter
+ ;; using the protobuf-based protocol (TODO: where is it documented..?).
+ (define name (package-name package))
+ (define version (package-version package))
+ (define signing-key (make-signing-key name))
+
+ (with-imported-modules '((guix build utils))
+ (computed-file
+ (string-append name "-" version ".crx")
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 ftw)
+ (srfi srfi-26))
+ (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
+ (xvfb #$(file-append xorg-server "/bin/Xvfb"))
+ (packdir (string-append "/tmp/extension/" #$name)))
+ (mkdir-p (dirname packdir))
+ (copy-recursively (ungexp package chromium-output) packdir)
+ (system (string-append xvfb " :1 &"))
+ (setenv "DISPLAY" ":1")
+ (sleep 2) ;give Xorg some time to initialize...
+ ;; Chromium stores the current time in the .crx Zip archive.
+ ;; Sidestep that by using "faketime" for determinism.
+ ;; FIXME (core-updates): faketime is missing an absolute reference
+ ;; to 'date', hence the need to set PATH.
+ (setenv "PATH" #$(file-append coreutils "/bin"))
+ (invoke #$(file-append libfaketime "/bin/faketime")
+ "2000-01-01 00:00:00"
+ chromium
+ "--user-data-dir=/tmp/signing-profile"
+ (string-append "--pack-extension=" packdir)
+ (string-append "--pack-extension-key=" #$signing-key))
+ (copy-file (string-append packdir ".crx") #$output))))))
+
+(define* (package->chromium-json package #:optional (chromium-output "out"))
+ "Return a derivation that creates a JSON file for PACKAGE, meant to be
+consumed by Chromium to make the installed extension visible."
+
+ (define name (package-name package))
+ (define version (package-version package))
+ (define crx (make-crx package chromium-output))
+
+ (computed-file (string-append name "-" version ".json")
+ #~(call-with-output-file #$output
+ (lambda (port)
+ ;; Documentation for the JSON format can be found in
+ ;; extensions/common/extension.h and
+ ;; chrome/browser/extensions/external_provider_impl.cc.
+ (format port "
+{
+ \"external_crx\": \"~a\",
+ \"external_version\": \"~a\"
+}
+"
+ #$crx #$version)))))
+
+
+(define (key->der key)
+ "Return a derivation for a file representation of KEY in DER format."
+ (computed-file "der"
+ #~(system* #$(file-append gnutls "/bin/certtool")
+ "--load-privkey" #$key
+ "--pubkey-info"
+ "--outfile" #$output
+ "--outder")))
+
+(define (gexp-sha256sum file)
+ "Return a derivation for a base16 representation of the SHA256 checksum
+of FILE."
+ (with-extensions (list guile-gcrypt)
+ #~(begin
+ (use-modules (gcrypt base16) (gcrypt hash))
+ (format #f (bytevector->base16-string (file-sha256 #$file))))))
+
+(define (chromium-json->profile-object json signing-key)
+ "Return a derivation that installs JSON to the destination searched by
+Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
+ (define der (key->der signing-key))
+ (define checksum (gexp-sha256sum der))
+
+ (with-imported-modules '((guix build utils))
+ (computed-file
+ "chromium-extension"
+ #~(begin
+ (use-modules (guix build utils))
+ (define (base16-string->chromium-base16 str)
+ ;; Translate STR, a hexadecimal string, to a Chromium-style
+ ;; representation using the letters a-p (where a=0, p=15).
+ (define s1 "0123456789abcdef")
+ (define s2 "abcdefghijklmnop")
+ (let loop ((chars (string->list str))
+ (converted '()))
+ (if (null? chars)
+ (list->string (reverse converted))
+ (loop (cdr chars) (cons (string-ref
+ s2 (string-index s1 (car chars)))
+ converted)))))
+
+ (let ((file-name (base16-string->chromium-base16
+ (string-take #$checksum 32)))
+ (extension-directory (string-append #$output
+ "/share/chromium/extensions")))
+ (mkdir-p extension-directory)
+ (symlink #$json (string-append extension-directory "/"
+ file-name ".json")))))))
+
+(define* (make-chromium-extension p #:optional (output "out"))
+ "Create a signed \".crx\" file from package P, an unpacked Chromium
+extension, and return a derivation for a package that makes the packed
+extension visible to Chromium when installed to a Guix profile.
+
+OUTPUT can be used to specify which output of P contains the Chromium
+extension directory."
+ (package
+ (inherit p)
+ (name (string-append (package-name p) "-chromium"))
+ (build-system trivial-build-system)
+ (native-inputs '())
+ (inputs
+ `(("extension" ,(chromium-json->profile-object
+ (package->chromium-json p output)
+ (make-signing-key (package-name p))))))
+ (propagated-inputs '())
+ (outputs '("out"))
+ (arguments
+ '(#:modules ((guix build utils))
+ #:builder
+ (begin
+ (use-modules (guix build utils))
+ (copy-recursively (assoc-ref %build-inputs "extension")
+ (assoc-ref %outputs "out")))))))
+
+
+
+(define uassets
+ (let ((commit "95d609786680bf4cf267b1dff3f429af88040da1"))
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/uBlockOrigin/uAssets.git")
+ (commit commit)))
+ (file-name (git-file-name "uAssets" (string-take commit 9)))
+ (sha256
+ (base32
+ "070v2g0hyxvqv77b6z404hdlggl0sq2bfhsfl4a84k93p9pwwlrq")))))
+
+(define ublock-origin
+ (package
+ (name "ublock-origin")
+ (version "1.30.8")
+ (home-page "https://github.com/gorhill/uBlock")
+ (source (origin
+ (method git-fetch)
+ (uri (git-reference (url home-page) (commit version)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "1y4dq4p4j4qrw6gfdcbpyigqjinqq3jm8r5x9h5ariiv9s8856w7"))))
+ (build-system gnu-build-system)
+ ;; XXX: The xpi and firefox outputs are currently unused, but included
+ ;; for easy testing.
+ (outputs '("xpi" "firefox" "chromium"))
+ (arguments
+ '(#:tests? #f ;no tests
+ #:allowed-references ()
+ #:phases
+ (modify-phases (map (lambda (phase)
+ (assq phase %standard-phases))
+ '(set-paths unpack patch-source-shebangs))
+ (add-after 'unpack 'link-uassets
+ (lambda* (#:key native-inputs inputs #:allow-other-keys)
+ (symlink (string-append (assoc-ref (or native-inputs inputs)
+ "uassets"))
+ "../uAssets")
+ #t))
+ (add-after 'unpack 'make-files-writable
+ (lambda _
+ ;; The build system copies some files and later tries
+ ;; modifying them.
+ (for-each make-file-writable (find-files "."))
+ #true))
+ (add-after 'patch-source-shebangs 'build-xpi
+ (lambda _
+ (invoke "./tools/make-firefox.sh" "all")))
+ (add-after 'build-xpi 'build-chromium
+ (lambda _
+ (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")))
+ (install-file "dist/build/uBlock0.firefox.xpi"
+ (string-append xpi "/lib/mozilla/extensions"))
+ (copy-recursively "dist/build/uBlock0.firefox" firefox)
+ (copy-recursively "dist/build/uBlock0.chromium" chromium)
+ #true))))))
+ (native-inputs
+ `(("python" ,python-wrapper)
+ ("uassets" ,uassets)
+ ("zip" ,zip)))
+ (synopsis "Block unwanted content from web sites")
+ (description
+ "uBlock Origin is a @dfn{wide spectrum blocker} for IceCat and
+ungoogled-chromium.")
+ (license license:gpl3+)))
+
+(define-public ublock-origin/chromium
+ (make-chromium-extension ublock-origin "chromium"))
--
2.28.0
M
M
Marius Bakke wrote on 2 Nov 2020 01:22
[PATCH v2 0/3] Install Chromium extensions with Guix!
(address . 44335@debbugs.gnu.org)
20201102002228.5971-1-marius@gnu.org
Hello,

Here is an updated patch series that introduces a new module to deal
with the Chromium extension machinery.

The procedures have also been tweaked a little to reduce duplication.

I still don't know why extensions installed with Guix are not updated.
It could be that the update functionality has been patched out by
ungoogled-chromium. More testing pending.

Marius Bakke (3):
gnu: ungoogled-chromium: Add search path for installed extensions.
Add (gnu build chromium-extension).
gnu: Add ublock-origin-chromium.

gnu/build/chromium-extension.scm | 192 ++++++++++++++++++
gnu/local.mk | 3 +
gnu/packages/browser-extensions.scm | 100 +++++++++
gnu/packages/chromium.scm | 18 +-
.../ungoogled-chromium-search-path.patch | 28 +++
5 files changed, 333 insertions(+), 8 deletions(-)
create mode 100644 gnu/build/chromium-extension.scm
create mode 100644 gnu/packages/browser-extensions.scm
create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

--
2.28.0
M
M
Marius Bakke wrote on 2 Nov 2020 01:22
[PATCH v2 1/3] gnu: ungoogled-chromium: Add search path for installed extensions.
(address . 44335@debbugs.gnu.org)
20201102002228.5971-2-marius@gnu.org
* gnu/packages/patches/ungoogled-chromium-search-path.patch: New file.
* gnu/local.mk (dist_patch_DATA): Adjust accordingly.
* gnu/packages/chromium.scm (%guix-patches): New variable.
(ungoogled-chromium-snippet): Apply %GUIX-PATCHES.
(ungoogled-chromium)[arguments]: Don't hard-code extensions directory.
[native-search-paths]: New field.
---
gnu/local.mk | 1 +
gnu/packages/chromium.scm | 18 ++++++------
.../ungoogled-chromium-search-path.patch | 28 +++++++++++++++++++
3 files changed, 39 insertions(+), 8 deletions(-)
create mode 100644 gnu/packages/patches/ungoogled-chromium-search-path.patch

Toggle diff (98 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index d4d04c01b8..51550e80cb 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1642,6 +1642,7 @@ dist_patch_DATA = \
%D%/packages/patches/u-boot-riscv64-fix-extlinux.patch \
%D%/packages/patches/ucx-tcp-iface-ioctl.patch \
%D%/packages/patches/udiskie-no-appindicator.patch \
+ %D%/packages/patches/ungoogled-chromium-search-path.patch \
%D%/packages/patches/ungoogled-chromium-system-nspr.patch \
%D%/packages/patches/unison-fix-ocaml-4.08.patch \
%D%/packages/patches/unknown-horizons-python-3.8-distro.patch \
diff --git a/gnu/packages/chromium.scm b/gnu/packages/chromium.scm
index f32a779805..06c57acd28 100644
--- a/gnu/packages/chromium.scm
+++ b/gnu/packages/chromium.scm
@@ -334,6 +334,10 @@
(base32
"18p9a7qffmy8m03nqva7maalgil13lj2mn0s56v3crbs4wk4lalj"))))
+(define %guix-patches
+ (list (local-file (search-patch "ungoogled-chromium-system-nspr.patch"))
+ (local-file (search-patch "ungoogled-chromium-search-path.patch"))))
+
;; This is a source 'snippet' that does the following:
;; *) Applies various patches for unbundling purposes and libstdc++ compatibility.
;; *) Runs the ungoogled patch-, domain substitution-, and scrubbing scripts.
@@ -356,9 +360,7 @@
(invoke "patch" "-p1" "--force" "--input"
patch "--no-backup-if-mismatch"))
(append '#+%debian-patches '#+%arch-patches
- '#+(list (local-file
- (search-patch
- "ungoogled-chromium-system-nspr.patch")))))
+ '#+%guix-patches))
(with-directory-excursion #+%ungoogled-origin
(format #t "Ungooglifying...~%")
@@ -571,11 +573,6 @@
(find-files (string-append "third_party/webrtc/modules"
"/audio_coding/codecs/opus")))
- (substitute* "chrome/common/chrome_paths.cc"
- (("/usr/share/chromium/extensions")
- ;; TODO: Add ~/.guix-profile.
- "/run/current-system/profile/share/chromium/extensions"))
-
;; Many files try to include ICU headers from "third_party/icu/...".
;; Remove the "third_party/" prefix to use system headers instead.
(substitute* (find-files "chrome" "\\.cc$")
@@ -840,6 +837,11 @@
("udev" ,eudev)
("valgrind" ,valgrind)
("vulkan-headers" ,vulkan-headers)))
+ (native-search-paths
+ (list (search-path-specification
+ (variable "CHROMIUM_EXTENSION_DIRECTORY")
+ (separator #f) ;single entry
+ (files '("share/chromium/extensions")))))
;; Building Chromium takes ... a very long time. On a single core, a busy
;; mid-end x86 system may need more than 24 hours to complete the build.
diff --git a/gnu/packages/patches/ungoogled-chromium-search-path.patch b/gnu/packages/patches/ungoogled-chromium-search-path.patch
new file mode 100644
index 0000000000..5ef9c56b52
--- /dev/null
+++ b/gnu/packages/patches/ungoogled-chromium-search-path.patch
@@ -0,0 +1,28 @@
+Look for system-installed extensions in CHROMIUM_EXTENSION_DIRECTORY instead
+of the hard-coded /usr/share/chromium/extensions.
+
+--- a/chrome/common/chrome_paths.cc
++++ b/chrome/common/chrome_paths.cc
+@@ -4,6 +4,7 @@
+
+ #include "chrome/common/chrome_paths.h"
+
++#include "base/environment.h"
+ #include "base/files/file_util.h"
+ #include "base/logging.h"
+ #include "base/native_library.h"
+@@ -511,7 +512,13 @@
+ #endif
+ #if defined(OS_LINUX) || defined(OS_CHROMEOS)
+ case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: {
+- cur = base::FilePath(kFilepathSinglePrefExtensions);
++ std::unique_ptr<base::Environment> environment(base::Environment::Create());
++ std::string extension_dir;
++ if (environment->GetVar("CHROMIUM_EXTENSION_DIRECTORY", &extension_dir)) {
++ cur = base::FilePath(extension_dir);
++ } else {
++ cur = base::FilePath(kFilepathSinglePrefExtensions);
++ }
+ break;
+ }
+ #endif
--
2.28.0
M
M
Marius Bakke wrote on 2 Nov 2020 01:22
[PATCH v2 2/3] Add (gnu build chromium-extension).
(address . 44335@debbugs.gnu.org)
20201102002228.5971-3-marius@gnu.org
* gnu/build/chromium-extension.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.
---
gnu/build/chromium-extension.scm | 192 +++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
2 files changed, 193 insertions(+)
create mode 100644 gnu/build/chromium-extension.scm

Toggle diff (212 lines)
diff --git a/gnu/build/chromium-extension.scm b/gnu/build/chromium-extension.scm
new file mode 100644
index 0000000000..35f49d788c
--- /dev/null
+++ b/gnu/build/chromium-extension.scm
@@ -0,0 +1,192 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.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 chromium-extension)
+ #:use-module (gcrypt base16)
+ #:use-module ((gcrypt hash) #:prefix hash:)
+ #:use-module (ice-9 iconv)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (gnu packages base)
+ #:use-module (gnu packages check)
+ #:use-module (gnu packages chromium)
+ #:use-module (gnu packages gnupg)
+ #:use-module (gnu packages tls)
+ #:use-module (gnu packages xorg)
+ #:use-module (guix build-system trivial)
+ #:export (make-chromium-extension))
+
+;;; Commentary:
+;;;
+;;; Tools to deal with Chromium extensions.
+;;;
+;;; Code:
+
+(define (make-signing-key seed)
+ "Return a derivation for a deterministic PKCS #8 private key using SEED."
+
+ (define sha256sum
+ (bytevector->base16-string (hash:sha256 (string->bytevector seed "UTF-8"))))
+
+ ;; certtool.c wants a 56 byte seed for a 2048 bit key.
+ (define size 2048)
+ (define normalized-seed (string-take sha256sum 56))
+
+ (computed-file (string-append seed "-signing-key.pem")
+ #~(system* #$(file-append gnutls "/bin/certtool")
+ "--generate-privkey"
+ "--key-type=rsa"
+ "--pkcs8"
+ ;; Use the provable FIPS-PUB186-4 algorithm for
+ ;; deterministic results.
+ "--provable"
+ "--password="
+ "--no-text"
+ (string-append "--bits=" #$(number->string size))
+ (string-append "--seed=" #$normalized-seed)
+ "--outfile" #$output)
+ #:local-build? #t))
+
+(define* (make-crx package+output signing-key)
+ "Create a signed \".crx\" file from the unpacked Chromium extension residing
+in PACKAGE+OUTPUT, a (list package output) pair. The extension will be signed
+with SIGNING-KEY."
+ (with-imported-modules '((guix build utils))
+ (computed-file
+ "chromium-extension.crx"
+ #~(begin
+ ;; This is not great. We pull Xorg and Chromium just to Zip and
+ ;; sign an extension. This should be implemented with something
+ ;; lighter. (TODO: where is the documentation..?)
+ (use-modules (guix build utils))
+ (let ((chromium #$(file-append ungoogled-chromium "/bin/chromium"))
+ (xvfb #$(file-append xorg-server "/bin/Xvfb"))
+ (packdir "/tmp/extension"))
+ (mkdir-p (dirname packdir))
+ (copy-recursively (ungexp (car package+output) (cadr package+output))
+ packdir)
+ (system (string-append xvfb " :1 &"))
+ (setenv "DISPLAY" ":1")
+ (sleep 2) ;give Xorg some time to initialize...
+ ;; Chromium stores the current time in the .crx Zip archive.
+ ;; Sidestep that by using "faketime" for determinism.
+ ;; FIXME (core-updates): faketime is missing an absolute reference
+ ;; to 'date', hence the need to set PATH.
+ (setenv "PATH" #$(file-append coreutils "/bin"))
+ (invoke #$(file-append libfaketime "/bin/faketime")
+ "2000-01-01 00:00:00"
+ chromium
+ "--user-data-dir=/tmp/signing-profile"
+ (string-append "--pack-extension=" packdir)
+ (string-append "--pack-extension-key=" #$signing-key))
+ (copy-file (string-append packdir ".crx") #$output)))
+ #:local-build? #t)))
+
+(define* (crx->chromium-json crx version)
+ "Return a derivation that creates a Chromium JSON settings file for the
+unpacked extension residing in output PACKAGE-OUTPUT of PACKAGE."
+ (computed-file "extension.json"
+ #~(call-with-output-file #$output
+ (lambda (port)
+ ;; Documentation for the JSON format can be found in
+ ;; extensions/common/extension.h and
+ ;; chrome/browser/extensions/external_provider_impl.cc.
+ (format port "
+{
+ \"external_crx\": \"~a\",
+ \"external_version\": \"~a\"
+}
+"
+ #$crx #$version)))
+ #:local-build? #t))
+
+
+(define (signing-key->public-der key)
+ "Return a derivation for a file containing the public key of KEY in DER
+format."
+ (computed-file "der"
+ #~(system* #$(file-append gnutls "/bin/certtool")
+ "--load-privkey" #$key
+ "--pubkey-info"
+ "--outfile" #$output
+ "--outder")
+ #:local-build? #t))
+
+(define (chromium-json->profile-object json signing-key)
+ "Return a derivation that installs JSON to the directory searched by
+Chromium, using a file name (aka extension ID) derived from SIGNING-KEY."
+ (define der (signing-key->public-der signing-key))
+
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules '((guix build utils))
+ (computed-file
+ "chromium-extension.json"
+ #~(begin
+ (use-modules (guix build utils)
+ (gcrypt base16)
+ (gcrypt hash))
+ (define (base16-string->chromium-base16 str)
+ ;; Translate STR, a hexadecimal string, to a Chromium-style
+ ;; representation using the letters a-p (where a=0, p=15).
+ (define s1 "0123456789abcdef")
+ (define s2 "abcdefghijklmnop")
+ (let loop ((chars (string->list str))
+ (converted '()))
+ (if (null? chars)
+ (list->string (reverse converted))
+ (loop (cdr chars) (cons (string-ref
+ s2 (string-index s1 (car chars)))
+ converted)))))
+
+ (let* ((checksum (bytevector->base16-string (file-sha256 #$der)))
+ (file-name (base16-string->chromium-base16
+ (string-take checksum 32)))
+ (extension-directory (string-append #$output
+ "/share/chromium/extensions")))
+ (mkdir-p extension-directory)
+ (symlink #$json (string-append extension-directory "/"
+ file-name ".json"))))
+ #:local-build? #t))))
+
+(define* (make-chromium-extension p #:optional (output "out"))
+ "Create a Chromium extension from package P and return a package that,
+when installed, will make the extension contained in P available as a
+Chromium browser extension. OUTPUT specifies which output of P to use."
+ (let* ((pname (package-name p))
+ (version (package-version p))
+ (signing-key (make-signing-key pname)))
+ (package
+ (inherit p)
+ (name (string-append pname "-chromium"))
+ (build-system trivial-build-system)
+ (native-inputs '())
+ (inputs
+ `(("extension" ,(chromium-json->profile-object
+ (crx->chromium-json (make-crx (list p output)
+ signing-key)
+ version)
+ signing-key))))
+ (propagated-inputs '())
+ (outputs '("out"))
+ (arguments
+ '(#:modules ((guix build utils))
+ #:builder
+ (begin
+ (use-modules (guix build utils))
+ (copy-recursively (assoc-ref %build-inputs "extension")
+ (assoc-ref %outputs "out"))))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 51550e80cb..e847f8c16a 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -657,6 +657,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/accounts.scm \
%D%/build/activation.scm \
%D%/build/bootloader.scm \
+ %D%/build/chromium-extension.scm \
%D%/build/cross-toolchain.scm \
%D%/build/image.scm \
%D%/build/file-systems.scm \
--
2.28.0
M
M
Marius Bakke wrote on 2 Nov 2020 01:22
[PATCH v2 3/3] gnu: Add ublock-origin-chromium.
(address . 44335@debbugs.gnu.org)
20201102002228.5971-4-marius@gnu.org
* gnu/packages/browser-extensions.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
---
gnu/local.mk | 1 +
gnu/packages/browser-extensions.scm | 100 ++++++++++++++++++++++++++++
2 files changed, 101 insertions(+)
create mode 100644 gnu/packages/browser-extensions.scm

Toggle diff (120 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index e847f8c16a..c14612cdef 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -111,6 +111,7 @@ GNU_SYSTEM_MODULES = \
%D%/packages/boost.scm \
%D%/packages/bootloaders.scm \
%D%/packages/bootstrap.scm \
+ %D%/packages/browser-extensions.scm \
%D%/packages/build-tools.scm \
%D%/packages/busybox.scm \
%D%/packages/c.scm \
diff --git a/gnu/packages/browser-extensions.scm b/gnu/packages/browser-extensions.scm
new file mode 100644
index 0000000000..064a916e73
--- /dev/null
+++ b/gnu/packages/browser-extensions.scm
@@ -0,0 +1,100 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Marius Bakke <marius@gnu.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 packages browser-extensions)
+ #:use-module (guix packages)
+ #:use-module (guix git-download)
+ #:use-module (guix build-system gnu)
+ #:use-module ((guix licenses) #:prefix license:)
+ #:use-module (gnu build chromium-extension)
+ #:use-module (gnu packages compression)
+ #:use-module (gnu packages python))
+
+(define uassets
+ (let ((commit "95d609786680bf4cf267b1dff3f429af88040da1"))
+ (origin
+ (method git-fetch)
+ (uri (git-reference
+ (url "https://github.com/uBlockOrigin/uAssets.git")
+ (commit commit)))
+ (file-name (git-file-name "uAssets" (string-take commit 9)))
+ (sha256
+ (base32
+ "070v2g0hyxvqv77b6z404hdlggl0sq2bfhsfl4a84k93p9pwwlrq")))))
+
+(define ublock-origin
+ (package
+ (name "ublock-origin")
+ (version "1.30.8")
+ (home-page "https://github.com/gorhill/uBlock")
+ (source (origin
+ (method git-fetch)
+ (uri (git-reference (url home-page) (commit version)))
+ (file-name (git-file-name name version))
+ (sha256
+ (base32
+ "1y4dq4p4j4qrw6gfdcbpyigqjinqq3jm8r5x9h5ariiv9s8856w7"))))
+ (build-system gnu-build-system)
+ (outputs '("xpi" "firefox" "chromium"))
+ (arguments
+ '(#:tests? #f ;no tests
+ #:allowed-references ()
+ #:phases
+ (modify-phases (map (lambda (phase)
+ (assq phase %standard-phases))
+ '(set-paths unpack patch-source-shebangs))
+ (add-after 'unpack 'link-uassets
+ (lambda* (#:key native-inputs inputs #:allow-other-keys)
+ (symlink (string-append (assoc-ref (or native-inputs inputs)
+ "uassets"))
+ "../uAssets")
+ #t))
+ (add-after 'unpack 'make-files-writable
+ (lambda _
+ ;; The build system copies some files and later tries
+ ;; modifying them.
+ (for-each make-file-writable (find-files "."))
+ #true))
+ (add-after 'patch-source-shebangs 'build-xpi
+ (lambda _
+ (invoke "./tools/make-firefox.sh" "all")))
+ (add-after 'build-xpi 'build-chromium
+ (lambda _
+ (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")))
+ (install-file "dist/build/uBlock0.firefox.xpi"
+ (string-append xpi "/lib/mozilla/extensions"))
+ (copy-recursively "dist/build/uBlock0.firefox" firefox)
+ (copy-recursively "dist/build/uBlock0.chromium" chromium)
+ #true))))))
+ (native-inputs
+ `(("python" ,python-wrapper)
+ ("uassets" ,uassets)
+ ("zip" ,zip)))
+ (synopsis "Block unwanted content from web sites")
+ (description
+ "uBlock Origin is a @dfn{wide spectrum blocker} for IceCat and
+ungoogled-chromium.")
+ (license license:gpl3+)))
+
+(define-public ublock-origin/chromium
+ (make-chromium-extension ublock-origin "chromium"))
--
2.28.0
M
M
Marius Bakke wrote on 2 Nov 2020 01:28
Re: [bug#44335] [PATCH v2 2/3] Add (gnu build chromium-extension).
(address . 44335@debbugs.gnu.org)
87blggioun.fsf@gnu.org
Marius Bakke <marius@gnu.org> writes:

Toggle quote (3 lines)
> * gnu/build/chromium-extension.scm: New file.
> * gnu/local.mk (GNU_SYSTEM_MODULES): Adjust accordingly.

Errh, I accidentally sent an outdated patch. Here is the new one:
-----BEGIN PGP SIGNATURE-----

iQFDBAEBCgAtFiEEu7At3yzq9qgNHeZDoqBt8qM6VPoFAl+fUrEPHG1hcml1c0Bn
bnUub3JnAAoJEKKgbfKjOlT6jmsH/iqhAVpCud9R9tl3u877Igd8AEydrD0JaWev
jACxP3ycfliWLTU4WHV3lpS29KMctMYKbCwOk1PbOZQtsCj4XI0SBzCK0/VazwOE
cHhNZjTOJsYFFoH2ScmA0RXspqmJglgK965jcf8HlQBLSMAM6dfsODT5cF2NBr45
E+x37zM/AVV+C78C46zLPiyb6w9xDAcvGqylbsweG+TO4p164gLmRP4SkWDEl5Z6
LYhFUUfXL6+bEGmZpKa2J96oD2RGLR498awRJs4C1SNYN1P2xW+JN5OW44LmG83e
o8xpR+E3yCwbnkk7cJSMj6qlA89bpKYoqSyHILloQN9AuXEQIKw=
=uFks
-----END PGP SIGNATURE-----

L
L
Leo Famulari wrote on 2 Nov 2020 01:55
Re: [bug#44335] [PATCH v2 0/3] Install Chromium extensions with Guix!
(name . Marius Bakke)(address . marius@gnu.org)(address . 44335@debbugs.gnu.org)
20201102005551.GA9959@jasmine.lan
On Mon, Nov 02, 2020 at 01:22:25AM +0100, Marius Bakke wrote:
Toggle quote (11 lines)
> Hello,
>
> Here is an updated patch series that introduces a new module to deal
> with the Chromium extension machinery.
>
> The procedures have also been tweaked a little to reduce duplication.
>
> I still don't know why extensions installed with Guix are not updated.
> It could be that the update functionality has been patched out by
> ungoogled-chromium. More testing pending.

Really great work Marius! We trust your judgement on when the code is
"ready".
M
M
Marius Bakke wrote on 8 Nov 2020 18:38
(name . Leo Famulari)(address . leo@famulari.name)(address . 44335-done@debbugs.gnu.org)
87r1p3g35e.fsf@gnu.org
Leo Famulari <leo@famulari.name> writes:

Toggle quote (15 lines)
> On Mon, Nov 02, 2020 at 01:22:25AM +0100, Marius Bakke wrote:
>> Hello,
>>
>> Here is an updated patch series that introduces a new module to deal
>> with the Chromium extension machinery.
>>
>> The procedures have also been tweaked a little to reduce duplication.
>>
>> I still don't know why extensions installed with Guix are not updated.
>> It could be that the update functionality has been patched out by
>> ungoogled-chromium. More testing pending.
>
> Really great work Marius! We trust your judgement on when the code is
> "ready".

Thanks, it's in 'master' now! After further testing I found that
updating extensions does work, not sure what went wrong the first time
around.

Removing and reinstalling extensions is still "impossible" without
serious Chromium profile surgery. But let's deal with that later.

I also committed a bonus extension that can send YouTube and other
streaming sites directly to the Kodi media center.

Feedback still welcome. Enjoy the new add-on enchanced
ungoogled-chromium experience! :-)

Now to figure out how to do the same for IceCat...
-----BEGIN PGP SIGNATURE-----

iQFDBAEBCgAtFiEEu7At3yzq9qgNHeZDoqBt8qM6VPoFAl+oLQ0PHG1hcml1c0Bn
bnUub3JnAAoJEKKgbfKjOlT6de8IAJyLptkoiekwZk6LPz27MiDHQ467+4q5cu1Y
VctNPG1QWntYKWKrTrDN5nQUgLqCi5HJd1hzImgEfSZevPpRNCiPV+CM5fUygu7z
eDYwD99/ILCHvjajwxHob8InzkM6dH32PUokTtWloYbjBRy8wl7GBb4DjvWFCeZm
rSAqxtIF9Dd1y3VY4bdPX8JSSvv+yW3xufFIvsoVv24nUrsgdjRdRE+P5sf3a5w8
1G4qjOofOUdjpV+GbVP8Gx7zwaiuytX9dADM9M5CqAJbcw7Wyge7GfGGhPMwz5VW
x+3JOWxqrTFGef7RiOwVL+DRBUxnQxdZr2BRxtPquDM4WEeHDDg=
=0QqR
-----END PGP SIGNATURE-----

Closed
L
L
Leo Famulari wrote on 8 Nov 2020 18:46
(name . Marius Bakke)(address . marius@gnu.org)(address . 44335-done@debbugs.gnu.org)
20201108174607.GA20018@jasmine.lan
On Sun, Nov 08, 2020 at 06:38:21PM +0100, Marius Bakke wrote:
Toggle quote (4 lines)
> Thanks, it's in 'master' now! After further testing I found that
> updating extensions does work, not sure what went wrong the first time
> around.

Great!

Toggle quote (3 lines)
> I also committed a bonus extension that can send YouTube and other
> streaming sites directly to the Kodi media center.

Ah, very nice :)
Closed
?