I haven’t had time since originally creating this to work further on it. Meanwhile bugs with xwidget on pgtk have been smoothed out upstream and some minimal maintenance continues to happen there. Thus if you want a browser in emacs, I recommend building with xwidgets (which also supports macos). I don’t plan to continue any development on this for both technical and personal reasons, but I think it represents an interesting (albeit somewhat hacky) approach to pushing what is possible with emacs dynamic modules.
This package is still in the early stages of active development. While I am currently using it daily, it should be considered as still in an alpha stage. This means that the public interface can and likely will change! Also, due to the nature of dynamic modules dealing with memory directly, bugs can mean segfaults, memory leaks, and ultimately untimely crashes. So beware that data loss may result! However, if you do experience crashes, please report it!
Browsers are a big target of malware and as such ensuring you’re running the most up to date version that receives security patches is essential. It is up to you to ensure you are running an up to date version of webkitgtk2. Note that not every distro consistently and quickly updates webkitgtk2 in its package repos. The WebKitGTK team publishes releases and security advisories here.
Running a browser capable of javascript execution inside Emacs is potentially
scary since inside Emacs, all code is trusted and usually has unrestricted
access to your system. This is somewhat mitigated by webkitgtk2 using a
multiprocess model where website’s content and scripts run in a separate process
from UI, which in this case is Emacs. I’ll make every effort to ensure that
potentially malicious JavaScript cannot remotely execute arbitrary lisp, however
my trust model will always treat user lisp code as trusted. Furthermore I make
no grantees about the overall security of emacs-webkit
.
I’ve found that the only two applications I regularly have open are Emacs and a browser. Emacs is a joy to use while I feel like I’m constantly fighting my browser. I used to be pretty happy with Firefox and vimperator however since Project Quantum came to FF 57+, I haven’t been satisfied with the extensibility or speed of the WebExtension replacements. There are a few keyboard driven, extensible browser projects such as qutebrowser, LuaKit, vimb, surf, etc…. Unfortunately given the complexity of the “modern” web, they all have to be based off of either the Blink (through QtWebEngine) or WebKit (through WebKitGTK) browser engines (I really wish Mozilla would put more effort into making a clean and maintained API for embedding the latest Gecko with rust components, i.e. servo). I shy away from Blink based browsers out of principles of wanting to avoid Google and the browser monoculture and also because the only easy way to embed Blink is through QtWebEngine and I don’t really like Qt all that much.
An attempt at adding a webkitgtk widget to Emacs with the experimental
xwidgets
feature was made many years ago and has received pretty minimal
development over the years. I think it isn’t for lack of interest in the
concept, but rather the difficulty of understanding the complicated dance that
is Emacs redisplay and how xwidgets hacked themselves into that. Also the
“politics” of having a full featured browser inside Emacs core that can
potentially execute non-free javascript usually means much discussion on
emacs-devel when trying to add new features. I figured that perhaps such a
feature could be implemented instead as a dynamic module. This has the advantage
of a clearer separation between Emacs’ display handling and webkit’s, hopefully
making it easier to workout the inevitable bugs that occur when forcing them to
coexist. This also allows features and fixes to be developed outside of Emacs
core with less concern for supporting all the platforms and environments that
Emacs needs to work with, while also avoiding some of the “political tensions”.
emacs-webkit
now has all of the features that the webkit xwidget
has, plus
more, such as integrating some features from ~xwwp~. I was also able to add
experimental support for opening an non-embedded, dedicated webkit window. In
principle this allows running emacs-webkit
on a tty as webkit popus up its own
window (of course one needs a graphical session to be running, i.e. $DISPLAY
needs to be set). In testing this, I’ve found there will always be behavior out
of Emacs’ control due to the window being at the mercy of the window manager, so
things like focusing end up wonky and harder to control. To try this out: (setq
webkit-own-window t)
.
While I think its neat what EAF has been able to do, I personally have less of a
desire to dive into its code due to its reliance on Qt (and hence Blink for its
browser component) and python, and I suspect this may be a barrier for others as
well. Furthermore, I don’t think it has a technical path forward to natively
work on Wayland due to its current reliance on the XEmbed protocol. My goal with
emacs-webkit
was to have something that will work with pgtk port of Emacs (in
fact I primarily developed it on Wayland running pgtk Emacs).
I think nyxt is certainly a cool project and I wish them the best! However, I would say this project is for the Emacs user like me who begrudgingly uses a modern web browser but wishes they didn’t have to. I wish I could make eww my default browser but it just very often doesn’t cut it due to the unfortunate world of “modern” javascript and “web apps”. In contrast, I would say nyxt is more for the lisp aficionado who wishes the web ran lisp instead of javascript and the Emacs user who wishes Emacs’ underlying UI paradigm looked more like the web’s DOM. I believe the nyxt developers want nyxt to essentially be a common lisp emacs. It is a massive undertaking and will take time for them to build an ecosystem like the one Emacs has developed over the decades. I’ve thought about how I could make nyxt integrate with Emacs in a way I would be happy with and I’ve found that while it is certainly possible to do so given nyxt’s extensibility, I felt like there would always be some friction. For example who’s UI should I use? Do I integrate Emacs buffer list into nyxt’s minibuffer or nyxts buffer list into Emacs? Finally I wanted an excuse to dig more into Emacs’ C guts and this project has given me a lot of chances to do so.
Once things stabilize a bit, I’ll probably package this for MELPA.
emacs-webkit
requires at least Emacs 28
Make sure you have gcc, pkg-config, gtk3, glib-networking, and of course
webkitgtk installed. Then just run make
to make webkit-module.so
.
Some package managers support custom build steps to automate building. For example with the straight.el develop branch you can use this recipe
(straight-use-package
'(webkit :type git :host github :repo "akirakyle/emacs-webkit"
:branch "main"
:files (:defaults "*.js" "*.css" "*.so")
:pre-build ("make")))
I’m a bit hesitant to add lisp code to do this automagically or fetch prebuilt
modules from the web like pdf-tools
or emacs-libvterm
, because I’m a
believer that it should be the job of a package manager, but perhaps I’ll be
convinced otherwise.
First ensure emacs-webkit
is on your load-path
.
(require 'webkit)
(global-set-key (kbd "s-b") 'webkit) ;; Bind to whatever global key binding you want if you want
(require 'webkit-ace) ;; If you want link hinting
(require 'webkit-dark) ;; If you want to use the simple dark mode
(use-package webkit
:bind ("s-b" 'webkit)) ;; Bind to whatever global key binding you want if you want
(use-package 'webkit-ace) ;; If you want link hinting
(use-package 'webkit-dark) ;; If you want to use the simple dark mode
M-x webkit
- Enter url or keywords to search
C-h m
(describe-mode
) to see keybindings.- Feel the power (and weight) of a browser running inside Emacs.
- Emacs’ builtin bookmarks and
org-store-link
are supported!
emacs-webkit
has a concept of an “insert” mode, which moves keyboard focus to
the webview
from Emacs. This means the webview will see all key-presses and
Emacs will only see the modifier keypresses that are unhandled by the
webview. This is useful for typing in a text box or using the keyboard shortcuts
a website might set up. To return focus back to Emacs use C-g
. Some
emacs-webkit
features might have a javascript component that requires moving
to insert mode. Sometimes javascript is buggy or crashes in which case you may
be left surprised that Emacs isn’t responding to you. C-g
is, as always, your
friend here.
webkit-start-web-inspector
will start webkit’s built in dev tools. Beware that
C-g
cannot escape from web inspector’s focus but C-<tab>
appears to return
focus to the webkit view (there doesn’t appear to be much emac-webkit
can do
about this).
;; If you don't care so much about privacy and want to give your data to google
(setq webkit-search-prefix "https://google.com/search?q=")
;; Specify a different set of characters use in the link hints
;; For example the following are more convienent if you use dvorak
(setq webkit-ace-chars "aoeuidhtns")
;; If you want history saved in a different place or
;; Set to `nil' to if you don't want history saved to file (will stay in memory)
(setq webkit-history-file "~/path/to/webkit-history")
;; If you want cookies saved in a different place or
;; Set to `nil' to if you don't want cookies saved
(setq webkit-cookie-file "~/path/to/cookies")
;; See the above explination in the Background section
;; This must be set before webkit.el is loaded so certain hooks aren't installed
(setq webkit-own-window t)
;; Set webkit as the default browse-url browser
(setq browse-url-browser-function 'webkit-browse-url)
;; Force webkit to always open a new session instead of reusing a current one
(setq webkit-browse-url-force-new t)
;; Globally disable javascript
(add-hook 'webkit-new-hook #'webkit-enable-javascript)
;; Override the "loading:" mode line indicator with an icon from `all-the-icons.el'
;; You could also use a unicode icon like ↺
(defun webkit--display-progress (progress)
(setq webkit--progress-formatted
(if (equal progress 100.0)
""
(format "%s%.0f%% " (all-the-icons-faicon "spinner") progress)))
(force-mode-line-update))
;; Set action to be taken on a download request. Predefined actions are
;; `webkit-download-default', `webkit-download-save', and `webkit-download-open'
;; where the save function saves to the download directory, the open function
;; opens in a temp buffer and the default function interactively prompts.
(setq webkit-download-action-alist '(("\\.pdf\\'" . webkit-download-open)
("\\.png\\'" . webkit-download-save)
(".*" . webkit-download-default))
;; Globally use a proxy
(add-hook 'webkit-new-hook (lambda () (webkit-set-proxy "socks://localhost:8000")))
;; Globally use the simple dark mode
(setq webkit-dark-mode t)
I personally use evil so I’ve included evil-collection
bindings which I hope
to upstream at some point when things stabilize.
(use-package evil-collection-webkit
:config
(evil-collection-xwidget-setup))
- Ad block
- Edit text areas in temp emacs buffer
- Pass integration
- XEmbed when using emacs
--with-x
- Browsing sessions/data and better cookie management
- Web extensions?
- Echo url on mouse hover
completing-read
link completion/heading jumping- History
display-table
mode - favicon on mode line