Startup webkit2 branch from the scratch.
authorDaniel Carl <danielcarl@gmx.de>
Sat, 19 Sep 2015 18:47:37 +0000 (20:47 +0200)
committerDaniel Carl <danielcarl@gmx.de>
Wed, 30 Mar 2016 21:32:23 +0000 (23:32 +0200)
78 files changed:
.gitignore
CHANGELOG.md
Makefile
README.md
config.mk
doc/vimb.1 [deleted file]
examples/formfiller/formfiller [deleted file]
examples/formfiller/scripts.js [deleted file]
src/.gitignore [new file with mode: 0644]
src/Makefile
src/arh.c [deleted file]
src/arh.h [deleted file]
src/ascii.h
src/autocmd.c [deleted file]
src/autocmd.h [deleted file]
src/bookmark.c [deleted file]
src/bookmark.h [deleted file]
src/command.c
src/command.h
src/completion.c
src/completion.h
src/config.def.h
src/cookiejar.c [deleted file]
src/cookiejar.h [deleted file]
src/dom.c [deleted file]
src/dom.h [deleted file]
src/ex.c
src/ex.h
src/ext-proxy.c [new file with mode: 0644]
src/ext-proxy.h [new file with mode: 0644]
src/handlers.c [deleted file]
src/handlers.h [deleted file]
src/hints.c [deleted file]
src/hints.h [deleted file]
src/hints.js [deleted file]
src/hints.js.h [new file with mode: 0644]
src/history.c
src/history.h
src/hsts.c [deleted file]
src/hsts.h [deleted file]
src/input.c
src/input.h
src/io.c [deleted file]
src/io.h [deleted file]
src/js.c
src/js.h
src/js2h.sh [deleted file]
src/main.c
src/main.h
src/map.c
src/map.h
src/normal.c
src/normal.h
src/scripts/.gitignore [new file with mode: 0644]
src/scripts/Makefile [new file with mode: 0644]
src/scripts/increment_uri_number.js [new file with mode: 0644]
src/scripts/js2h.sh [new file with mode: 0755]
src/setting.c
src/setting.h
src/shortcut.c
src/shortcut.h
src/util.c
src/util.h
src/webextension/Makefile [new file with mode: 0644]
src/webextension/ext-dom.c [new file with mode: 0644]
src/webextension/ext-dom.h [new file with mode: 0644]
src/webextension/ext-main.c [new file with mode: 0644]
src/webextension/ext-main.h [new file with mode: 0644]
src/webextension/ext-util.c [new file with mode: 0644]
src/webextension/ext-util.h [new file with mode: 0644]
tests/Makefile [deleted file]
tests/manual/112-editable-focus.html [deleted file]
tests/manual/146-hsts-iframe.html [deleted file]
tests/manual/201-editable-focus-in-iframes.html [deleted file]
tests/test-handlers.c [deleted file]
tests/test-map.c [deleted file]
tests/test-shortcut.c [deleted file]
tests/test-util.c [deleted file]

index 00a41de..711ddb0 100644 (file)
@@ -1,11 +1,5 @@
 *.[oad]
 *.lo
+*.so
 *.tar.gz
-src/hints.js.h
-src/config.h
-src/vimb
-src/libvimb.so
-tests/*
-!tests/Makefile
-!tests/*.c
-!tests/manual/
+sandbox
index 77da6f6..9fb2ea4 100644 (file)
@@ -1,5 +1,12 @@
 # Changes in vimb
 
+## [unreleased]
+
+### Removed
+
+* `FEATURE_COOKIE` precompiler flag was removed bcause compiling without cookie
+  support does not bring any real benefit
+
 ## [2.11] - 2015-12-17
 
 ### Added
@@ -37,4 +44,5 @@
   cookie file
 * Fixed none POSIX `echo -n` call
 
+[unreleased]: https://github.com/fanglingsu/vimb/compare/2.11...HEAD
 [2.11]: https://github.com/fanglingsu/vimb/compare/2.10...2.11
index e21743e..b7abcfd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,49 +1,39 @@
 include config.mk
 
-SRCDIR=src
-DOCDIR=doc
+all: vimb
 
-all: $(TARGET)
 options:
-       @echo "$(PROJECT) build options:"
+       @echo "vimb build options:"
        @echo "LIBS    = $(LIBS)"
        @echo "CFLAGS  = $(CFLAGS)"
        @echo "LDFLAGS = $(LDFLAGS)"
        @echo "CC      = $(CC)"
 
-test: $(LIBTARGET)
-       @$(MAKE) $(MFLAGS) -s -C tests
+install: vimb
+       @# binary
+       install -d $(BINPREFIX)
+       install -m 755 $(SRCDIR)/vimb $(BINPREFIX)/vimb
+       @# extension
+       install -d $(EXTPREFIX)
+       install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET)
 
-clean:
-       @$(MAKE) $(MFLAGS) -C src clean
-       @$(MAKE) $(MFLAGS) -C tests clean
+uninstall:
+       $(RM) $(BINPREFIX)/vimb $(EXTPREFIX)/$(EXTTARGET)
 
-install: $(TARGET) $(DOCDIR)/$(MAN1)
-       install -d $(DESTDIR)$(BINDIR)
-       install -m 755 $(SRCDIR)/$(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET)
-       install -d $(DESTDIR)$(EXAMPLEDIR)
-       cp -r examples/* $(DESTDIR)$(EXAMPLEDIR)
-       install -d $(DESTDIR)$(MANDIR)/man1
-       @sed -e "s!VERSION!$(VERSION)!g" \
-               -e "s!PREFIX!$(PREFIX)!g" \
-               -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/$(MAN1) > $(DESTDIR)$(MANDIR)/man1/$(MAN1)
+vimb: $(SUBDIRS:%=%.subdir-all)
 
-uninstall:
-       $(RM) $(DESTDIR)$(BINDIR)/$(TARGET)
-       $(RM) $(DESTDIR)$(MANDIR)/man1/$(MAN1)
-       $(RM) -r $(DESTDIR)$(EXAMPLEDIR)
+%.subdir-all:
+       $(MAKE) $(MFLAGS) -C $*
 
-dist: dist-clean
-       @echo "Creating tarball."
-       @git archive --format tar -o $(DIST_FILE) HEAD
+%.subdir-clean:
+       $(MAKE) $(MFLAGS) -C $* clean
 
-dist-clean:
-       $(RM) $(DIST_FILE)
+clean: $(SUBDIRS:%=%.subdir-clean)
 
-$(TARGET):
-       @$(MAKE) $(MFLAGS) -C src $(TARGET)
+runsandbox: sandbox
+       sandbox/usr/bin/vimb
 
-$(LIBTARGET):
-       @$(MAKE) $(MFLAGS) -C src $(LIBTARGET)
+sandbox:
+       make RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install
 
-.PHONY: clean all install uninstall options dist dist-clean test
+.PHONY: all options clean install uninstall sandbox
index 0944100..b2e0331 100644 (file)
--- a/README.md
+++ b/README.md
-# Vimb - the Vim-like browser
-
-Vimb is a Vim-like web browser that is inspired by Pentadactyl and Vimprobable.
-The goal of Vimb is to build a completely keyboard-driven, efficient and
-pleasurable browsing-experience with low memory and CPU usage that is
-intuitive to use for Vim users.
-
-More information and some screenshots of Vimb browser in action can be found on
-the project page of [Vimb][].
-
-## Features
-
-- it's modal like Vim
-- Vim like [keybindings][] - assignable for each browser mode
-- nearly every configuration can be changed at runtime with Vim like [set syntax][set]
-- [history][] for `ex` commands, search queries, URLs
-- completions for: commands, URLs, bookmarked URLs, variable names of settings, search-queries
-- [hinting][hints] - marks links, form fields and other clickable elements to
-  be clicked, opened or inspected
-- SSL validation against ca-certificate file
-- HTTP Strict Transport Security (HSTS)
-- open input or textarea with configurable external editor
-- user defined URL-shortcuts with placeholders
-- custom [protocol handlers][handlers]
-- read it later [queue][] to collect URIs for later use
-- multiple yank/paste [registers][]
-- Vim like [autocmd][]
-
-## Packages
-
-- Arch Linux [vimb-git][arch-git], [vimb][arch]
-- [NetBSD][]
-- [FreeBSD][]
-- [Void Linux][]
-
-## Dependencies
-
-- libwebkit >=1.5.0
-- libgtk+-2.0
-- libsoup >=2.38
-
-On Ubuntu these dependencies can be installed by
-`sudo apt-get install libsoup2.4-dev libwebkit-dev libgtk-3-dev libwebkitgtk-3.0-dev`.
-
-## Install
-
-Edit `config.mk` to match your local setup.
-
-Edit src/config.h to match your personal preferences.
-Edit `src/config.h` to match your personal preferences.
-
-The default `Makefile` will not overwrite your customised `config.h` with the
-contents of `config.def.h`, even if it was updated in the latest git pull.
-Therefore, you should always compare your customised `config.h` with
-`config.def.h` and make sure you include any changes to the latter in your
-`config.h`.
-
-Run the following commands to compile and install Vimb (if necessary, the last one as
-root).
-
-    make clean
-    make // or make GTK=3 to compile against gtk3
-    make install
-
-To build Vimb against GTK3 you can use `make GTK=3`.
-
-# License
-
-Information about the license is found in the file: LICENSE.
-
-# Mailing list
-
-- feature requests, issues and patches can be discussed on the [mailing list][mail]
-
-[vimb]:        http://fanglingsu.github.io/vimb/ "Vimb - Vim like browser project page"
-[keybindings]: http://fanglingsu.github.io/vimb/keybindings.html "vimb keybindings"
-[hints]:       http://fanglingsu.github.io/vimb/keybindings.html#hinting "vimb hinting"
-[queue]:       http://fanglingsu.github.io/vimb/commands.html#queue "vimb read it later queue feature"
-[history]:     http://fanglingsu.github.io/vimb/keybindings.html#history "vimb keybindings to access history"
-[handlers]:    http://fanglingsu.github.io/vimb/commands.html#handlers "vimb custom protocol handlers"
-[registers]:   http://fanglingsu.github.io/vimb/keybindings.html#registers "vimb yank/paste registers"
-[mail]:        https://lists.sourceforge.net/lists/listinfo/vimb-users "vimb - mailing list"
-[NetBSD]:      http://pkgsrc.se/wip/vimb "vimb - NetBSD package"
-[autocmd]:     http://fanglingsu.github.io/vimb/commands.html#autocmd "Vim like autocmd and augroup feature"
-[set]:         http://fanglingsu.github.io/vimb/commands.html#settings "Vim like set syntax"
-[Arch-git]:    https://aur.archlinux.org/packages/vimb-git/ "vimb - archlinux package"
-[Arch]:        https://aur.archlinux.org/packages/vimb/ "vimb - archlinux package"
-[FreeBSD]:     http://www.freshports.org/www/vimb/ "vimb - FreeBSD port"
-[Void Linux]:  https://github.com/voidlinux/void-packages/blob/master/srcpkgs/vimb/template "vimb - Void Linux package"
+# vimb - the vim like browser
+
+This is the development branch for the new webkit2 port of vimb. This branch
+does not work and lags a lot of features of the webkit1 version of vimb. So
+this is only meant to be the playground for the developers at the moment.
+
+If you like to have a working vimb, please use the master branch.
+
+## Patching and Coding style
+
+- the code is indented by 4 spaces - if you use vim to code you can set
+  `:set expandtab ts=4 sts=4 sw=4`
+- the functions are sorted alphabetically within the c files
+- it's a good advice to orientate on the already available code
+- if you are using `indent`, following options describe best the code style
+  - `--k-and-r-style`
+  - `--case-indentation4`
+  - `--dont-break-function-decl-args`
+  - `--dont-break-procedure-type`
+  - `--dont-line-up-parentheses`
+  - `--no-tabs`
+
+## directories
+
+    ├── doc                 documentation like manual page
+    └── src                 all sources to build vimb
+        ├── scripts         JavaScripts that are compiled in to various purposes
+        └── webextension    Source files for the webextension
+
+## dependencies
+
+- webkit2gtk-4.0 >= 2.3.5
+
+## compile and run
+
+To inform vimb during compile time where the webextension should be loaded
+from, the `RUNPREFIX` option can be set to a full qualified path to the
+directory where the extension should be sotred in.
+
+To run vimb wihtout installation you could run as a sandbox like this
+
+    make runsandbox
+
+This will compile and install vimb into the local _sandbox_ folder in the
+project directory.
+
+## Tasks
+
+1. general infrastructure and built
+  - [x] write make file
+    - [x] allow to built as sandbox for local testing
+    - [x] add a way to specify the target of the webextension shared objects
+          this is now available with the `RUNPREFIX` oprion for the make
+  - [x] establish communication channel between the vimb instance and the now
+        required webextension (dbus)
+2. migrate as many of the features of the webkit1 vimb
+  - [ ] starting with custom config file `--config,-c` option
+  - [ ] running multiple ex-commands during startup `--cmd,-C` options
+  - [ ] starting with a named profile `--profile,-p` option
+  - [ ] xembed `--embed,-e` option
+  - [ ] socket support `--socket,-s` and `--dump,-d` option to print the actual
+        used socket path to stdout
+  - [ ] kiosk-mode `--kiosk,-k`
+  - [ ] allow to start vimb reading html from `stdin` by `vimb -`
+  - [ ] browser modes normal, input, command, pass-through and hintmode
+  - [ ] download support
+  - [ ] editor command
+  - [ ] external downloader
+  - [ ] hinting
+  - [ ] searching and matching of search results
+  - [ ] navigation j, k, h, l, ...
+  - [ ] history and history lookup
+  - [ ] completion
+  - [ ] HSTS
+  - [ ] auto-response-header
+  - [ ] cookies support
+  - [ ] register support and `:register` command
+  - [ ] read it later queue
+  - [ ] show scroll indicator in statusbar as top, x%, bttom or all
+        how can we get this information from webview easily?
+  - [ ] find a way to disable the scrollbars on the main frame
+  - [ ] page marks - maybe we change make them global (shared between
+        instances and to work also if the page was reloaded or changed like
+        the marks in vim)
+  - [x] zooming
+  - [ ] yanking
+  - [x] keymapping
+  - [ ] URL handler
+  - [x] shortcuts
+  - [ ] autocommands and augroups
+3. documentation
+4. testing
+  - [ ] write automatic test to the essential main features
+  - [ ] adapt the manual test cases and add some more to avoid regressions
+        before a release
+  - [ ] write new test cases for essential things like mode switching of vimb
+        when clicking form fields or tabbing over them.
+5. new features and changed behaviour
+  - [ ] try to use the webkit2 feature of running multiple pages with related
+        view instance `webkit_web_view_new_with_related_view`
+  - [ ] allow setting of different scopes, global and instance (new feature)
+  - [ ] remove the settings related to the gui like `status-color-bg` this was
+        only a hack and is not recommended for new gtk3 applications. the
+        color and font settings should be setup by css instead.
+  - [ ] webkit2 does not provide the view mode `source` so maybe this is going
+        to be removed together with the `gf` keybinding or we find a simple
+        workaround for this
+
+# license
+
+Information about the license are found in the file LICENSE.
index 3809336..e3f1800 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -1,63 +1,37 @@
-#----------------user/install options----------------
-VERSION = 2.11
-
-PROJECT     = vimb
-PREFIX     ?= /usr/local
-BINDIR     ?= $(PREFIX)/bin
-MANDIR     ?= $(PREFIX)/share/man
-EXAMPLEDIR ?= $(PREFIX)/share/$(PROJECT)/examples
-
-#----------------compile options---------------------
-
-VERBOSE ?= 0
-
-LIBS = libsoup-2.4
-
-GTK3LIBS=gtk+-3.0 webkitgtk-3.0
-GTK2LIBS=gtk+-2.0 webkit-1.0
-
-ifeq (${GTK}, 3)
-ifeq ($(shell pkg-config --exists $(GTK3LIBS) && echo 1), 1) #has gtk3 libs
-LIBS += $(GTK3LIBS)
-USEGTK3 = 1
-else
-LIBS += $(GTK2LIBS)
-$(warning Cannot find gtk3-libs, falling back to gtk2)
-endif
-else
-LIBS += $(GTK2LIBS)
-endif
-
-# generate a first char upper case project name
-PROJECT_UCFIRST = $(shell echo '${PROJECT}' | awk '{for(i=1;i<=NF;i++){$$i=toupper(substr($$i,1,1))substr($$i,2)}}1')
-
-CPPFLAGS  = -DVERSION=\"${VERSION}\"
-CPPFLAGS += -DPROJECT=\"${PROJECT}\" -DPROJECT_UCFIRST=\"${PROJECT_UCFIRST}\"
+VERSION = dev-3.0
+
+PREFIX         ?= /usr/local
+BINPREFIX      := $(DESTDIR)$(PREFIX)/bin
+MANPREFIX      := $(DESTDIR)$(PREFIX)/share/man
+EXAMPLEPREFIX  := $(DESTDIR)$(PREFIX)/share/vimb/example
+RUNPREFIX      := $(PREFIX)
+EXTPREFIX      := $(RUNPREFIX)/lib/vimb
+
+# define some directories
+SRCDIR  = src
+DOCDIR  = doc
+SUBDIRS = $(SRCDIR)/scripts $(SRCDIR)/webextension $(SRCDIR)
+
+# used libs
+LIBS = gtk+-3.0 webkit2gtk-4.0
+
+# setup general used CFLAGS
+CFLAGS   += -std=c99 -pipe -Wall
+#CPPFLAGS += -pedantic
+CPPFLAGS += -DVERSION=\"${VERSION}\" -DEXTPREFIX=\"${EXTPREFIX}\"
+CPPFLAGS += -DPROJECT=\"vimb\" -DPROJECT_UCFIRST=\"Vimb\"
 CPPFLAGS += -D_XOPEN_SOURCE=500
-CPPFLAGS += -D_POSIX_SOURCE
-ifeq ($(USEGTK3), 1)
-CPPFLAGS += -DHAS_GTK3
 CPPFLAGS += -DGSEAL_ENABLE
 CPPFLAGS += -DGTK_DISABLE_SINGLE_INCLUDES
-CPPFLAGS += -DGTK_DISABLE_DEPRECATED
 CPPFLAGS += -DGDK_DISABLE_DEPRECATED
-endif
-
-# prepare the lib flags used for the linker
-LIBFLAGS = $(shell pkg-config --libs $(LIBS))
-
-# some compiler flags in case CFLAGS is not set by user
-# -Wno-typedef-redefinition to avoid redifinition warnings caused by glib
-CFLAGS  ?= -Wall -pipe -Wno-overlength-strings -Werror=format-security -Wno-typedef-redefinition
-# normal compiler flags
-CFLAGS  += $(shell pkg-config --cflags $(LIBS))
-CFLAGS  += -std=c99
-CFLAGS  += ${CPPFLAGS}
-LDFLAGS += ${LIBFLAGS}
 
-TARGET    = $(PROJECT)
-LIBTARGET = lib$(PROJECT).so
-DIST_FILE = $(PROJECT)_$(VERSION).tar.gz
-MAN1      = $(PROJECT).1
+# flags used to build webextension
+EXTTARGET   = webext_main.so
+EXTCFLAGS   = ${CFLAGS} -fPIC $(shell pkg-config --cflags webkit2gtk-4.0)
+EXTCFLAGS  += $(CPPFLAGS)
+EXTLDFLAGS  = $(shell pkg-config --libs webkit2gtk-4.0) -shared
 
-MFLAGS   ?= --no-print-directory
+# flags used for the main application
+CFLAGS     += $(shell pkg-config --cflags $(LIBS))
+CFLAGS     += ${CPPFLAGS}
+LDFLAGS    += $(shell pkg-config --libs $(LIBS))
diff --git a/doc/vimb.1 b/doc/vimb.1
deleted file mode 100644 (file)
index 7c0e2ea..0000000
+++ /dev/null
@@ -1,1617 +0,0 @@
-.\" vim: ft=groff
-.ad l
-.TH VIMB 1 "DATE" "vimb/VERSION" "Vimb Manual"
-.de EX
-.nf
-.ft CW
-..
-.de EE
-.ft R
-.fi
-..
-.SH NAME
-Vimb - Vim Browser - A modal web browser based on WebKit, inspired by Vim: the
-great editor.
-.SH SYNOPSIS
-.B vimb
-.OP OPTIONS
-.RI [ URI "|" file "|" - ]
-.SH DESCRIPTION
-Vimb is a WebKit based web browser that behaves like the Vimperator
-plugin for Firefox and has usage paradigms from the great editor, Vim.
-The goal of Vimb is to build a completely keyboard-driven, efficient
-and pleasurable browsing-experience.
-.SH OPTIONS
-If no \fIURI\fP or \fIfile\fP is given, Vimb will open the configured
-home-page.
-If \fIURI\fP is '-', Vimb reads the HTML to display from stdin.
-.PP
-Mandatory arguments to long options are mandatory for short options too.
-.TP
-.BI "\-C, \-\-cmd " "CMD"
-Run \fICMD\fP as ex command line right before the first page is loaded.
-If the flag is used more than one time, the ordering is preserved when
-\fICMD\fP are executed.
-You could also pass several ex commands in one \fICMD\fP,
-if they are separated by "|".
-.sp
-Example:
-.EX
-vimb --cmd "set cookie-accept=origin|set header=Referer,DNT=1"
-.EE
-.TP
-.BI "\-c, \-\-config " "CONFIG-FILE"
-Use custom configuration given as \fICONFIG-FILE\fP.
-This will also be applied on new spawned instances.
-.TP
-.BI "\-p, \-\-profile " "PROFILE-NAME"
-Create or open specified configuration profile. Configuration data for the profile is stored in a directory named \fIPROFILE-NAME\fP under default directory for configuration data.
-.TP
-.BI "\-e, \-\-embed " "WINID"
-.I WINID
-of an XEmbed-aware application, that Vimb will use as its parent.
-.TP
-.B \-d, \-\-dump
-Dump the current used socket path to stdout in case Vimb is started with \-s
-option.
-.sp
-Example:
-.EX
-sh -c "./vimb -s -d > ~/vimb.socket" &
-echo ":o github.com<CR>" | socat - unix-connect:$(< ~/vimb.socket)
-.EE
-.TP
-.B "\-h, \-\-help"
-Show help options.
-.TP
-.B \-k, \-\-kiosk
-Run Vimb in kiosk mode with nearly no keybindings, not inputbox and no context
-menu.
-.TP
-.B \-s, \-\-socket
-If given Vimb will create a control socket in the user runtime directory.
-.TP
-.B "\-v, \-\-version"
-Print build and version information.
-.SH MODES
-Vimb is modal and has the following main modes:
-.TP
-.B Normal Mode
-The default mode.
-Pressing Escape always enter normal mode.
-.TP
-.B Input Mode
-Used for editing text elements in a webpage.
-.TP
-.B Command Mode
-Execute `ex` commands from the builtin inputbox (commandline).
-.TP
-.B Pass-Through Mode
-In Pass-Through mode only the `<Esc>` and `<C-[>` keybindings are interpreted
-by Vimb, all other keystrokes are given to the webview to handle them.
-This allows the use of a website's configured keybindings, that might otherwise
-be swallowed by Vimb.
-.SH NORMAL MODE COMMANDS
-Some of the Normal Model Commands can have a numeric count to multiply the
-effect of the command.
-If a command supports the count this is shown as \fB[N]\fP.
-.SS General
-.TP
-.B :
-Start Command Mode and print `:' to the input box.
-.TP
-.B gi
-Set cursor to the first editable element in the page and switch to Input
-Mode.
-.TP
-.B CTRL\-Z
-Switch Vimb into Pass-Through Mode.
-.TP
-.B gf
-Toggle show HTML source of the current page.
-.TP
-.B gF
-Open the Web Inspector for the current page.
-.TP
-.B CTRL\-V
-Pass the next key press directly to GTK.
-.TP
-.B CTRL\-Q
-Quit the browser if there are no running downloads.
-.SS Navigation
-.TP
-.B o
-Start Command Mode and print `:open ' to the input box.
-.TP
-.B O
-Start Command Mode and print `:open URI' to the input box.
-.TP
-.B t
-Start Command Mode and print `:tabopen ' to the input box.
-.TP
-.B T
-Start Command Mode and print `:tabopen URI' to the input box.
-.TP
-.B gh
-Open the configured home-page.
-.TP
-.B gH
-Open the configured home-page in a new window.
-.TP
-.B u
-Open the last closed page.
-.TP
-.B U
-Open the last closed page in a new window.
-.TP
-.B CTRL\-P
-Open the oldest entry from the Read It Later queue in the current browser window (only if
-Vimb has been compiled with QUEUE feature).
-.TP
-.BI [ \(dqx ]p
-Open the URI out of the register \fIx\fP or, if not given, from the clipboard.
-.TP
-.BI [ \(dqx ]P
-Open the URI out of the register \fIx\fP or, if not given, from the clipboard in a
-new window.
-.TP
-.BI [ N ]CTRL\-O
-Go back \fIN\fP steps in the browser history.
-.TP
-.BI [ N ]CTRL\-I
-Go forward \fIN\fP steps in the browser history.
-.TP
-.BI [ N ]gu
-Go to the \fIN\fPth descendent directory of the current opened URI.
-.TP
-.B gU
-Go to the domain of the current opened page.
-.TP
-.BI [ N ]CTRL\-A
-Increments the last number in URL by 1, or by \fIN\fP if given.
-.TP
-.BI [ N ]CTRL\-X
-Decrements the last number in URL by 1, or by \fIN\fP if given.
-Negative numbers are not supported as trailing numbers in URLs
-are often preceded by hyphens.
-.TP
-.B r
-Reload the website.
-.TP
-.B R
-Reload the website without using caches.
-.TP
-.B CTRL\-C
-Stop loading the current page.
-.SS Motion
-.TP
-.BI [ N ]CTRL\-F
-Scroll \fIN\fP pages down.
-.TP
-.BI [ N ]CTRL\-B
-Scroll \fIN\fP pages up.
-.TP
-.BI [ N ]CTRL\-D
-Scroll \fIN\fP half pages down.
-.TP
-.BI [ N ]CTRL\-U
-Scroll \fIN\fP half pages up.
-.TP
-.BI [ N ]gg
-Scroll to the top of the current page.
-Or if \fIN\fP is given to \fIN\fP% of the page.
-.TP
-.BI [ N ]G
-Scroll to the bottom of the current page.
-Or if \fIN\fP is given to \fIN\fP% of the page.
-.TP
-.B 0, ^
-Scroll to the absolute left of the document.
-Unlike in Vim, 0 and ^ work exactly the same way.
-.TP
-.B $
-Scroll to the absolute right of the document.
-.TP
-.BI [ N ]h
-Scroll \fIN\fP steps to the left of page.
-.TP
-.BI [ N ]l
-Scroll \fIN\fP steps to the right of page.
-.TP
-.BI [ N ]j
-Scroll page \fIN\fP steps down.
-.TP
-.BI [ N ]k
-Scroll page \fIN\fP steps up.
-.TP
-.BI [ N ]]]
-Follow the last \fIN\fPth link matching `nextpattern'.
-.TP
-.BI [ N ][[
-Follow the last \fIN\fPth link matching `previouspattern'.
-.TP
-.BI m{ a-z }
-Set a page mark {\fIa-z\fP} at the current position on the page.
-Such set marks are only available on the current page;
-if the page is left, all marks will be removed.
-.TP
-.BI '{ a-z }
-Jump to the mark {\fIa-z\fP} on the current page.
-.TP
-.B ''
-Jumps to the position before the latest jump, or where the last "m'" command
-was given.
-.SS Hinting
-Hinting in Vimb is how you accomplish the tasks that you would do with the
-mouse in common mouse-driven browsers: open a URI, yank a URI, save a page and
-so on. When hinting is started, the relevant elements on the page will
-be marked by labels generated from configured `hintkeys'.
-Hints can be selected by using <Tab>, <C-I> or <C-Tab>, <C-O>,
-by typing the chars of the label, or filtering the elements by some text
-that is part of the hinted element (like URI, link text, button label)
-or any combination of these methods.
-If <enter> is pressed, the current active hint will be fired.
-If only one possible hint remains, this will be fired automatically.
-.PP
-.BI Syntax: " ;{mode}{hint}"
-.PP
-Start Hints mode.
-Different elements depending on \fImode\fP are highlighted and `numbered'.
-Elements can be selected either by typing their label, or by typing part
-of their text (\fIhint\fP) to narrow down the result.
-When an element has been selected, it is automatically clicked
-or used (depending on \fImode\fP) and hint mode ends.
-.PP
-The filtering of hints by text splits the query at ' ' and use the single parts
-as separate queries to filter the hints.
-This is useful for hints that have a lot of filterable chars in common
-and many chars are required to make a distinct selection.
-For example ';over tw' will easily select the second hint out of
-{'very long link text one', 'very long link text two'}.
-.PP
-The following keys have special meanings in Hints modes:
-.PD 0
-.IP \fB<CR>\fP
-Selects the first highlighted element, or the current focused.
-.IP "\fB<Tab>\fP"
-Moves the focus to the next hint element.
-.IP "\fB<S-Tab>\fP"
-Moves the focus to the previous hint element.
-.IP "\fB<Esc>, CTRL\-C, CTRL\-[\fP"
-Exits Hints mode without selecting an element.
-.PD
-.TP
-.B Hint modes:
-.RS
-.PD 0
-.TP
-.B f
-Is an alias for the \fB;o\fP hint mode.
-.TP
-.B F
-Is an alias for the \fB;t\fP hint mode.
-.TP
-.B ;o
-Open hint's location in the current window.
-.TP
-.B ;t
-Open hint's location in a new window.
-.TP
-.B ;s
-Saves the hint's destination under the configured `download-path'.
-.TP
-.B ;O
-Generate an `:open' prompt with hint's URI.
-.TP
-.B ;T
-Generate an `:tabopen' prompt with hint's URI.
-.TP
-.B ;e
-Open the configured $EDITOR (`editor-command') with the hinted form element's
-content.
-If the file in $EDITOR is saved and the $EDITOR is closed, the file
-content will be put back in the form field.
-.TP
-.B ;i
-Open hinted image in the current window.
-.TP
-.B ;I
-Open hinted image in a new window.
-.TP
-.B ;p
-Push the hint's URI to the end of the Read It Later queue like the `:qpush'
-command.
-This is only available if Vimb was compiled with the QUEUE feature.
-.TP
-.B ;P
-Push the hint's URI to the beginning of the Read It Later queue like the
-`:qunshift' command.
-This is only available if Vimb was compiled with the QUEUE feature.
-.TP
-.B ;x
-Hints like ;o, but instead of opening the hinted URI, the
-`x-hint-command' is run in Vimb.
-.TP
-.BI [ \(dqx ];y
-Yank hint's destination location into primary and secondary clipboard and into
-the register \fIx\fP.
-.TP
-.BI [ \(dqx ];Y
-Yank hint's text description or form text into primary and secondary clipboard
-and into the register \fIx\fP.
-.PD
-.RE
-.TP
-.BI Syntax: " g;{mode}{hint}"
-Start an extended hints mode and stay there until <Esc> is pressed.
-Like normal hinting, except that after a hint is selected, hints
-remain visible so that another one can be selected with the same action
-as the first.
-Note that the extended hint mode can only be combined with the following
-hint modes \fII p P s t y Y\fP.
-.SS Searching
-.TP
-.BI / QUERY ", ?" QUERY
-Start searching for \fIQUERY\fP in the current page.
-\fI/\fP start search forward, \fI?\fP in backward direction.
-.TP
-.B *, #
-Start searching for the current selected text, or if no text is selected for
-the content of the primary or secondary clipboard.
-\fI*\fP start the search in forward direction and \fI#\fP in backward
-direction.
-.sp
-Note that these commands will yank the text selection into the clipboard and
-may remove other content from there!
-.TP
-.BI [ N ]n
-Search for \fIN\fPnth next search result depending on current search
-direction.
-.TP
-.BI [ N ]N
-Search for \fIN\fPnth previous search result depending on current search
-direction.
-.SS Zooming
-.TP
-.BI [ N ]zi
-Zoom-In the text of the page by \fIN\fP steps.
-.TP
-.BI [ N ]zo
-Zoom-Out the text of the page by \fIN\fP steps.
-.TP
-.BI [ N ]zI
-Full-Content Zoom-In the page by \fIN\fP steps.
-.TP
-.BI [ N ]zO
-Full-Content Zoom-Out the page by \fIN\fP steps.
-.TP
-.B zz
-Reset Zoom.
-.SS Yank
-.TP
-.BI [ \(dqx ]y
-Yank the URI or current page into register \fIx\fP and clipboard.
-.TP
-.BI [ \(dqx ]Y
-Yank the current selection into register \fIx\fP and clipboard.
-.SH COMMAND MODE
-Commands that are listed below are ex-commands like in Vim, that are typed
-into the inputbox (the command line of vimb).
-The commands may vary in their syntax or in the parts they allow,
-but in general they follow a simple syntax.
-.PP
-.BI Syntax: " :[:| ][N]cmd[name][!][ lhs][ rhs]"
-.sp
-Where \fIlhs\fP (left hand side) must not contain any unescaped space.
-The syntax of the rhs (right hand side) if this is available depends on the
-command.
-At the moment the count parts [N] of commands is parsed, but currently there is
-no command that uses the count.
-.sp
-Commands that are typed interactivly (from the inputbox or from socket) are
-normally recorded into command history and register.
-To avoid this, the commands can be prefixed by one or more additional `:' or
-whitespace.
-.PP
-Multiple commands, separated by a `|' can be given in a single command line
-and will be executed consecutively.
-The pipe can be included as an argument to a command by escaping it with a
-backslash.
-.br
-Following commands process the entire command-line string literally.
-These commands will include any `|' as part of their argument string and so
-cannot be followed by another command.
-.PP
-.PD 0
-.IP - 2
-autocmd
-.IP -
-cmap, cnoremap, imap, inoremap, nmap, nnoremap
-.IP -
-eval
-.IP -
-normal
-.IP -
-open, tabopen
-.IP -
-shellcmd
-.PD
-.SS Command Line Editing
-.TP
-.B <Esc>, CTRL\-[, CTRL-C
-Ignore all typed content and switch back to normal mode.
-.TP
-.B <CR>
-Submit the entered `ex` command or search query to run it.
-.TP
-.B CTRL\-H
-Deletes the char before the cursor.
-.TP
-.B CTRL\-W
-Deletes the last word before the cursor.
-.TP
-.B CTRL\-U
-Remove everything between cursor and prompt.
-.TP
-.B CTRL\-B
-Moves the cursor directly behind the prompt `:'.
-.TP
-.B CTRL\-E
-Moves the cursor after the char in inputbox.
-.TP
-.B CTRL\-V
-Pass the next key press directly to GTK.
-.TP
-.B CTRL\-R {a-z"%:/;}
-Insert the content of given register at cursor position.
-See also section about `:reg[ister]' command.
-.SS Command Line History
-.TP
-.B <Tab>
-Start completion of the content in the inputbox in forward direction.
-.TP
-.B <S-Tab>
-Start completion of the content in the inputbox in backward direction.
-.TP
-.B <Up>
-Step backward in the command history.
-.TP
-.B <Down>
-Step forward in the command history.
-.SS Open
-.TP
-.BI ":o[pen] [" URI ]
-Open the give \fIURI\fP in the current window.
-If \fIURI\fP is empty, the configured 'home-page' is opened.
-.TP
-.BI ":t[abopen] [" URI ]
-Open the give \fIURI\fP in a new window.
-If \fIURI\fP is empty, the configured 'home-page' is opened.
-.SS Key Mapping
-Key mappings allow users to alter the actions of key presses.
-Each key mapping is associated with a mode and only has effect
-when the mode is active.
-The following commands allow the user to substitute one sequence
-of key presses by another.
-.PP
-.BI Syntax: " :{m}map {lhs} {rhs}"
-.PP
-Note that the \fIlhs\fP ends with the first found space.
-If you want to use space also in the {lhs} you have to escape this
-with a single `\\', as shown in the examples.
-.sp
-The \fIrhs\fP starts with the first non-space char. If you want a \fIrhs\fP
-that starts with a space, you have to use "<Space>".
-.PP
-Standard key mapping commands are provided for these modes \fIm\fP:
-.PD 0
-.IP \fBn\fP
-Normal mode: when browsing normally.
-.IP \fBi\fP
-Insert mode: when interacting with text fields on a website.
-.IP \fBc\fP
-Command Line mode: when typing into Vimb's command line.
-.PD
-.PP
-Most keys in key sequences are represented simply by the character that you
-see on the screen when you type them.
-However, as a number of these characters have special meanings, and a
-number of keys have no visual representation, a special notation is required.
-.PP
-As special key names have the format \fI<...>\fP.
-The following special keys can be used: <Left>, <Up>, <Right>, <Down>
-for the cursor keys, <Tab>, <Esc>, <CR>, <Space>, <BS>, <F1>-<F12> and <C-A>-<C-Z>.
-.TP
-.BI ":nm[ap] {" lhs "} {" rhs }
-.TP
-.BI ":im[ap] {" lhs "} {" rhs }
-.TP
-.BI ":cm[ap] {" lhs "} {" rhs }
-Map the key sequence \fIlhs\fP to \fIrhs\fP for the modes where the map
-command applies.
-The result, including \fIrhs\fP, is then further scanned for mappings.
-This allows for nested and recursive use of mappings.
-.RS
-.P
-Examples:
-.PD 0
-.IP ":cmap <C-G>h /home/user/downloads/"
-Adds a keybind to insert a file path into the input box.
-This could be useful for the `:save' command
-that could be used as ":save ^Gh".
-.IP ":nmap <F1> :set scripts=on<CR>:open !glib<Tab><CR>"
-This will enable scripts and lookup the first bookmarked URI with the tag
-`glib' and open it immediately if F1 key is pressed.
-.IP ":nmap \\\\\ \\\\\  50G;o"
-Example which maps two spaces to go to 50% of the page, and start hinting mode.
-.PD
-.RE
-.TP
-.BI ":nn[oremap] {" lhs "} {" rhs }
-.TP
-.BI ":ino[remap] {" lhs "} {" rhs }
-.TP
-.BI ":cno[remap] {" lhs "} {" rhs }
-Map the key sequence \fIlhs\fP to \fIrhs\fP for the mode where the map command
-applies.
-Disallow mapping of \fIrhs\fP, to avoid nested and recursive mappings.
-Often used to redefine a command.
-.TP
-.BI ":nu[nmap] {" lhs }
-.TP
-.BI ":iu[nmap] {" lhs }
-.TP
-.BI ":cu[nmap] {" lhs }
-Remove the mapping of \fIlhs\fP for the applicable mode.
-.SS Bookmarks
-.TP
-.BI ":bma [" tags ]
-Save the current opened URI with \fItags\fP to the bookmark file.
-.TP
-.BI ":bmr [" URI ]
-Removes all bookmarks for given \fIURI\fP or, if not given, the current opened
-page.
-.SS Handlers
-Handlers allow specifying external scripts to handle alternative URI methods.
-.TP
-.BI ":handler-add " "handler" "=" "cmd"
-Adds a handler to direct \fIhandler\fP links to the external \fIcmd\fP.
-The \fIcmd\fP can contain one placeholder `%s` that will be filled by the
-full URI given when the command is called.
-.RS
-.P
-Examples:
-.PD 0
-.IP ":handler-add magnet=xdg-open %s"
-to open magnet links with xdg-open.
-.IP ":handler-add magnet=transmission-gtk %s"
-to open magnet links directly with Transmission.
-.IP ":handler-add irc=irc-handler.sh %s"
-to direct irc://<host>:<port>/<channel> links to a wrapper for your IRC client.
-.PD
-.RE
-.TP
-.BI ":handler-remove " "handler"
-Remove the handler for the given URI \fIhandler\fP.
-.SS Shortcuts
-Shortcuts allow the opening of an URI built up from a named template with additional
-parameters.
-If a shortcut named 'dd' is defined, you can use it with `:open dd
-list of parameters' to open the generated URI.
-.PP
-Shortcuts are convenient to use with search engines where the URI is standardised
-and a single parameter is user defined.
-.TP
-.BI ":shortcut-add " "shortcut" "=" "URI"
-Adds a shortcut with the \fIshortcut\fP and \fIURI\fP template.
-The \fIURI\fP can contain multiple placeholders $0-$9 that will be
-filled by the parameters given when the shortcut is called.
-The parameters given when the shortcut is called will be split
-into as many parameters like the highest used placeholder.
-.sp
-To use spaces within the parameters, the parameters can be grouped by
-surrounding them with single-or double quotes-as shown in example shortcut
-`map'.
-.RS
-.P
-Examples:
-.PD 0
-.IP ":shortcut-add dl=https://duckduckgo.com/lite/?q=$0"
-to setup a search engine.
-Can be called by `:open dl my search phrase'.
-.IP ":shortcut-add gh=https://github.com/$0/$1"
-to build URIs from given parameters.
-Can be called `:open gh fanglingsu vimb'.
-.IP ":shortcut-add map=https://maps.google.com/maps?saddr=$0&daddr=$1"
-to search for a route, all but the last parameter must be quoted if they
-contain spaces like `:open map "city hall, London" railway station, London'
-.PD
-.RE
-.TP
-.BI ":shortcut-remove " "shortcut"
-Remove the search engine to the given \fIshortcut\fP.
-.TP
-.BI ":shortcut-default " "shortcut"
-Set the shortcut for given \fIshortcut\fP as the default.
-It doesn't matter if the \fIshortcut\fP is already in use or not
-to be able to set it.
-.SS Settings
-.TP
-.BI ":se[t] " var = value
-Set configuration values named by \fIvar\fP.
-To set boolean variable you should use 'on', 'off' or 'true' and 'false'.
-Colors are given as hexadecimal value like '#f57700'.
-.TP
-.BI ":se[t] " var += value
-Add the \fIvalue\fP to a number option, or append the \fIvalue\fP to a string
-option.
-When the option is a comma separated list, a comma is added, unless
-the value was empty.
-.TP
-.BI ":se[t] " var ^= value
-Multiply the \fIvalue\fP to a number option, or prepend the \fIvalue\fP to a
-string option.
-When the option is a comma separated list, a comma is added,
-unless the value was empty.
-.TP
-.BI ":se[t] " var -= value
-Subtract the \fIvalue\fP from a number option, or remove the \fIvalue\fP from
-a string option, if it is there.
-When the option is a comma separated list, a
-comma is deleted, unless the option becomes empty.
-.TP
-.BI ":se[t] " var ?
-Show the current set value of variable.
-.IR VAR .
-.TP
-.BI ":se[t] " var !
-Toggle the value of boolean variable \fIvar\fP and display the new set value.
-.SS Queue
-The queue allows the marking of URIs for later reading (something like a Read It Later
-list).
-This list is shared between the single instances of Vimb.
-Only available if Vimb has been compiled with the QUEUE feature.
-.TP
-.BI ":qpu[sh] [" URI ]
-Push \fIURI\fP or, if not given, the current URI to the end of the queue.
-.TP
-.BI ":qun[shift] [" URI ]
-Push \fIURI\fP or, if not given, the current URI to the beginning of the queue.
-.TP
-.B :qp[op]
-Open the oldest queue entry in the current browser window and remove it from the
-queue.
-.TP
-.B :qc[lear]
-Removes all entries from queue.
-.SS Automatic commands
-An autocommand is a command that is executed automatically in response to some
-event, such as a URI being opened.
-Autocommands are very powerful.
-Use them with care and they will help you avoid typing many commands.
-.PP
-Autocommands are built with following properties.
-.TP
-.I group
-When the [\fIgroup\fP] argument is not given, Vimb uses the current group as
-defined with ':augroup', otherwise, Vimb uses the group defined with
-[\fIgroup\fP].
-Groups are useful to remove multiple grouped autocommands.
-.TP
-.I event
-You can specify a comma separated list of event names.
-No white space can be used in this list.
-.RS
-.PP
-.PD 0
-Events:
-.TP
-.B LoadProvisional
-Fired if a new page is going to be opened.
-No data has been received yet, the load may still fail for transport issues.
-.TP
-.B LoadCommited
-Fired if first data chunk has arrived, meaning that the necessary transport
-requirements are established, and the load is being performed.
-This is the right event to toggle content related setting
-like 'scripts', 'plugins' and such things.
-.TP
-.B LoadFirstLayout
-fired if the first layout with actual visible content is shown.
-.TP
-.B LoadFinished
-Fires when everything that was required to display on the page has been loaded.
-.TP
-.B LoadFailed
-Fired when some error occurred during the page load that prevented it from
-being completed.
-.TP
-.B DownloadStart
-Fired right before a download is started.
-This is fired for Vimb downloads as well as external downloads
-if 'download-use-external' is enabled.
-.TP
-.B DownloadFinished
-Fired if a Vimb managed download is finished.
-For external download this event is not available.
-.TP
-.B DownloadFailed
-Fired if a Vimb managed download failed.
-For external download this event is not available.
-.PD
-.RE
-.TP
-.I pat
-Comma separated list of patterns, matches in order to check if a autocommand
-applies to the URI associated to an event.
-To use ',' within the single patterns this must be escaped as '\e,'.
-.RS
-.PP
-.PD 0
-Patterns:
-.IP "\fB*\fP"
-Matches any sequence of characters.
-This includes also '/' in contrast to shell patterns.
-.IP "\fB?\fP"
-Matches any single character except of '/'.
-.IP "\fB{one,two}\fP"
-Matches 'one' or 'two'.
-Any '{', ',' and '}' within this pattern must be escaped by a '\\'.
-\&'*' and '?' have no special meaning within the curly braces.
-.IP "\fB\e\fP"
-Use backslash to escape the special meaning of '?*{},' in the pattern or
-pattern list.
-.PD
-.RE
-.TP
-.I cmd
-Any `ex` command vimb understands.
-The leading ':' is not required.
-Multiple commands can be separated by '|'.
-.TP
-.BI ":au[tocmd] [" group "] {" event "} {" pat "} {" cmd "}"
-Add \fIcmd\fP to the list of commands that Vimb will execute automatically on
-\fIevent\fP for a URI matching \fIpat\fP autocmd-patterns.
-Vimb always adds the \fIcmd\fP after existing autocommands, so that the
-autocommands are executed in the order in which they were given.
-.TP
-.BI ":au[tocmd]! [" group "] {" event "} {" pat "} {" cmd "}"
-Remove all autocommands associated with \fIevent\fP and which pattern match
-\fIpat\fP, and add the command \fIcmd\fP.
-Note that the pattern is not matches literally to find autocommands
-to remove, like Vim does.
-Vimb matches the autocommand pattern with \fIpat\fP.
-If [\fIgroup\fP] is not given, deletes autocommands in current group,
-as noted above.
-.TP
-.BI ":au[tocmd]! [" group "] {" event "} {" pat "}"
-Remove all autocommands associated with \fIevent\fP and which pattern matches
-\fIpat\fP in given group (current group by default).
-.TP
-.BI ":au[tocmd]! [" group "] * {" pat "}"
-Remove all autocommands with patterns matching \fIpat\fP for all events
-in given group (current group by default).
-.TP
-.BI ":au[tocmd]! [" group "] {" event "}"
-Remove all autocommands for \fIevent\fP in given group (current group
-by default).
-.TP
-.BI ":au[tocmd]! [" group "]"
-Remove all autocommands in given group (current group by default).
-.TP
-.BI ":aug[roup] {" name "}"
-Define the autocmd group \fIname\fP for the following ":autocmd" commands.
-The name "end" selects the default group.
-.TP
-.BI ":aug[roup]! {" name "}"
-Delete the autocmd group \fIname\fP.
-.PP
-Example:
-.EX
-:aug github
-:  au LoadCommited * set scripts=off|set cookie-accept=never
-:  au LoadCommited http{s,}://github.com/* set scripts=on
-:aug end
-.EE
-.SS Misc
-.TP
-.BI ":sh[ellcmd] " cmd
-Runs the given shell \fIcmd\fP syncron and print the output into inputbox.
-The following patterns in \fIcmd\fP are expanded: '~username', '~/', '$VAR'
-and '${VAR}'.
-A '\\' before these patterns disables the expansion.
-.PP
-.RS
-.PP
-.PD 0
-The following environment variables are set for called shell commands.
-.TP
-.B VIMB_URI
-This variable is set by Vimb everytime a new page is opened to the URI of the
-page.
-.TP
-.B VIMB_TITLE
-Contains the title of the current opened page.
-.TP
-.B VIMB_PID
-Contains the pid of the running Vimb instance.
-.TP
-.B VIMB_SOCKET
-Holds the full path to the control socket, if Vimb is compiled with SOCKET
-feature and started with `--socket' option.
-.TP
-.B VIMB_XID
-Holds the X-Window id of the Vimb window or of the embedding window if Vimb is
-started with the -e option.
-.PD
-.PP
-Example:
-.EX
-:sh ls -l $HOME
-.EE
-.RE
-.TP
-.BI ":sh[ellcmd]! " cmd
-Like :sh[ellcmd], but asyncron.
-.sp
-Example:
-.EX
-:sh! /bin/sh -c 'echo "`date` $VIMB_URI" >> myhistory.txt'
-.EE
-.TP
-.BI ":s[ave] [" path "]"
-Download current opened page into configured download directory.
-If \fIpath\fP is given, download under this file name or path.
-\fIpath\fP is expanded and can therefore contain '~/', '${ENV}'
-and '~user' pattern.
-.TP
-.BI ":so[urce] [" file "]"
-Read ex commands from \fIfile\fP.
-.TP
-.B :q[uit]
-Close the browser.
-This will be refused if there are running downloads.
-.TP
-.B :q[uit]!
-Close the browser independent from an running download.
-.TP
-.B :reg[ister]
-Display the contents of all registers.
-.RS
-.PP
-.PD 0
-Registers:
-.TP
-.BR \(dqa " - " \(dqz
-26 named registers "a to "z.
-Vimb fills these registers only when you say so.
-.TP
-.B \(dq:
-Last executed `ex` command.
-.TP
-.B \(dq"
-Last yanked content.
-.TP
-.B \(dq%
-Curent opened URI.
-.TP
-.B \(dq/
-Last search phrase.
-.TP
-.B \(dq;
-Contains the last hinted URL.
-This can be used in `x-hint-command' to get the URL of the hint.
-.PD
-.RE
-.TP
-.BI :e[val] " javascript"
-Runs the given \fIjavascript\fP in the current page and display the evaluated
-value.
-.sp
-Example: :eval document.cookie
-.TP
-.BI :e[val]! " javascript"
-Like :eval, but there is nothing print to the input box.
-.TP
-.BI ":no[rmal] [" cmds ]
-Execute normal mode commands \fIcmds\fP.
-This makes it possible to execute normal mode commands typed on the input box.
-.sp
-\fIcmds\fP cannot start with a space.
-Put a count of 1 (one) before it, "1 " is one space.
-.sp
-Example: :set scripts!|no! R
-.TP
-.BI ":no[rmal]! [" cmds ]
-Like :normal, but no mapping is applied to \fIcmds\fP.
-.TP
-.B :ha[rdcopy]
-Print current document.
-Open a GUI dialog where you can select the printer,
-number of copies, orientation, etc.
-.SH INPUT MODE
-.TP
-.B <Esc>, CTRL\-[
-Switch back to normal mode.
-.TP
-.B CTRL\-O
-Executes the next command as normal mode command and return to input mode.
-.TP
-.B CTRL\-T
-Open configured $EDITOR with content of current form field.
-.TP
-.B CTRL\-V
-Pass the next key press directly to GTK.
-.TP
-.B CTRL\-Z
-Enter the pass-through mode.
-.SH COMPLETIONS
-The completions are triggered by pressing `<Tab>` or `<S-Tab>` in the
-activated inputbox.
-Depending of the current inserted content different completions are started.
-The completion takes additional typed chars to filter
-the completion list that is shown.
-.TP
-.B commands
-The completion for commands are started when at least `:` is shown in the
-inputbox.
-If initial chars are passed, the completion will lookup those
-commands that begin with the given chars.
-.TP
-.B settings
-The setting name completion is started if at least `:set ` is shown in
-inputbox and does also match settings that begins with already typed setting
-prefix.
-.TP
-.B history
-The history of URIs is shown for the `:open ` and `:tabopen ` commands.
-This completion looks up every given word in the history URI and titles.
-Only those history items are shown, where the title or URI contains all tags.
-.sp
-Example:
-.RS
-.PD 0
-.IP ":open foo bar<Tab>"
-will complete only URIs that contain the words foo and bar.
-.PD
-.RE
-.TP
-.B bookmarks
-The bookmark completion is similar to the history completion, but does match
-only the tags of the bookmarks.
-The bookmark completion ist started by `:open \fB!\fP`
-or `:tabopen \fB!\fP` and does a prefix search for all given words in
-the bookmark tags.
-.sp
-Example:
-.RS
-.PD 0
-.IP ":open \fB!\fPfoo ba"
-will match all bookmarks that have tags starting with "foo" and "ba".
-If the bookmark does not have any tags set, the URL is split on `.' and `/'
-into tags.
-.PD
-.RE
-.TP
-.B boomark tags
-The boomark tag completion allows the insertion of already used bookmarks for the
-`:bma ` commands.
-.TP
-.B search
-The search completion allows a filtered list of already done searches.
-This completion starts by `/` or `?` in inputbox and performs a prefix
-comparison for further typed chars.
-.SH SETTINGS
-All settings listed below can be set with the `:set' command.
-.SS WebKit-Settings
-.TP
-.B accelerated-compositing (bool)
-Enable or disable support for accelerated compositing on pages.
-Accelerated compositing uses the GPU to render animations on pages
-smoothly and also allows proper rendering of 3D CSS transforms.
-.TP
-.B auto-load-images (bool)
-Load images automatically.
-.TP
-.B auto-resize-window (bool)
-Indicates if Vimb will honor size and position changes of the window by various
-DOM methods.
-.TP
-.B auto-shrink-images (bool)
-Automatically shrink standalone images to fit.
-.TP
-.B caret (bool)
-Whether to enable accessibility enhanced keyboard navigation.
-.TP
-.B closed-max-items (int)
-Maximum number of stored last closed browser windows. If closed-max-items is
-set to 0, closed browser windows will not be stored.
-.TP
-.B cursivfont (string)
-The font family used as the default for content using cursive font.
-.TP
-.B defaultencoding (string)
-The default text charset used when interpreting content with an unspecified
-charset.
-.TP
-.B defaultfont (string)
-The font family to use as the default for content that does not specify a
-font.
-.TP
-.B dns-prefetching (bool)
-Indicates if Vimb prefetches domain names.
-.TP
-.B dom-paste (bool)
-Whether to enable DOM paste.
-If set to TRUE, document.execCommand("Paste")
-will correctly execute and paste content of the clipboard.
-.TP
-.B file-access-from-file-uris (bool)
-Boolean property to control file access for file:// URIs.
-If this option is enabled every file:// will have its own security
-unique domain.
-.TP
-.B fontsize (int)
-The default font size used to display text.
-.TP
-.B frame-flattening (bool)
-Whether to enable the Frame Flattening.
-With this setting each subframe is expanded to its contents,
-which will flatten all the frames to become one scrollable page.
-.TP
-.B html5-database (bool)
-Whether to enable HTML5 client-side SQL database support.
-Client-side SQL database allows web pages to store structured data
-and be able to use SQL to manipulate that data asynchronously.
-.TP
-.B html5-local-storage (bool)
-Whether to enable HTML5 localStorage support.
-localStorage provides simple synchronous storage access.
-.TP
-.B hyperlink-auditing (bool)
-Enable or disable support for <a ping>.
-.TP
-.B images (bool)
-Determines whether images should be automatically loaded or not.
-.TP
-.B insecure-content-show (bool)
-Whether pages loaded via HTTPS should load subresources such as images and
-frames from non-HTTPS URIs.
-Only for webkit>=2.0.
-.TP
-.B insecure-content-run (bool)
-Whether pages loaded via HTTPS should run subresources such as CSS, scripts,
-and plugins from non-HTTPS URIs.
-Only for webkit>=2.0.
-.TP
-.B java-applet (bool)
-Enable or disable support for the Java <applet> tag.
-Keep in mind that Java content can be still shown in the page
-through <object> or <embed>, which are the preferred tags for this task.
-.TP
-.B javascript-can-access-clipboard (bool)
-Whether JavaScript can access the clipboard.
-.TP
-.B javascript-can-open-windows-automatically (bool)
-Whether JavaScript can open popup windows automatically without user
-intervention.
-.TP
-.B media-playback-allows-inline (bool)
-Whether media playback is full-screen only or inline playback is allowed.
-Setting it to false allows specifying that media playback should be always
-fullscreen.
-.TP
-.B media-playback-requires-user-gesture (bool)
-Whether a user gesture (such as clicking the play button) would be required to
-start media playback or load media.
-Setting it on requires a gesture by the
-user to start playback, or to load the media.
-.TP
-.B media-stream (bool)
-Enable or disable support for MediaSource on pages.
-MediaSource is an experimental proposal which extends HTMLMediaElement
-to allow JavaScript to generate media streams for playback.
-.TP
-.B mediasource (bool)
-Enable or disable support for MediaSource on pages.
-MediaSource is an experimental proposal which extends HTMLMediaElement
-to allow JavaScript to generate media streams for playback.
-.TP
-.B minimumfontsize (int)
-The minimum font size used to display text.
-.TP
-.B monofont (string)
-The font family used as the default for content using monospace font.
-.TP
-.B monofontsize (int)
-Default font size for the monospace font.
-.TP
-.B offlinecache (bool)
-Whether to enable HTML5 offline web application cache support.
-Offline web application cache allows web applications to run even
-when the user is not connected to the network.
-.TP
-.B pagecache (bool)
-Enable or disable the page cache.
-Disabling the page cache is generally only useful for special
-circumstances like low-memory scenarios or special purpose
-applications like static HTML viewers.
-.TP
-.B print-backgrounds (bool)
-Whether background images should be printed.
-.TP
-.B private-browsing (bool)
-Whether to enable private browsing mode.
-This suppresses  printing of messages into JavaScript Console.
-At the time this is the only way to force WebKit to
-not allow a page to store data in the windows sessionStorage.
-.TP
-.B plugins (bool)
-Determines whether or not plugins on the page are enabled.
-.TP
-.B print-backgrounds (bool)
-Whether background images should be drawn during printing.
-.TP
-.B resizable-text-areas (bool)
-Whether text areas are resizable.
-.TP
-.B respect-image-orientation (bool)
-Whether Vimb should respect image orientation.
-.TP
-.B sansfont (string)
-The font family used as the default for content using sans-serif font.
-.TP
-.B scripts (bool)
-Determines whether or not JavaScript executes within a page.
-.TP
-.B seriffont (string)
-The font family used as the default for content using serif font.
-.TP
-.B site-specific-quirks (bool)
-Enables the site-specific compatibility workarounds.
-.TP
-.B smooth-scrolling (bool)
-Enable or disable support for smooth scrolling.
-.TP
-.B spacial-navigation (bool)
-Whether to enable the Spatial Navigation.
-This feature consists in the ability to navigate between focusable
-elements in a Web page, such as hyperlinks and form controls, by using
-Left, Right, Up and Down arrow keys.
-For example, if
-a user presses the Right key, heuristics determine whether there is an
-element they might be trying to reach towards the right, and if there are
-multiple elements, which element they probably want.
-.TP
-.B spell-checking (bool)
-Whether to enable spell checking while typing.
-.TP
-.B spell-checking-languages (string)
-The languages to be used for spell checking, separated by commas.
-.sp
-The locale string typically is in the form lang_COUNTRY, where lang is an
-ISO-639 language code, and COUNTRY is an ISO-3166 country code.
-For instance, sv_FI for Swedish as written in Finland or pt_BR
-for Portuguese as written in Brazil.
-.sp
-If no value is specified the default value for GTK is used.
-.TP
-.B tab-key-cycles-through-elements (bool)
-Whether the Tab key cycles through elements on the page.
-.sp
-If true, pressing the Tab key will focus the next element in the web view.
-Otherwise, the web view will interpret Tab key presses as normal key presses.
-If the selected element is editable, the Tab key will cause the insertion
-of a Tab character.
-.TP
-.B universal-access-from-file-uris (bool)
-Whether to allow files loaded through file:// URIs universal access to all
-pages.
-.TP
-.B useragent (string)
-The user-agent string used by WebKit.
-.TP
-.B webaudio (bool)
-Enable or disable support for WebAudio on pages.
-WebAudio is an experimental proposal for allowing web pages
-to generate Audio WAVE data from JavaScript.
-.TP
-.B webgl (bool)
-Enable or disable support for WebGL on pages.
-.TP
-.B webinspector (bool)
-Determines whether or not developer tools, such as the Web Inspector, are
-enabled.
-.TP
-.B xssauditor (bool)
-Whether to enable the XSS auditor.
-This feature filters some kinds of reflective XSS attacks
-on vulnerable web sites.
-.SS Vimb-Settings
-.TP
-.B auto-response-header (list)
-Prepend HTTP-Header to responses received from server, based on pattern
-matching.
-The purpose of this setting is to enforce some security setting in the client.
-For example, you could set Content-Security-Policy (see
-`http://www.w3.org/TR/CSP/') for implement a whitelist policy, or set
-Strict-Transport-Security for server that don't provide this header whereas
-they propose https website.
-.sp
-Note that this setting will not replace existing headers, but add a new one.
-If multiple patterns match a requested URI, the last matched rule will be
-applied.
-You could also specified differents headers for the same pattern.
-.sp
-The format is a list of `pattern header-list`.
-If `header-list` has not than one element, enclosing with QUOTE
-is mandatory: `"pattern header-list"`.
-The header-list format is the same as `header` setting.
-.RS
-.PP
-Example:
-.PD 0
-.IP ":set auto-response-header=* Content-security-policy=default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'none'"
-.IP ":set auto-response-header+=https://example.com/* Content-security-policy=default-src 'self' https://*.example.com/"
-.IP ":set auto-response-header+=https://example.com/* Strict-Transport-Security=max-age=31536000"
-.IP ":set auto-response-header+=""https://*.example.org/sub/* Content-security-policy,X-Test=ok"""
-.PD
-.RE
-.TP
-.B ca-bundle (string)
-The path to the .crt file for the certificate validation.
-The given path is expanded with standard file expansion.
-.TP
-.B completion-bg-active (color)
-Background color for selected completion item.
-.TP
-.B completion-bg-normal (color)
-Background color for non-selected completion items.
-.TP
-.B completion-fg-active (color)
-Foreground color for the selected completion item.
-.TP
-.B completion-fg-normal (color)
-Foreground color for the non-selected completion items.
-.TP
-.B completion-font (string)
-Font used for the completion items.
-.TP
-.B cookie-accept (string)
-Cookie accept policy {`always', `never', `origin' (accept all non-third-party
-cookies)}.
-.TP
-.B cookie-timeout (int)
-Cookie timeout in seconds.
-.TP
-.B cookie-expire-time (int)
-Enforce expire-time on cookies.
-The default value `-1' keep expire-time as defined by server side.
-The value `0' convert all cookies as session-only cookies
-(`cookie-timeout' setting is used as for any other session-cookie).
-Any other value enforce the expire-time (the expire-time value will be the
-lower between server-side request and time defined with `cookie-expire-time').
-.TP
-.B download-command (string)
-A command with placeholder '%s' that will be invoked to download a URI.
-.RS
-.TP
-The following additional environment variable are available:
-.PD 0
-.TP
-.B $VIMB_URI
-The URI of the current opened page, normally the page where the download was
-started from, also known as referer.
-.TP
-.B $VIMB_FILE
-The target file that is calculated by Vimb according to the `download-path'.
-Note that this file might already exists, so it's strongly recommended to
-check the path in this variable before usage.
-.TP
-.B $VIMB_COOKIES
-Path to the cookie file Vimb uses.
-This is only available if Vimb is compiled with the COOKIE feature.
-.TP
-.B $VIMB_USER_AGENT
-Holds the user agent string that Vimb uses.
-.TP
-.B $VIMB_MIME_TYPE
-The mime-type of the download.
-This variable is only available when the server sent the mime-type header
-with the response and only if the download was not start by the `:save'
-command or the `;s' hinting.
-.TP
-.B $VIMB_USE_PROXY
-Indicates if the proxy is enabled in Vimb.
-If enable this variable is `1', otherwise `0'.
-Note that this variable gives no hint if the proxy settings
-apply to the URL to be downloaded, only if proxy is enabled in general.
-.PD
-.PP
-Example:
-.PD 0
-.IP ":set download-command=/bin/sh -c\
- ""wget -c %s -O $VIMB_FILE --load-cookies $VIMB_COOKIES"""
-.PD
-.RE
-.TP
-.B download-path (string)
-Path to the default download directory.
-If the directory is not set download will be written into current directory.
-The following pattern will be expanded if the download
-is started '~/', '~user', '$VAR' and '${VAR}'.
-.TP
-.B download-use-external (bool)
-Indicates if the external download tool set as `download-command' should be
-used to handle downloads.
-If this is disabled Vimb will handle the download.
-.TP
-.B editor-command (string)
-Command with placeholder '%s' called if form filler is opened with $EDITOR to
-spawn the editor-like `x-terminal-emulator -e vi %s'.
-To use Gvim as the editor, it's necessary to call it with `-f' to run it
-in the foreground.
-.TP
-.B fullscreen (bool)
-Show the current window full-screen.
-.TP
-.B header (list)
-Comma separated list of headers that replaces default header sent by WebKit or
-new headers.
-The format for the header list elements is `name[=[value]]'.
-.sp
-Note that these headers will replace already existing headers.
-If there is no '=' after the header name, then the complete header
-will be removed from the request, if the '=' is present means that
-the header value is set to empty value.
-.sp
-To use '=' within a header value the value must be quoted like shown in
-Example for the Cookie header.
-.RS
-.PP
-Example:
-.PD 0
-.IP ":set header=DNT=1,User-Agent,Cookie='name=value'"
-Send the 'Do Not Track' header with each request and remove the User-Agent
-Header completely from request.
-.PD
-.RE
-.TP
-.B hint-follow-last (bool)
-If on, vimb automatically follows the last remaining hint on the page.
-If off hints are fired only if enter is pressed.
-.TP
-.B hint-number-same-length (bool)
-If on, all hint numbers will have the same length, so no hints will be
-ambiguous.
-.TP
-.B hint-timeout (int)
-Timeout before automatically following a non-unique numerical hint.
-To disable auto fire of hints, set this value to 0.
-.TP
-.B hintkeys (string)
-The keys used to label and select hints.
-With its default value, each hint has a unique number which can be typed
-to select it, while all other characters are used to filter hints based
-on their text.
-With a value such as asdfg;lkjh,
-each hint is `numbered' based on the characters of the home row.
-Note that the hint matching by label built of hintkeys is case sensitive.
-In this vimb differs from some other browsers that show hint labels in upper
-case, but match them lowercase.
-.RS
-To have upper case hint labels, it's possible to add following css to the
-`style.css' file in vimb's configuration directory.
-.IP "._hintLabel {text-transform: uppercase !important;}"
-.RE
-.TP
-.B history-max-items (int)
-Maximum number of unique items stored in search-, command or URI history.
-If history-max-items is set to 0, the history file will not be changed.
-.TP
-.B home-page (string)
-Homepage that vimb opens if started without a URI.
-.TP
-.B hsts (bool)
-Enable or disables the HSTS (HTTP Strict Transport Security) feature.
-.TP
-.B input-autohide (bool)
-If enabled the inputbox will be hidden whenever it contains no text.
-.TP
-.B input-bg-error (color)
-Background color for the inputbox if error is shown.
-.TP
-.B input-bg-normal (color)
-Background color of the inputbox.
-.TP
-.B input-fg-error (color)
-Foreground color of inputbox if error is shown.
-.TP
-.B input-fg-normal (color)
-Foreground color of inputbox.
-.TP
-.B input-font-error (string)
-Font user in inputbox if error is shown.
-.TP
-.B input-font-normal (string)
-Font used for inputbox.
-.TP
-.B nextpattern (list)
-Patterns to use when guessing the next page in a document.
-Each pattern is successively tested against each link in the page
-beginning from the last link.
-Default
-"/\\bnext\\b/i,/^(>|>>|»)$/,/^(>|>>|»)/,/(>|>>|»)$/,/\\bmore\\b/i".
-Note that you have to escape the '|' as '\\|' else the '|' will terminate
-the :set command and start a new command.
-.TP
-.B maximum-cache-size (int)
-Size in kB used to cache various page data.
-This caching is independent from `pagecache' or `offlinecache'.
-To disable caching, the size could be set to '0'.
-.TP
-.B previouspattern (list)
-Patterns to use when guessing the previous page in a document.
-Each pattern is successively tested against each link in the page
-beginning from the last link.
-Default "/\\bnext\\b/i,/^(>|>>|»)$/,/^(>|>>|»)/,/(>|>>|»)$/,/\\bmore\\b/i"
-.TP
-.B proxy (bool)
-Indicates if the environment variable `http_proxy' is evaluated.
-.TP
-.B scrollstep (int)
-Number of pixel vimb scrolls if 'j' or 'k' is used.
-.TP
-.B statusbar (bool)
-Indicates if the statusbar should be shown.
-.TP
-.B status-color-bg (color)
-Background color of the statusbar.
-.TP
-.B status-color-fg (color)
-Foreground color of the statusbar.
-.TP
-.B status-font (string)
-Font used in statusbar.
-.TP
-.B status-ssl-color-bg (color)
-Background color of statusbar if current page uses trusted https certificate.
-.TP
-.B status-ssl-color-fg (color)
-Foreground color for statusbar for https pages.
-.TP
-.B status-ssl-font (string)
-Statusbar font for https pages.
-.TP
-.B status-sslinvalid-color-bg (color)
-Background color of the statusbar if the certificate if the https page isn't
-trusted.
-.TP
-.B status-sslinvalid-color-fg (color)
-Foreground of statusbar for untrusted https pages.
-.TP
-.B status-sslinvalid-font (string)
-Statusbar font for untrusted https pages.
-.TP
-.B strict-focus (bool)
-Vimb checks if an editable element is focused and switch to input mode.
-If strict-focus is enabled, this isn't done for focused element on page load
-(without user interaction), instead the focus is removed from the focused
-element.
-Focus changed that appear after the page was completely loaded are
-not affected by this setting.
-.TP
-.B strict-ssl (bool)
-If 'on', vimb will not load a untrusted https site.
-.TP
-.B stylesheet (bool)
-If 'on' the user defined styles-sheet is used.
-.TP
-.B timeoutlen (int)
-The time in milliseconds that is waited for a key code or mapped key sequence
-to complete.
-.TP
-.B x-hint-command (string)
-Command used if hint mode ;x is fired.
-The command can be any vimb command string.
-Note that the command is run through the mapping mechanism of vimb so
-it might change the behaviour by adding or changing mappings.
-.RS
-.P
-.PD 0
-.IP ":set x-hint-command=50G"
-Not really useful.
-If the hint is fired, scroll to the middle of the page.
-.IP ":set x-hint-command=:sh! curl -e <C-R>% <C-R>;"
-This fills the inputbox with the prefilled download command and replaces
-`<C-R>%' with the current URI and `<C-R>;' with the URI of the hinted element.
-.PD
-.RE
-.SH FILES
-.TP
-.I $XDG_CONFIG_HOME/vimb/
-Default directory for configuration data.
-.RS
-.PD 0
-.TP
-.I config
-Configuration file to set WebKit setting, some GUI styles and keybindings.
-.TP
-.I cookies
-Cookie store file.
-.TP
-.I closed
-Holds the URI of last closed browser windows.
-.TP
-.I history
-This file holds the history of unique opened URIs.
-.TP
-.I command
-This file holds the history of commands and search queries performed via input
-box.
-.TP
-.I search
-This file holds the history of search queries.
-.TP
-.I bookmark
-Holds the bookmarks saved with command `bma'.
-The records are stored there as
-.br
-`URI<tab>title<tab>space separated tags' or as
-.br
-`URI<tab><tab>tags` if there is no title.
-.TP
-.I queue
-Holds the Read It Later queue filled by `qpush' if
-Vimb has been compiled with QUEUE feature.
-.TP
-.I hsts
-Holds the known hosts hosts if Vimb is compiled with HTTP strict transport
-security feature.
-.TP
-.I scripts.js
-This file can be used to run user scripts, that are injected into every paged
-that is opened.
-.TP
-.I style.css
-File for userdefined CSS styles.
-These file is used if the config variable `stylesheet' is enabled.
-.PD
-.RE
-.TP
-.I $XDG_CONFIG_HOME/vimb/PROFILE-NAME
-Directory for configuration data if executed with \fB-p \fIPROFILE-NAME\fR parameter. It has same structure as default directory for configuration data.
-.TP
-.I $XDG_CACHE_HOME/vimb/
-Default directory for cache data.
-.TP
-.I $XDG_CACHE_HOME/vimb/PROFILE-NAME
-Directory for cache data if executed with \fB-p \fIPROFILE-NAME\fR parameter.
-.TP
-.I $XDG_RUNTIME_DIR/vimb/socket/
-Directory where the control sockets are placed.
-.TP
-.I $XDG_RUNTIME_DIR/vimb/PROFILE-NAME/socket/
-Directory where the control sockets are placed if executed with \fB-p \fIPROFILE-NAME\fR parameter.
-.PP
-There are also some sample scripts installed together with Vimb under
-PREFIX/share/vimb/examples.
-.SH ENVIRONMENT
-.TP
-.B http_proxy
-If this variable is set to an non-empty value, and the configuration option
-`proxy' is enabled, this will be used as HTTP proxy.
-If the proxy URL has no scheme set, HTTP is assumed.
-.TP
-.B no_proxy
-A comma separated list of domains and/or IPs which should not be proxied.
-Note that an IPv6 address must appear in brackets if used with a port,
-for example "[::1]:443".
-.IP
-Example: "localhost,127.0.0.1,::1,fc00::/7,example.com:8080"
-.SH "REPORTING BUGS"
-Report bugs to the main project page on https://github.com/fanglingsu/vimb/issues
-.br
-or on the mailing list https://lists.sourceforge.net/lists/listinfo/vimb-users.
-.SH AUTHOR
-Daniel Carl
diff --git a/examples/formfiller/formfiller b/examples/formfiller/formfiller
deleted file mode 100755 (executable)
index 05edd49..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env bash
-# Formfiller that reads credentials from file and let vimb fill execute a
-# JavaScript method to fill in the form data. Call this from vimb by ':sh!
-# formfiller %'
-#
-# The form data are stored in $VIMB_KEY_DIR or as fallback
-# $XDG_CONFIG_HOME/vimb/keys. The files must be names as
-# [prefix]{domain}.gpg or [prefix]{domain}. The files must contain a valid
-# JavaScript array that can be used for the _vbform.fill() method.
-#
-# A unencrypted sample file could look like this:
-# ["input[name='user']:daniel", "input[name='password']:p45w0rD"]
-
-# dmenu command use in case multiple files are found for current domain
-DMENU="dmenu -l 7"
-
-if [[ -z "$XDG_CONFIG_HOME" ]]; then
-    XDG_CONFIG_HOME=$HOME/.config
-fi
-
-VIMB_KEY_DIR=${VIMB_KEY_DIR:-"$XDG_CONFIG_HOME/vimb/keys"}
-uri=$1
-
-die() {
-    echo "$1" >&2
-    exit 1
-}
-
-fillform() {
-    local path=$1
-    local data=""
-    case "$path" in
-        *.gpg )
-            # this requires the gpg-agent to contains already the key
-            data=$(gpg --batch -qd "$path")
-            # abort here if the file could not be decrypted
-            if [ $? -gt 0 ]; then
-                exit 1
-            fi
-            ;;
-        * )
-            data=$(cat "$path")
-            ;;
-    esac
-    # make sure we are in normal mode and fill in the form data
-    # use :: to not save the secrets into vimb command history or into the
-    # last ex command register ":
-    echo "<Esc>::e! _vbform.fill($data);<CR>" | socat - UNIX-CONNECT:$VIMB_SOCKET
-}
-
-# check if uri is given
-if [ -z "$uri" ]; then
-    die 'No URI given'
-fi
-# check if the script is run from vimb with socket support enabled
-if [ -z "$VIMB_SOCKET" ] || [ ! -S "$VIMB_SOCKET" ]; then
-    die 'This script must be run from vimb with socket support'
-fi
-
-# extract the domain part without ports from given uri
-domain=$(echo "$uri" | sed -r 's@https?://([^:/]+).*@\1@')
-
-# find matching data files prefix${domain}{,.gpg}
-files=($(find "$VIMB_KEY_DIR" -name "*$domain" -o -name "*${domain}.gpg"))
-# strip of the key dir
-files=("${files[@]#"$VIMB_KEY_DIR"/}")
-
-# if only one matchin data file found - use this direct
-if [ ${#files[@]} -eq 1 ]; then
-    fillform "$VIMB_KEY_DIR/${files[0]}"
-    exit 1
-else
-    # else allow to select the right one via dmenu
-    match=$(printf '%s\n' "${files[@]}" | sort | $DMENU)
-    if [ -n $match ]; then
-        fillform "$VIMB_KEY_DIR/$match"
-    fi
-fi
diff --git a/examples/formfiller/scripts.js b/examples/formfiller/scripts.js
deleted file mode 100644 (file)
index 8357409..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Script to insert various data into form fields.
- * Given data is an array of "Selector:Value" tupel.
- * ["selector:value","...:..."]
- * Example call from within vimb:
- * ::e! _vbform.fill(["input[name='login']:daniel","input[name='password']:p45w0rD"]);
- *
- * Note the double ':' in front, this tells vimb not to write the command into
- * history or the last excmd register ":. */
-"use strict"
-var _vbform = {
-    fill: function(data) {
-        data = data||[];
-        var e, i, k, s;
-
-        // iterate over data array and try to find the elements
-        for (i = 0; i < data.length; i++) {
-            // split into selector and value part
-            s = data[i].split(":");
-            e = document.querySelectorAll(s[0]);
-            for (k = 0; k < e.length; k++) {
-                // fill the form fields
-                this.set(e[k], s[1]);
-            }
-        }
-    },
-
-    // set the value for the form element
-    set: function(e, v) {
-        var i, t, n = e.nodeName.toLowerCase();
-
-        if (n == "input") {
-            t = e.type;
-            if (t == "checkbox" || t == "radio") {
-                e.checked = ("1" == v);
-            } else if (t == "submit") {
-                e.click();
-            } else {
-                e.value = v;
-            }
-        } else if (n == "textarea") {
-            e.value = v;
-        } else if (n == "select") {
-            for (i = 0; i < e.options.length; i++) {
-                if (e.options[i].value == v) {
-                    e.options[i].selected = "selected";
-                }
-            }
-        }
-    }
-};
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..ca46d8a
--- /dev/null
@@ -0,0 +1,2 @@
+config.h
+vimb
index b72d7c9..e4e2fae 100644 (file)
@@ -1,63 +1,27 @@
 BASEDIR=..
 include $(BASEDIR)/config.mk
 
-OBJ  = $(patsubst %.c, %.o, $(wildcard *.c))
-LOBJ = $(patsubst %.c, %.lo, $(wildcard *.c))
+OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
 
-all: $(TARGET)
+all: vimb
 
-clean: clean-lib
-       $(RM) $(TARGET) *.o *.lo hints.js.h
+clean:
+       $(RM) vimb *.o
 
-clean-lib:
-       $(RM) $(LIBTARGET)
-
-hints.o:  hints.js.h
-hints.lo: hints.js.h
-
-hints.js.h: hints.js
-       @echo "minify $<"
-       @cat $< | ./js2h.sh > $@
-
-$(OBJ):  config.h $(BASEDIR)/config.mk
-$(LOBJ): config.h $(BASEDIR)/config.mk
-
-$(TARGET): $(OBJ)
-ifeq ($(VERBOSE),0)
+vimb: $(OBJ)
        @echo "$(CC) $@"
-       @$(CC) $(OBJ) -o $@ $(LDFLAGS)
-else
-       $(CC) $(OBJ) -o $@ $(LDFLAGS)
-endif
+       @$(CC) $(LDFLAGS) $(OBJ) -o $@
 
-$(LIBTARGET): $(LOBJ)
-ifeq ($(VERBOSE),0)
-       @echo "$(CC) $@"
-       @$(CC) -shared ${LOBJ} -o $@ $(LDFLAGS)
-else
-       $(CC) -shared ${LOBJ} -o $@ $(LDFLAGS)
-endif
+$(OBJ): config.h $(BASEDIR)/config.mk
+
+-include $(OBJ:.o=.d)
 
 config.h:
        @echo create $@ from config.def.h
        @cp config.def.h $@
 
-%.o: %.c %.h
-ifeq ($(VERBOSE),0)
+%.o: %.c
        @echo "${CC} $@"
        @$(CC) $(CFLAGS) -c -o $@ $<
-else
-       $(CC) $(CFLAGS) -c -o $@ $<
-endif
-
-%.lo: %.c %.h
-ifeq ($(VERBOSE),0)
-       @echo "${CC} $@"
-       @$(CC) -DTESTLIB $(CFLAGS) -fPIC -c -o $@ $<
-else
-       $(CC) -DTESTLIB $(CFLAGS) -fPIC -c -o $@ $<
-endif
-
--include $(OBJ:.o=.d)
 
-.PHONY: all clean clean-lib
+.PHONY: all clean
diff --git a/src/arh.c b/src/arh.c
deleted file mode 100644 (file)
index 6fd0f8e..0000000
--- a/src/arh.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- * Copyright (C) 2014 Sébastien Marie
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_ARH
-#include "ascii.h"
-#include "arh.h"
-#include "util.h"
-
-typedef struct {
-    char       *pattern; /* pattern the uri is matched against */
-    GHashTable *headers; /* header list */
-} MatchARH;
-
-static void marh_free(MatchARH *);
-static char *read_pattern(char **);
-
-
-/**
- * parse the data string to a list of MatchARH
- *
- * pattern name=value[,...]
- */
-GSList *arh_parse(const char *data, const char **error)
-{
-    GSList *parsed = NULL;
-    GSList *data_list = NULL;
-
-    /* parse data as comma separated list
-     * of "pattern1 HEADERS-GROUP1","pattern2 HEADERS-GROUP2",... */
-    data_list = soup_header_parse_list(data);
-
-    /* iter the list for parsing each elements */
-    for (GSList *l = data_list; l; l = g_slist_next(l)) {
-        char *one_data = (char *)l->data;
-        char *pattern;
-        GHashTable *headers;
-
-        /* remove QUOTE around one_data if any */
-        if (one_data && *one_data == '"') {
-            int last = strlen(one_data) - 1;
-            if (last >= 0 && one_data[last] == '"') {
-                one_data[last] = '\0';
-                one_data++;
-            }
-        }
-
-        /* read pattern and parse headers */
-        pattern = read_pattern(&one_data);
-        headers = soup_header_parse_param_list(one_data);
-
-        /* check result (need a pattern and at least one header) */
-        if (pattern && g_hash_table_size(headers)) {
-            MatchARH *marh = g_slice_new0(MatchARH);
-            marh->pattern = g_strdup(pattern);
-            marh->headers = headers;
-
-            parsed = g_slist_append(parsed, marh);
-
-#ifdef DEBUG
-            PRINT_DEBUG("append pattern='%s' headers[%d]='0x%lx'",
-                   marh->pattern, g_hash_table_size(marh->headers), (long)marh->headers);
-            GHashTableIter iterHeaders;
-            const char *name, *value;
-            g_hash_table_iter_init(&iterHeaders, headers);
-            while (g_hash_table_iter_next(&iterHeaders, (gpointer)&name, (gpointer)&value)) {
-                PRINT_DEBUG(" %s=%s", name, value);
-            }
-#endif
-        } else {
-            /* an error occurs: cleanup */
-            soup_header_free_param_list(headers);
-            soup_header_free_list(data_list);
-            arh_free(parsed);
-
-            /* set error if asked */
-            if (error != NULL) {
-                *error = "syntax error";
-            }
-
-            return NULL;
-        }
-    }
-
-    soup_header_free_list(data_list);
-
-    return parsed;
-}
-
-/**
- * free the list of MatchARH
- */
-void arh_free(GSList *list)
-{
-    g_slist_free_full(list, (GDestroyNotify)marh_free);
-}
-
-/**
- * append to reponse-header of SoupMessage,
- * the header that match the specified uri
- */
-void arh_run(GSList *list, const char *uri, SoupMessage *msg)
-{
-    PRINT_DEBUG("uri: %s", uri);
-
-    /* walk thought the list */
-    for (GSList *l=list; l; l = g_slist_next(l)) {
-        MatchARH *marh = (MatchARH *)l->data;
-
-        if (util_wildmatch(marh->pattern, uri)) {
-            GHashTableIter iter;
-            const char *name  = NULL;
-            const char *value = NULL;
-
-            g_hash_table_iter_init(&iter, marh->headers);
-            while (g_hash_table_iter_next(&iter, (gpointer)&name, (gpointer)&value)) {
-                if (value) {
-                    /* the pattern match, so replace headers
-                     * as side-effect, for one header the last matched will be keeped */
-                    soup_message_headers_replace(msg->response_headers, name, value);
-
-                    PRINT_DEBUG(" header added: %s: %s", name, value);
-                } else {
-                    /* remove a previously setted auto-reponse-header */
-                    soup_message_headers_remove(msg->response_headers, name);
-
-                    PRINT_DEBUG(" header removed: %s", name);
-                }
-            }
-        }
-    }
-}
-
-/**
- * free a MatchARH
- */
-static void marh_free(MatchARH *marh)
-{
-    if (marh) {
-        g_free(marh->pattern);
-        soup_header_free_param_list(marh->headers);
-
-        g_slice_free(MatchARH, marh);
-    }
-}
-
-/**
- * read until ' ' (or any other SPACE)
- */
-static char *read_pattern(char **line)
-{
-    char *pattern;
-
-    if (!*line || !**line) {
-        return NULL;
-    }
-
-    /* remember where the pattern starts */
-    pattern = *line;
-
-    /* move pointer to the end of the pattern (or end-of-line) */
-    while (**line && !VB_IS_SPACE(**line)) {
-        (*line)++;
-    }
-
-    if (**line) {
-        /* end the pattern */
-        *(*line)++ = '\0';
-
-        /* skip trailing whitespace */
-        while (VB_IS_SPACE(**line)) {
-            (*line)++;
-        }
-
-        return pattern;
-
-    } else {
-        /* end-of-line was encounter */
-        return NULL;
-    }
-}
-#endif
diff --git a/src/arh.h b/src/arh.h
deleted file mode 100644 (file)
index d235b36..0000000
--- a/src/arh.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- * Copyright (C) 2014 Sébastien Marie
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_ARH
-
-#ifndef _ARH_H
-#define _ARH_H
-
-#include "main.h"
-
-GSList *arh_parse(const char *, const char **);
-void    arh_free(GSList *);
-void    arh_run(GSList *, const char *, SoupMessage *);
-
-#endif /* end of include guard: _ARH_H */
-#endif
index a5234e1..f375a30 100644 (file)
@@ -73,11 +73,11 @@ static const unsigned char chartable[256] = {
 
 /* CSI (control sequence introducer) is the first byte of a control sequence
  * and is always followed by two bytes. */
-#define CSI         0x80
-#define CSI_STR     "\x80"
+#define CSI                 0x80
+#define CSI_STR             "\x80"
 
 /* get internal representation for conrol character ^C */
-#define CTRL(c)     ((c) ^ 0x40)
+#define CTRL(c)             ((c) ^ 0x40)
 
 #define IS_SPECIAL(c)       (c < 0)
 
@@ -95,7 +95,6 @@ static const unsigned char chartable[256] = {
 #define KEY_DOWN        TERMCAP2KEY('k', 'd')
 #define KEY_LEFT        TERMCAP2KEY('k', 'l')
 #define KEY_RIGHT       TERMCAP2KEY('k', 'r')
-
 #define KEY_F1          TERMCAP2KEY('k', '1')
 #define KEY_F2          TERMCAP2KEY('k', '2')
 #define KEY_F3          TERMCAP2KEY('k', '3')
diff --git a/src/autocmd.c b/src/autocmd.c
deleted file mode 100644 (file)
index e856a4c..0000000
+++ /dev/null
@@ -1,488 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_AUTOCMD
-#include "autocmd.h"
-#include "ascii.h"
-#include "ex.h"
-#include "util.h"
-#include "completion.h"
-
-typedef struct {
-    guint bits;     /* the bits identify the events the command applies to */
-    char *excmd;    /* ex command string to be run on matches event */
-    char *pattern;  /* list of patterns the uri is matched agains */
-} AutoCmd;
-
-typedef struct {
-    char    *name;
-    GSList  *cmds;
-} AuGroup;
-
-static struct {
-    const char *name;
-    guint      bits;
-} events[] = {
-    {"*",                0x00ff},
-    {"LoadProvisional",  0x0001},
-    {"LoadCommited",     0x0002},
-    {"LoadFirstLayout",  0x0004},
-    {"LoadFinished",     0x0008},
-    {"LoadFailed",       0x0010},
-    {"DownloadStart",    0x0020},
-    {"DownloadFinished", 0x0040},
-    {"DownloadFailed",   0x0080},
-};
-
-extern VbCore vb;
-
-static AuGroup *curgroup = NULL;
-static GSList  *groups   = NULL;
-static guint   usedbits  = 0;       /* holds all used event bits */
-
-static GSList *get_group(const char *name);
-static guint get_event_bits(const char *name);
-static void rebuild_used_bits(void);
-static char *get_next_word(char **line);
-static AuGroup *new_group(const char *name);
-static void free_group(AuGroup *group);
-static AutoCmd *new_autocmd(const char *excmd, const char *pattern);
-static void free_autocmd(AutoCmd *cmd);
-
-
-void autocmd_init(void)
-{
-    curgroup = new_group("end");
-    groups   = g_slist_prepend(groups, curgroup);
-}
-
-void autocmd_cleanup(void)
-{
-    if (groups) {
-        g_slist_free_full(groups, (GDestroyNotify)free_group);
-    }
-}
-
-/**
- * Handle the :augroup {group} ex command.
- */
-gboolean autocmd_augroup(char *name, gboolean delete)
-{
-    GSList *item;
-
-    if (!*name) {
-        return false;
-    }
-
-    /* check for group "end" that marks the default group */
-    if (!strcmp(name, "end")) {
-        curgroup = (AuGroup*)groups->data;
-        return true;
-    }
-
-    item = get_group(name);
-
-    /* check if the group is going to be removed */
-    if (delete) {
-        /* group does not exist - so do nothing */
-        if (!item) {
-            return true;
-        }
-        if (curgroup == (AuGroup*)item->data) {
-            /* if the group to delete is the current - switch the the default
-             * group after removing it */
-            curgroup = (AuGroup*)groups->data;
-        }
-
-        /* now remove the group */
-        free_group((AuGroup*)item->data);
-        groups = g_slist_delete_link(groups, item);
-
-        /* there where autocmds remove - so recreate the usedbits */
-        rebuild_used_bits();
-
-        return true;
-    }
-
-    /* check if the group does already exists */
-    if (item) {
-        /* if the group is found in the known groups use it as current */
-        curgroup = (AuGroup*)item->data;
-
-        return true;
-    }
-
-    /* create a new group and use it as current */
-    curgroup = new_group(name);
-
-    /* append it to known groups */
-    groups = g_slist_prepend(groups, curgroup);
-
-    return true;
-}
-
-/**
- * Add or delete auto commands.
- *
- * :au[tocmd]! [group] {event} {pat} [nested] {cmd}
- * group and nested flag are not supported at the moment.
- */
-gboolean autocmd_add(char *name, gboolean delete)
-{
-    guint bits;
-    char *parse, *word, *pattern, *excmd;
-    GSList *item;
-    AuGroup *grp = NULL;
-
-    parse = name;
-
-    /* parse group name if it's there */
-    word = get_next_word(&parse);
-    if (word) {
-        /* check if the word is a known group name */
-        item = get_group(word);
-        if (item) {
-            grp = (AuGroup*)item->data;
-
-            /* group is found - get the next word */
-            word = get_next_word(&parse);
-        }
-    }
-    if (!grp) {
-        /* no group found - use the current one */
-        grp = curgroup;
-    }
-
-    /* parse event name - if none was matched run it for all events */
-    if (word) {
-        bits = get_event_bits(word);
-        if (!bits) {
-            return false;
-        }
-        word = get_next_word(&parse);
-    } else {
-        bits = events[AU_ALL].bits;
-    }
-
-    /* last word is the pattern - if not found use '*' */
-    pattern = word ? word : "*";
-
-    /* the rest of the line becoes the ex command to run */
-    if (parse && !*parse) {
-        parse = NULL;
-    }
-    excmd = parse;
-
-    /* delete the autocmd if bang was given */
-    if (delete) {
-        GSList *lc;
-        AutoCmd *cmd;
-        gboolean removed = false;
-
-        /* check if the group does already exists */
-        for (lc = grp->cmds; lc; lc = lc->next) {
-            cmd = (AutoCmd*)lc->data;
-            /* if not bits match - skip the command */
-            if (!(cmd->bits & bits)) {
-                continue;
-            }
-            /* skip if pattern does not match - we check the pattern against
-             * another pattern */
-            if (!util_wildmatch(pattern, cmd->pattern)) {
-                continue;
-            }
-            /* remove the bits from the item */
-            cmd->bits &= ~bits;
-
-            /* if the command has no matching events - remove it */
-            grp->cmds = g_slist_delete_link(grp->cmds, lc);
-            free_autocmd(cmd);
-
-            removed = true;
-        }
-
-        /* if ther was at least one command removed - rebuilt the used bits */
-        if (removed) {
-            rebuild_used_bits();
-        }
-
-        return true;
-    }
-
-    /* add the new autocmd */
-    if (excmd && grp) {
-        AutoCmd *cmd = new_autocmd(excmd, pattern);
-        cmd->bits    = bits;
-
-        /* add the new autocmd to the group */
-        grp->cmds = g_slist_append(grp->cmds, cmd);
-
-        /* merge the autocmd bits into the used bits */
-        usedbits |= cmd->bits;
-    }
-
-    return true;
-}
-
-/**
- * Run named auto command.
- */
-gboolean autocmd_run(AuEvent event, const char *uri, const char *group)
-{
-    GSList  *lg, *lc;
-    AuGroup *grp;
-    AutoCmd *cmd;
-    guint bits = events[event].bits;
-
-    /* if there is no autocmd for this event - skip here */
-    if (!(usedbits & bits)) {
-        return true;
-    }
-
-    /* loop over the groups and find matching commands */
-    for (lg = groups; lg; lg = lg->next) {
-        grp = lg->data;
-        /* if a group was given - skip all none matching groupes */
-        if (group && strcmp(group, grp->name)) {
-            continue;
-        }
-        /* test each command in group */
-        for (lc = grp->cmds; lc; lc = lc->next) {
-            cmd = lc->data;
-            /* skip if this dos not match the event bits */
-            if (!(bits & cmd->bits)) {
-                continue;
-            }
-            /* check pattern only if uri was given */
-            /* skip if pattern does not match */
-            if (uri && !util_wildmatch(cmd->pattern, uri)) {
-                continue;
-            }
-            /* run the command */
-            /* TODO shoult the result be tested for RESULT_COMPLETE? */
-            /* run command and make sure it's not writte to command history */
-            ex_run_string(cmd->excmd, false);
-        }
-    }
-
-    return true;
-}
-
-gboolean autocmd_fill_group_completion(GtkListStore *store, const char *input)
-{
-    GSList *lg;
-    gboolean found = false;
-    GtkTreeIter iter;
-
-    if (!input || !*input) {
-        for (lg = groups; lg; lg = lg->next) {
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, ((AuGroup*)lg->data)->name, -1);
-            found = true;
-        }
-    } else {
-        for (lg = groups; lg; lg = lg->next) {
-            char *value = ((AuGroup*)lg->data)->name;
-            if (g_str_has_prefix(value, input)) {
-                gtk_list_store_append(store, &iter);
-                gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, value, -1);
-                found = true;
-            }
-        }
-    }
-
-    return found;
-}
-
-gboolean autocmd_fill_event_completion(GtkListStore *store, const char *input)
-{
-    int i;
-    const char *value;
-    gboolean found = false;
-    GtkTreeIter iter;
-
-    if (!input || !*input) {
-        for (i = 0; i < LENGTH(events); i++) {
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, events[i].name, -1);
-            found = true;
-        }
-    } else {
-        for (i = 0; i < LENGTH(events); i++) {
-            value = events[i].name;
-            if (g_str_has_prefix(value, input)) {
-                gtk_list_store_append(store, &iter);
-                gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, value, -1);
-                found = true;
-            }
-        }
-    }
-
-    return found;
-}
-
-/**
- * Get the augroup by it's name.
- */
-static GSList *get_group(const char *name)
-{
-    GSList  *lg;
-    AuGroup *grp;
-
-    for (lg = groups; lg; lg = lg->next) {
-        grp = lg->data;
-        if (!strcmp(grp->name, name)) {
-            return lg;
-        }
-    }
-
-    return NULL;
-}
-
-static guint get_event_bits(const char *name)
-{
-    int result = 0;
-
-    while (*name) {
-        int i, len;
-        for (i = 0; i < LENGTH(events); i++) {
-            /* we count the chars that given name has in common with the
-             * current processed event */
-            len = 0;
-            while (name[len] && events[i].name[len] && name[len] == events[i].name[len]) {
-                len++;
-            }
-
-            /* check if the matching chars built a full word match */
-            if (events[i].name[len] == '\0'
-                && (name[len] == '\0' || name[len] == ',')
-            ) {
-                /* add the bits to the result */
-                result |= events[i].bits;
-
-                /* move pointer after the match and skip possible ',' */
-                name += len;
-                if (*name == ',') {
-                    name++;
-                }
-                break;
-            }
-        }
-
-        /* check if the end was reached without a match */
-        if (i >= LENGTH(events)) {
-            /* is this the right place to write the error */
-            vb_echo(VB_MSG_ERROR, true, "Bad autocmd event name: %s", name);
-            return 0;
-        }
-    }
-
-    return result;
-}
-
-/**
- * Rebuild the usedbits from scratch.
- * Save all used autocmd event bits in the bitmap.
- */
-static void rebuild_used_bits(void)
-{
-    GSList *lc, *lg;
-    AuGroup *grp;
-
-    /* clear the current set bits */
-    usedbits = 0;
-    /* loop over the groups */
-    for (lg = groups; lg; lg = lg->next) {
-        grp = (AuGroup*)lg->data;
-
-        /* merge the used event bints into the bitmap */
-        for (lc = grp->cmds; lc; lc = lc->next) {
-            usedbits |= ((AutoCmd*)lc->data)->bits;
-        }
-    }
-}
-
-/**
- * Get the next word from given line.
- * Given line pointer is set past the word and and a 0-byte is added there.
- */
-static char *get_next_word(char **line)
-{
-    char *word;
-
-    if (!*line || !**line) {
-        return NULL;
-    }
-
-    /* remember where the word starts */
-    word = *line;
-
-    /* move pointer to the end of the word or of the line */
-    while (**line && !VB_IS_SPACE(**line)) {
-        (*line)++;
-    }
-
-    /* end the word */
-    if (**line) {
-        *(*line)++ = '\0';
-    }
-
-    /* skip trailing whitespace */
-    while (VB_IS_SPACE(**line)) {
-        (*line)++;
-    }
-
-    return word;
-}
-
-static AuGroup *new_group(const char *name)
-{
-    AuGroup *new = g_slice_new(AuGroup);
-    new->name    = g_strdup(name);
-    new->cmds    = NULL;
-
-    return new;
-}
-
-static void free_group(AuGroup *group)
-{
-    g_free(group->name);
-    if (group->cmds) {
-        g_slist_free_full(group->cmds, (GDestroyNotify)free_autocmd);
-    }
-    g_slice_free(AuGroup, group);
-}
-
-static AutoCmd *new_autocmd(const char *excmd, const char *pattern)
-{
-    AutoCmd *new = g_slice_new(AutoCmd);
-    new->excmd   = g_strdup(excmd);
-    new->pattern = g_strdup(pattern);
-    return new;
-}
-
-static void free_autocmd(AutoCmd *cmd)
-{
-    g_free(cmd->excmd);
-    g_free(cmd->pattern);
-    g_slice_free(AutoCmd, cmd);
-}
-
-#endif
diff --git a/src/autocmd.h b/src/autocmd.h
deleted file mode 100644 (file)
index aef09a2..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_AUTOCMD
-
-#ifndef _AUTOCMD_H
-#define _AUTOCMD_H
-
-#include "main.h"
-
-/* this values correspond to indices in events[] array in autocmd.c */
-typedef enum {
-    AU_ALL,
-    AU_LOAD_PROVISIONAL,
-    AU_LOAD_COMMITED,
-    AU_LOAD_FIRST_LAYOUT,
-    AU_LOAD_FINISHED,
-    AU_LOAD_FAILED,
-    AU_DOWNLOAD_START,
-    AU_DOWNLOAD_FINISHED,
-    AU_DOWNLOAD_FAILED,
-} AuEvent;
-
-void autocmd_init(void);
-void autocmd_cleanup(void);
-gboolean autocmd_augroup(char *name, gboolean delete);
-gboolean autocmd_add(char *name, gboolean delete);
-gboolean autocmd_run(AuEvent event, const char *uri, const char *group);
-gboolean autocmd_fill_group_completion(GtkListStore *store, const char *input);
-gboolean autocmd_fill_event_completion(GtkListStore *store, const char *input);
-
-#endif /* end of include guard: _AUTOCMD_H */
-#endif
diff --git a/src/bookmark.c b/src/bookmark.c
deleted file mode 100644 (file)
index adc2e03..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#include "main.h"
-#include "bookmark.h"
-#include "util.h"
-#include "completion.h"
-
-extern VbCore vb;
-
-typedef struct {
-    char *uri;
-    char *title;
-    char *tags;
-} Bookmark;
-
-static GList *load(const char *file);
-static gboolean bookmark_contains_all_tags(Bookmark *bm, char **query,
-    unsigned int qlen);
-static Bookmark *line_to_bookmark(const char *uri, const char *data);
-static void free_bookmark(Bookmark *bm);
-
-/**
- * Write a new bookmark entry to the end of bookmark file.
- */
-gboolean bookmark_add(const char *uri, const char *title, const char *tags)
-{
-    const char *file = vb.files[FILES_BOOKMARK];
-    if (tags) {
-        return util_file_append(file, "%s\t%s\t%s\n", uri, title ? title : "", tags);
-    }
-    if (title) {
-        return util_file_append(file, "%s\t%s\n", uri, title);
-    }
-    return util_file_append(file, "%s\n", uri);
-}
-
-gboolean bookmark_remove(const char *uri)
-{
-    char **lines, *line, *p;
-    int len, i;
-    GString *new;
-    gboolean removed = false;
-
-    if (!uri) {
-        return false;
-    }
-
-    lines = util_get_lines(vb.files[FILES_BOOKMARK]);
-    if (lines) {
-        new = g_string_new(NULL);
-        len = g_strv_length(lines) - 1;
-        for (i = 0; i < len; i++) {
-            line = lines[i];
-            g_strstrip(line);
-            /* ignore the title or bookmark tags and test only the uri */
-            if ((p = strchr(line, '\t'))) {
-                *p = '\0';
-                if (!strcmp(uri, line)) {
-                    removed = true;
-                    continue;
-                } else {
-                    /* reappend the tags */
-                    *p = '\t';
-                }
-            }
-            if (!strcmp(uri, line)) {
-                removed = true;
-                continue;
-            }
-            g_string_append_printf(new, "%s\n", line);
-        }
-        g_strfreev(lines);
-        g_file_set_contents(vb.files[FILES_BOOKMARK], new->str, -1, NULL);
-        g_string_free(new, true);
-    }
-
-    return removed;
-}
-
-gboolean bookmark_fill_completion(GtkListStore *store, const char *input)
-{
-    gboolean found = false;
-    char **parts;
-    unsigned int len;
-    GtkTreeIter iter;
-    GList *src = NULL;
-    Bookmark *bm;
-
-    src = load(vb.files[FILES_BOOKMARK]);
-    src = g_list_reverse(src);
-    if (!input || !*input) {
-        /* without any tags return all bookmarked items */
-        for (GList *l = src; l; l = l->next) {
-            bm = (Bookmark*)l->data;
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set(
-                store, &iter,
-                COMPLETION_STORE_FIRST, bm->uri,
-#ifdef FEATURE_TITLE_IN_COMPLETION
-                COMPLETION_STORE_SECOND, bm->title,
-#endif
-                -1
-            );
-            found = true;
-        }
-    } else {
-        parts = g_strsplit(input, " ", 0);
-        len   = g_strv_length(parts);
-
-        for (GList *l = src; l; l = l->next) {
-            bm = (Bookmark*)l->data;
-            if (bookmark_contains_all_tags(bm, parts, len)) {
-                gtk_list_store_append(store, &iter);
-                gtk_list_store_set(
-                    store, &iter,
-                    COMPLETION_STORE_FIRST, bm->uri,
-#ifdef FEATURE_TITLE_IN_COMPLETION
-                    COMPLETION_STORE_SECOND, bm->title,
-#endif
-                    -1
-                );
-                found = true;
-            }
-        }
-        g_strfreev(parts);
-    }
-
-    g_list_free_full(src, (GDestroyNotify)free_bookmark);
-
-    return found;
-}
-
-gboolean bookmark_fill_tag_completion(GtkListStore *store, const char *input)
-{
-    gboolean found;
-    unsigned int len, i;
-    char **tags, *tag;
-    GList *src = NULL, *taglist = NULL;
-    Bookmark *bm;
-
-    /* get all distinct tags from bookmark file */
-    src = load(vb.files[FILES_BOOKMARK]);
-    for (GList *l = src; l; l = l->next) {
-        bm = (Bookmark*)l->data;
-        /* if bookmark contains no tags we can go to the next bookmark */
-        if (!bm->tags) {
-            continue;
-        }
-
-        tags = g_strsplit(bm->tags, " ", -1);
-        len  = g_strv_length(tags);
-        for (i = 0; i < len; i++) {
-            tag = tags[i];
-            /* add tag only if it isn't already in the list */
-            if (!g_list_find_custom(taglist, tag, (GCompareFunc)strcmp)) {
-                taglist = g_list_prepend(taglist, g_strdup(tag));
-            }
-        }
-        g_strfreev(tags);
-    }
-
-    /* generate the completion with the found tags */
-    found = util_fill_completion(store, input, taglist);
-
-    g_list_free_full(src, (GDestroyNotify)free_bookmark);
-    g_list_free_full(taglist, (GDestroyNotify)g_free);
-
-    return found;
-}
-
-#ifdef FEATURE_QUEUE
-/**
- * Push a uri to the end of the queue.
- *
- * @uri: URI to put into the queue
- */
-gboolean bookmark_queue_push(const char *uri)
-{
-    return util_file_append(vb.files[FILES_QUEUE], "%s\n", uri);
-}
-
-/**
- * Push a uri to the beginning of the queue.
- *
- * @uri: URI to put into the queue
- */
-gboolean bookmark_queue_unshift(const char *uri)
-{
-    return util_file_prepend(vb.files[FILES_QUEUE], "%s\n", uri);
-}
-
-/**
- * Retrieves the oldest entry from queue.
- *
- * @item_count: will be filled with the number of remaining items in queue.
- * Returned uri must be freed with g_free.
- */
-char *bookmark_queue_pop(int *item_count)
-{
-    return util_file_pop_line(vb.files[FILES_QUEUE], item_count);
-}
-
-/**
- * Removes all contents from the queue file.
- */
-gboolean bookmark_queue_clear(void)
-{
-    FILE *f;
-    if ((f = fopen(vb.files[FILES_QUEUE], "w"))) {
-        fclose(f);
-        return true;
-    }
-    return false;
-}
-#endif /* FEATURE_QUEUE */
-
-static GList *load(const char *file)
-{
-    return util_file_to_unique_list(file, (Util_Content_Func)line_to_bookmark, 0);
-}
-
-/**
- * Checks if the given bookmark matches all given query strings as prefix. If
- * the bookmark has no tags, the matching is done on the '/' splited URL.
- *
- * @bm:    bookmark to test if it matches
- * @query: char array with tags to search for
- * @qlen:  length of given query char array
- *
- * Return: true if the bookmark contained all tags
- */
-static gboolean bookmark_contains_all_tags(Bookmark *bm, char **query,
-    unsigned int qlen)
-{
-    const char *separators;
-    char *cursor;
-    unsigned int i;
-    gboolean found;
-
-    if (!qlen) {
-        return true;
-    }
-
-    if (bm->tags) {
-        /* If there are tags - use them for matching. */
-        separators = " ";
-        cursor     = bm->tags;
-    } else {
-        /* No tags available - matching is based on the path parts of the URL. */
-        separators = "./";
-        cursor     = bm->uri;
-    }
-
-    /* iterate over all query parts */
-    for (i = 0; i < qlen; i++) {
-        found = false;
-
-        /* we want to do a prefix match on all bookmark tags - so we check for
-         * a match on string begin - if this fails we move the cursor to the
-         * next space and do the test again */
-        while (cursor && *cursor) {
-            /* match was not found at current cursor position */
-            if (g_str_has_prefix(cursor, query[i])) {
-                found = true;
-                break;
-            }
-            /* If match was not found at the cursor position - move cursor
-             * behind the next separator char. */
-            if ((cursor = strpbrk(cursor, separators))) {
-                cursor++;
-            }
-        }
-
-        if (!found) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static Bookmark *line_to_bookmark(const char *uri, const char *data)
-{
-    char *p;
-    Bookmark *bm;
-
-    /* data part may consist of title or title<tab>tags */
-    bm      = g_slice_new(Bookmark);
-    bm->uri = g_strdup(uri);
-    if (data && (p = strchr(data, '\t'))) {
-        *p        = '\0';
-        bm->title = g_strdup(data);
-        bm->tags  = g_strdup(p + 1);
-    } else {
-        bm->title = g_strdup(data);
-        bm->tags  = NULL;
-    }
-
-    return bm;
-}
-
-static void free_bookmark(Bookmark *bm)
-{
-    g_free(bm->uri);
-    g_free(bm->title);
-    g_free(bm->tags);
-    g_slice_free(Bookmark, bm);
-}
diff --git a/src/bookmark.h b/src/bookmark.h
deleted file mode 100644 (file)
index 32aaba4..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#ifndef _BOOKMARK_H
-#define _BOOKMARK_H
-
-gboolean bookmark_add(const char *uri, const char *title, const char *tags);
-gboolean bookmark_remove(const char *uri);
-gboolean bookmark_fill_completion(GtkListStore *store, const char *input);
-gboolean bookmark_fill_tag_completion(GtkListStore *store, const char *input);
-#ifdef FEATURE_QUEUE
-gboolean bookmark_queue_push(const char *uri);
-gboolean bookmark_queue_unshift(const char *uri);
-char *bookmark_queue_pop(int *item_count);
-gboolean bookmark_queue_clear(void);
-#endif
-
-#endif /* end of include guard: _BOOKMARK_H */
index a5bb9f2..c957c46 100644 (file)
  * This file contains functions that are used by normal mode and command mode
  * together.
  */
-#include "config.h"
-#include "main.h"
 #include "command.h"
+#include "config.h"
 #include "history.h"
-#include "bookmark.h"
+#include "main.h"
+#include <stdlib.h>
 
-extern VbCore vb;
+extern struct Vimb vb;
 
-gboolean command_search(const Arg *arg)
+gboolean command_search(Client *c, const Arg *arg)
 {
-    static short dir;   /* last direction 1 forward, -1 backward*/
     const char *query;
-    static gboolean newsearch = true;
     gboolean forward;
+    WebKitFindController *fc;
 
+    fc = webkit_web_view_get_find_controller(c->webview);
     if (arg->i == 0) {
-#ifdef FEATURE_SEARCH_HIGHLIGHT
-        webkit_web_view_unmark_text_matches(vb.gui.webview);
-        vb.state.search_matches = 0;
-        vb_update_statusbar();
-#endif
-        newsearch = true;
-        return true;
+        webkit_find_controller_search_finish(fc);
+        c->state.search.matches = 0;
+        c->state.search.active  = FALSE;
+        vb_statusbar_update(c);
+        return TRUE;
     }
 
     /* copy search query for later use */
     if (arg->s) {
         /* set search direction only when the searching is started */
-        dir   = arg->i > 0 ? 1 : -1;
+        c->state.search.direction = arg->i > 0 ? 1 : -1;
         query = arg->s;
         /* add new search query to history and search register */
-        vb_register_add('/', query);
-        history_add(HISTORY_SEARCH, query, NULL);
+        vb_register_add(c, '/', query);
+        history_add(c, HISTORY_SEARCH, query, NULL);
     } else {
         /* no search phrase given - continue a previous search */
-        query = vb_register_get('/');
+        query = vb_register_get(c, '/');
     }
 
-    forward = (arg->i * dir) > 0;
+    forward = (arg->i * c->state.search.direction) > 0;
 
     if (query) {
-        unsigned int count = abs(arg->i);
-        if (newsearch) {
-#ifdef FEATURE_SEARCH_HIGHLIGHT
-            /* highlight matches if the search is started new or continued
-             * after switch to normal mode which calls this function with
-             * COMMAND_SEARCH_OFF */
-            vb.state.search_matches = webkit_web_view_mark_text_matches(vb.gui.webview, query, false, 0);
-            webkit_web_view_set_highlight_text_matches(vb.gui.webview, true);
-            vb_update_statusbar();
-#endif
-            newsearch = false;
-            /* skip first search because this is done during typing in ex
-             * mode, else the search will mark the next match as active */
-            if (count) {
-                count -= 1;
-            }
+        guint count = abs(arg->i);
+
+        if (!c->state.search.active) {
+            webkit_find_controller_search(fc, query,
+                    WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE |
+                    WEBKIT_FIND_OPTIONS_WRAP_AROUND |
+                    (forward ?  WEBKIT_FIND_OPTIONS_NONE : WEBKIT_FIND_OPTIONS_BACKWARDS),
+                    G_MAXUINT);
+            c->state.search.active = TRUE;
+            /* Skip first search because the first match is already
+             * highlighted on search start. */
+            count -= 1;
         }
 
-        while (count--) {
-            if (!webkit_web_view_search_text(vb.gui.webview, query, false, forward, true)) {
-                break;
-            }
-        };
+        if (forward) {
+            while (count--) {
+                webkit_find_controller_search_next(fc);
+            };
+        } else {
+            while (count--) {
+                webkit_find_controller_search_previous(fc);
+            };
+        }
     }
 
-    return true;
+    return TRUE;
 }
 
-gboolean command_yank(const Arg *arg, char buf)
+gboolean command_yank(Client *c, const Arg *arg, char buf)
 {
-    static char *tmpl = "Yanked: %s";
-
-    if (arg->i == COMMAND_YANK_SELECTION) {
-        char *text = NULL;
-        /* copy current selection to clipboard */
-        webkit_web_view_copy_clipboard(vb.gui.webview);
-        text = gtk_clipboard_wait_for_text(PRIMARY_CLIPBOARD());
-        if (!text) {
-            text = gtk_clipboard_wait_for_text(SECONDARY_CLIPBOARD());
-        }
-        if (text) {
-            /* save in given register and default "" register */
-            vb_register_add(buf, text);
-            vb_register_add('"', text);
-            vb_echo(VB_MSG_NORMAL, false, tmpl, text);
-            g_free(text);
-
-            return true;
-        }
-
-        return false;
-    }
-
-    Arg a = {VB_CLIPBOARD_PRIMARY|VB_CLIPBOARD_SECONDARY};
-    if (arg->i == COMMAND_YANK_URI) {
-        /* yank current uri */
-        a.s = vb.state.uri;
-    } else {
-        /* use current arg.s as new clipboard content */
-        a.s = arg->s;
-    }
-    if (a.s) {
-        vb_set_clipboard(&a);
-        /* save in given register and default "" register */
-        vb_register_add(buf, a.s);
-        vb_register_add('"', a.s);
-        vb_echo(VB_MSG_NORMAL, false, tmpl, a.s);
-
-        return true;
-    }
-
-    return false;
+    /* TODO no implemented yet */
+    return TRUE;
 }
 
-gboolean command_save(const Arg *arg)
+gboolean command_save(Client *c, const Arg *arg)
 {
-    WebKitDownload *download;
+#if 0
     const char *uri, *path = NULL;
 
     if (arg->i == COMMAND_SAVE_CURRENT) {
-        uri = vb.state.uri;
+        uri = c->state.uri;
         /* given string is the path to save the download to */
         if (arg->s && *(arg->s) != '\0') {
             path = arg->s;
@@ -152,52 +110,18 @@ gboolean command_save(const Arg *arg)
     }
 
     if (!uri || !*uri) {
-        return false;
+        return FALSE;
     }
+#endif
 
-    download = webkit_download_new(webkit_network_request_new(uri));
-    vb_download(vb.gui.webview, download, path);
-
-    return true;
+    /* TODO start the download to given path here */
+    return TRUE;
 }
 
 #ifdef FEATURE_QUEUE
-gboolean command_queue(const Arg *arg)
+gboolean command_queue(Client *c, const Arg *arg)
 {
-    gboolean res = false;
-    int count = 0;
-    char *uri;
-
-    switch (arg->i) {
-        case COMMAND_QUEUE_POP:
-            if ((uri = bookmark_queue_pop(&count))) {
-                res = vb_load_uri(&(Arg){VB_TARGET_CURRENT, uri});
-                g_free(uri);
-            }
-            vb_echo(VB_MSG_NORMAL, false, "Queue length %d", count);
-            break;
-
-        case COMMAND_QUEUE_PUSH:
-            res = bookmark_queue_push(arg->s ? arg->s : vb.state.uri);
-            if (res) {
-                vb_echo(VB_MSG_NORMAL, false, "Pushed to queue");
-            }
-            break;
-
-        case COMMAND_QUEUE_UNSHIFT:
-            res = bookmark_queue_unshift(arg->s ? arg->s : vb.state.uri);
-            if (res) {
-                vb_echo(VB_MSG_NORMAL, false, "Pushed to queue");
-            }
-            break;
-
-        case COMMAND_QUEUE_CLEAR:
-            if (bookmark_queue_clear()) {
-                vb_echo(VB_MSG_NORMAL, false, "Queue cleared");
-            }
-            break;
-    }
-
-    return res;
+    /* TODO no implemented yet */
+    return TRUE;
 }
 #endif
index 68c8d85..92cf494 100644 (file)
@@ -20,6 +20,9 @@
 #ifndef _COMMAND_H
 #define _COMMAND_H
 
+#include <gtk/gtk.h>
+#include "main.h"
+
 enum {
     COMMAND_YANK_ARG,
     COMMAND_YANK_URI,
@@ -40,11 +43,11 @@ enum {
 };
 #endif
 
-gboolean command_search(const Arg *arg);
-gboolean command_yank(const Arg *arg, char buf);
-gboolean command_save(const Arg *arg);
+gboolean command_search(Client *c, const Arg *arg);
+gboolean command_yank(Client *c, const Arg *arg, char buf);
+gboolean command_save(Client *c, const Arg *arg);
 #ifdef FEATURE_QUEUE
-gboolean command_queue(const Arg *arg);
+gboolean command_queue(Client *c, const Arg *arg);
 #endif
 
 #endif /* end of include guard: _COMMAND_H */
index a15b081..8bfb9b0 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
+#include "completion.h"
 #include "config.h"
 #include "main.h"
-#include "completion.h"
-
-extern VbCore vb;
 
-static struct {
-    GtkWidget *win;
-    GtkWidget *tree;
-    int       active;  /* number of the current active tree item */
-    CompletionSelectFunc selfunc;
-} comp;
+typedef struct {
+    GtkWidget               *win, *tree;
+    int                     active;  /* number of the current active tree item */
+    CompletionSelectFunc    selfunc;
+} Completion;
 
 static gboolean tree_selection_func(GtkTreeSelection *selection,
     GtkTreeModel *model, GtkTreePath *path, gboolean selected, gpointer data);
 
+extern struct Vimb vb;
 
-gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
-    gboolean back)
+
+/**
+ * Stop the completion and reset temporary used data.
+ */
+void completion_clean(Client *c)
+{
+    Completion *comp = (Completion*)c->comp;
+    c->mode->flags  &= ~FLAG_COMPLETION;
+
+    if (comp->win) {
+        gtk_widget_destroy(comp->win);
+        comp->win  = NULL;
+        comp->tree = NULL;
+    }
+}
+
+/**
+ * Free the memory of the completion set on the client.
+ */
+void completion_cleanup(Client *c)
+{
+    if (c->comp) {
+        g_slice_free(Completion, c->comp);
+        c->comp = NULL;
+    }
+}
+
+/**
+ * Start the completion by creating the required widgets and setting a select
+ * function.
+ */
+gboolean completion_create(Client *c, GtkTreeModel *model,
+        CompletionSelectFunc selfunc, gboolean back)
 {
     GtkCellRenderer *renderer;
     GtkTreeSelection *selection;
@@ -44,6 +73,7 @@ gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
     GtkTreePath *path;
     GtkTreeIter iter;
     int height, width;
+    Completion *comp = (Completion*)c->comp;
 
     /* if there is only one match - don't build the tree view */
     if (gtk_tree_model_iter_n_children(model, NULL) == 1) {
@@ -53,59 +83,56 @@ gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
             gtk_tree_model_get(model, &iter, COMPLETION_STORE_FIRST, &value, -1);
 
             /* call the select function */
-            selfunc(value);
+            selfunc(c, value);
 
             g_free(value);
             g_object_unref(model);
 
-            return false;
+            return FALSE;
         }
     }
 
-    comp.selfunc = selfunc;
+    comp->selfunc = selfunc;
 
     /* prepare the tree view */
-    comp.win  = gtk_scrolled_window_new(NULL, NULL);
-    comp.tree = gtk_tree_view_new();
-#ifndef HAS_GTK3
-    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(comp.win), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
-#endif
-    gtk_box_pack_end(GTK_BOX(vb.gui.box), comp.win, false, false, 0);
-    gtk_container_add(GTK_CONTAINER(comp.win), comp.tree);
+    comp->win  = gtk_scrolled_window_new(NULL, NULL);
+    comp->tree = gtk_tree_view_new();
+
+    GtkCssProvider* provider = gtk_css_provider_get_default();
+    gtk_css_provider_load_from_data(provider, GUI_STYLE, -1, NULL);
+    gtk_style_context_add_provider(gtk_widget_get_style_context(comp->tree),
+            GTK_STYLE_PROVIDER(provider),
+            GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
 
-    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(comp.tree), false);
+    gtk_box_pack_end(GTK_BOX(gtk_widget_get_parent(GTK_WIDGET(c->statusbar.box))), comp->win, FALSE, FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(comp->win), comp->tree);
+
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(comp->tree), FALSE);
     /* we have only on line per item so we can use the faster fixed heigh mode */
-    gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(comp.tree), true);
-    gtk_tree_view_set_model(GTK_TREE_VIEW(comp.tree), model);
+    gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(comp->tree), TRUE);
+    gtk_tree_view_set_model(GTK_TREE_VIEW(comp->tree), model);
     g_object_unref(model);
 
-    VB_WIDGET_OVERRIDE_TEXT(comp.tree, VB_GTK_STATE_NORMAL, &vb.style.comp_fg[VB_COMP_NORMAL]);
-    VB_WIDGET_OVERRIDE_BASE(comp.tree, VB_GTK_STATE_NORMAL, &vb.style.comp_bg[VB_COMP_NORMAL]);
-    VB_WIDGET_OVERRIDE_TEXT(comp.tree, VB_GTK_STATE_SELECTED, &vb.style.comp_fg[VB_COMP_ACTIVE]);
-    VB_WIDGET_OVERRIDE_BASE(comp.tree, VB_GTK_STATE_SELECTED, &vb.style.comp_bg[VB_COMP_ACTIVE]);
-    VB_WIDGET_OVERRIDE_TEXT(comp.tree, VB_GTK_STATE_ACTIVE, &vb.style.comp_fg[VB_COMP_ACTIVE]);
-    VB_WIDGET_OVERRIDE_BASE(comp.tree, VB_GTK_STATE_ACTIVE, &vb.style.comp_bg[VB_COMP_ACTIVE]);
-
     /* prepare the selection */
-    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(comp.tree));
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(comp->tree));
     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
-    gtk_tree_selection_set_select_function(selection, tree_selection_func, NULL, NULL);
+    gtk_tree_selection_set_select_function(selection, tree_selection_func, c, NULL);
 
     /* get window dimension */
-    gtk_window_get_size(GTK_WINDOW(vb.gui.window), &width, &height);
+    gtk_window_get_size(GTK_WINDOW(c->window), &width, &height);
 
     /* prepare first column */
     column = gtk_tree_view_column_new();
     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
-    gtk_tree_view_append_column(GTK_TREE_VIEW(comp.tree), column);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(comp->tree), column);
 
     renderer = gtk_cell_renderer_text_new();
     g_object_set(renderer,
-        "font-desc", vb.style.comp_font,
+        "font-desc", c->config.comp_font,
         "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
         NULL
     );
-    gtk_tree_view_column_pack_start(column, renderer, true);
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
     gtk_tree_view_column_add_attribute(column, renderer, "text", COMPLETION_STORE_FIRST);
     gtk_tree_view_column_set_min_width(column, 2 * width/3);
 
@@ -113,20 +140,20 @@ gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
 #ifdef FEATURE_TITLE_IN_COMPLETION
     column = gtk_tree_view_column_new();
     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
-    gtk_tree_view_append_column(GTK_TREE_VIEW(comp.tree), column);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(comp->tree), column);
 
     renderer = gtk_cell_renderer_text_new();
     g_object_set(renderer,
-        "font-desc", vb.style.comp_font,
+        "font-desc", c->config.comp_font,
         "ellipsize", PANGO_ELLIPSIZE_END,
         NULL
     );
-    gtk_tree_view_column_pack_start(column, renderer, true);
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
     gtk_tree_view_column_add_attribute(column, renderer, "text", COMPLETION_STORE_SECOND);
 #endif
 
     /* to set the height for the treeview the tree must be realized first */
-    gtk_widget_show(comp.tree);
+    gtk_widget_show(comp->tree);
 
     /* this prevents the first item to be placed out of view if the completion
      * is shown */
@@ -135,30 +162,31 @@ gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
     }
 
     /* use max 1/3 of window height for the completion */
-#ifdef HAS_GTK3
-    gtk_widget_get_preferred_size(comp.tree, NULL, &size);
+    gtk_widget_get_preferred_size(comp->tree, NULL, &size);
     height /= 3;
     gtk_scrolled_window_set_min_content_height(
-        GTK_SCROLLED_WINDOW(comp.win),
+        GTK_SCROLLED_WINDOW(comp->win),
         size.height > height ? height : size.height
     );
-#else
-    gtk_widget_size_request(comp.tree, &size);
-    height /= 3;
-    if (size.height > height) {
-        gtk_widget_set_size_request(comp.win, -1, height);
-    }
-#endif
 
-    vb.mode->flags |= FLAG_COMPLETION;
+    c->mode->flags |= FLAG_COMPLETION;
 
     /* set to -1 to have the cursor on first or last item set in move_cursor */
-    comp.active = -1;
-    completion_next(back);
+    comp->active = -1;
+    completion_next(c, back);
 
-    gtk_widget_show(comp.win);
+    gtk_widget_show(comp->win);
 
-    return true;
+    return TRUE;
+}
+
+/**
+ * Initialize the completion system for given client.
+ */
+void completion_init(Client *c)
+{
+    /* Allocate memory for the completion struct and save it on the client. */
+    c->comp = g_slice_new0(Completion);
 }
 
 /**
@@ -166,52 +194,78 @@ gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
  * If the end/beginning is reached return false and start on the opposite end
  * on the next call.
  */
-gboolean completion_next(gboolean back)
+gboolean completion_next(Client *c, gboolean back)
 {
     int rows;
     GtkTreePath *path;
-    GtkTreeView *tree = GTK_TREE_VIEW(comp.tree);
+    GtkTreeView *tree = GTK_TREE_VIEW(((Completion*)c->comp)->tree);
+    Completion *comp  = (Completion*)c->comp;
 
     rows = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(tree), NULL);
     if (back) {
-        comp.active--;
+        comp->active--;
         /* Step back over the beginning. */
-        if (comp.active == -1) {
+        if (comp->active == -1) {
             /* Unselect the current item to show the user that the shown
              * content is the initial typed content. */
-            gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(comp.tree)));
+            gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(comp->tree)));
 
-            return false;
-        } else if (comp.active < -1) {
-            comp.active = rows - 1;
+            return FALSE;
+        }
+        if (comp->active < -1) {
+            comp->active = rows - 1;
         }
     } else {
-        comp.active++;
+        comp->active++;
         /* Step over the end. */
-        if (comp.active == rows) {
-            gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(comp.tree)));
+        if (comp->active == rows) {
+            gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(comp->tree)));
 
-            return false;
-        } else if (comp.active >= rows) {
-            comp.active = 0;
+            return FALSE;
+        }
+        if (comp->active >= rows) {
+            comp->active = 0;
         }
     }
 
     /* get new path and move cursor to it */
-    path = gtk_tree_path_new_from_indices(comp.active, -1);
-    gtk_tree_view_set_cursor(tree, path, NULL, false);
+    path = gtk_tree_path_new_from_indices(comp->active, -1);
+    gtk_tree_view_set_cursor(tree, path, NULL, FALSE);
     gtk_tree_path_free(path);
 
-    return true;
+    return TRUE;
 }
 
-void completion_clean(void)
+/**
+ * Fills the given list store by matching data of also given src list.
+ */
+gboolean completion_fill(GtkListStore *store, const char *input, GList *src)
 {
-    vb.mode->flags &= ~FLAG_COMPLETION;
-    if (comp.win) {
-        gtk_widget_destroy(comp.win);
-        comp.win = comp.tree = NULL;
+    gboolean found = FALSE;
+    GtkTreeIter iter;
+
+    /* If no filter input given - copy all entries into the data store. */
+    if (!input || !*input) {
+        for (GList *l = src; l; l = l->next) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, l->data, -1);
+            found = TRUE;
+        }
+        return found;
     }
+
+    /* If filter input is given - copy matching list entires into data store.
+     * Strings are compared by prefix matching. */
+    for (GList *l = src; l; l = l->next) {
+        char *value = (char*)l->data;
+        if (g_str_has_prefix(value, input)) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, l->data, -1);
+            found = TRUE;
+        }
+    }
+
+    return found;
 }
 
 static gboolean tree_selection_func(GtkTreeSelection *selection,
@@ -219,16 +273,19 @@ static gboolean tree_selection_func(GtkTreeSelection *selection,
 {
     char *value;
     GtkTreeIter iter;
+    Completion *comp = (Completion*)((Client*)data)->comp;
 
     /* if not selected means the item is going to be selected which we are
      * interested in */
     if (!selected && gtk_tree_model_get_iter(model, &iter, path)) {
         gtk_tree_model_get(model, &iter, COMPLETION_STORE_FIRST, &value, -1);
 
-        comp.selfunc(value);
+        comp->selfunc((Client*)data, value);
 
         g_free(value);
+        /* TODO update comp->active on select by mouse to continue with <Tab>
+         * or <S-Tab> from selected item on. */
     }
 
-    return true;
+    return TRUE;
 }
index 26b057d..e353ecb 100644 (file)
 #ifndef _COMPLETION_H
 #define _COMPLETION_H
 
+#include <glib.h>
+
 #include "main.h"
 
+typedef void (*CompletionSelectFunc) (Client *c, char *match);
+
 enum {
     COMPLETION_STORE_FIRST,
 #ifdef FEATURE_TITLE_IN_COMPLETION
@@ -30,11 +34,13 @@ enum {
     COMPLETION_STORE_NUM
 };
 
-typedef void (*CompletionSelectFunc) (char *match);
 
-gboolean completion_create(GtkTreeModel *model, CompletionSelectFunc selfunc,
-    gboolean back);
-void completion_clean(void);
-gboolean completion_next(gboolean back);
+void completion_clean(Client *c);
+void completion_cleanup(Client *c);
+gboolean completion_create(Client *c, GtkTreeModel *model,
+        CompletionSelectFunc selfunc, gboolean back);
+void completion_init(Client *c);
+gboolean completion_next(Client *c, gboolean back);
+gboolean completion_fill(GtkListStore *store, const char *input, GList *src);
 
 #endif /* end of include guard: _COMPLETION_H */
index 97531bf..da33cc7 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#ifndef _CONFIG_H
-#define _CONFIG_H
-
-
 /* features */
-/* enable cookie support */
-#define FEATURE_COOKIE
-/* highlight search results */
-#define FEATURE_SEARCH_HIGHLIGHT
-/* disable scrollbars */
-#define FEATURE_NO_SCROLLBARS
-/* show page title in url completions */
-#define FEATURE_TITLE_IN_COMPLETION
-/* enable the read it later queue */
-#define FEATURE_QUEUE
-/* show load progress in window title */
-#define FEATURE_TITLE_PROGRESS
 /* should the history indicator [+-] be shown in status bar after url */
 #define FEATURE_HISTORY_INDICATOR
-/* should the profile name be shown before url in url bar */
-#define FEATURE_PROFILE_INDICATOR
 /* show wget style progressbar in status bar */
 #define FEATURE_WGET_PROGRESS_BAR
-#ifdef HAS_GTK3
-/* enables workaround for hight dpi displays */
-/* eventually the environment variable GDK_DPI_SCALE=2.0 must be set */
-/* to get the hack working */
-/* #define FEATURE_HIGH_DPI */
+#ifdef FEATURE_WGET_PROGRESS_BAR
+/* chars to use for the progressbar */
+#define PROGRESS_BAR                "=> "
+#define PROGRESS_BAR_LEN            20
 #endif
-/* enable HTTP Strict-Transport-Security*/
-#define FEATURE_HSTS
-/* enable soup caching - size can be configure by maximum-cache-size setting */
-#define FEATURE_SOUP_CACHE
-/* enable the :autocmd feature */
-#define FEATURE_AUTOCMD
-/* enable the :auto-response-header feature */
-#define FEATURE_ARH
-/* allow to use socket to remote control vimb */
-#define FEATURE_SOCKET
+/* show page title in url completions */
+#define FEATURE_TITLE_IN_COMPLETION
 
 /* time in seconds after that message will be removed from inputbox if the
  * message where only temporary */
-#define MESSAGE_TIMEOUT              5
+#define MESSAGE_TIMEOUT             5
 
-/* number of chars to be shown for ambiguous commands */
+/* number of chars to be shown in statusbar for ambiguous commands */
 #define SHOWCMD_LEN                 10
+/* css applied to the gui elements of the borwser window */
+#define GUI_STYLE                   "GtkBox#statusbar{color:#fff;background-color:#000;font:monospace bold 10;} \
+GtkBox#statusbar.secure{background-color:#95e454;color:#000;} \
+GtkBox#statusbar.insecure{background-color:#f77;color:#000;} \
+GtkTextView{background-color:#fff;color:#000;font:monospace 10;} \
+GtkTextView.error{background-color:#f77;font-weight:bold;} \
+GtkTreeView{color:#fff;background-color:#656565;font:monospace;} \
+GtkTreeView:hover{background-color:#777;} \
+GtkTreeView:selected{color:#f6f3e8;background-color:#888;}"
 
-/* parh to crt file for the certificate validation */
-#define SETTING_CA_BUNDLE           "/etc/ssl/certs/ca-certificates.crt"
-#define SETTING_MAX_CONNS           25
-#define SETTING_MAX_CONNS_PER_HOST   5
 /* default font size for fonts in webview */
 #define SETTING_DEFAULT_FONT_SIZE   10
-#define SETTING_GUI_FONT_NORMAL     "monospace normal 10"
-#define SETTING_GUI_FONT_EMPH       "monospace bold 10"
-#define SETTING_HOME_PAGE           "http://fanglingsu.github.io/vimb/"
-
-#define MAXIMUM_HINTS              500
-
-#define WIN_WIDTH                  800
-#define WIN_HEIGHT                 600
-
-#ifdef FEATURE_WGET_PROGRESS_BAR
-/* chars to use for the progressbar */
-#define PROGRESS_BAR             "=> "
-#define PROGRESS_BAR_LEN            20
-#endif
-
-/* CSS style use on creating hints. This might also be averrules by css out of
- * $XDG_CONFIG_HOME/vimb/style.css file. */
-#define HINT_CSS "#_hintContainer{\
-position:static\
-}\
-._hintLabel{\
--webkit-transform:translate(-4px,-4px);\
-position:absolute;\
-z-index:100000;\
-font:bold .8em monospace;\
-color:#000;\
-background-color:#fff;\
-margin:0;\
-padding:0px 1px;\
-border:1px solid #444;\
-opacity:0.7\
-}\
-._hintElem{\
-background-color:#ff0 !important;\
-color:#000 !important\
-}\
-._hintElem._hintFocus{\
-background-color:#8f0 !important\
-}\
-._hintLabel._hintFocus{\
-z-index:100001;\
-opacity:1\
-}"
-
-#endif /* end of include guard: _CONFIG_H */
+#define SETTING_HOME_PAGE           "about:blank"
diff --git a/src/cookiejar.c b/src/cookiejar.c
deleted file mode 100644 (file)
index a16392f..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-
-#ifdef FEATURE_COOKIE
-#include <fcntl.h>
-#include <sys/file.h>
-#include "main.h"
-#include "cookiejar.h"
-
-G_DEFINE_TYPE(CookieJar, cookiejar, SOUP_TYPE_COOKIE_JAR_TEXT)
-
-static void cookiejar_changed(SoupCookieJar *self, SoupCookie *old, SoupCookie *new);
-static void cookiejar_class_init(CookieJarClass *klass);
-static void cookiejar_finalize(GObject *self);
-static void cookiejar_init(CookieJar *self);
-static void cookiejar_set_property(GObject *self, guint prop_id,
-    const GValue *value, GParamSpec *pspec);
-
-extern VbCore vb;
-
-
-SoupCookieJar *cookiejar_new(const char *file, gboolean ro)
-{
-    return g_object_new(
-        COOKIEJAR_TYPE,
-        SOUP_COOKIE_JAR_TEXT_FILENAME, file,
-        SOUP_COOKIE_JAR_READ_ONLY, ro,
-        NULL
-    );
-}
-
-static void cookiejar_changed(SoupCookieJar *self, SoupCookie *old_cookie, SoupCookie *new_cookie)
-{
-    flock(COOKIEJAR(self)->lock, LOCK_EX);
-    SoupDate *expire;
-    if (new_cookie) {
-    /* session-expire-time handling */
-    if (vb.config.cookie_expire_time == 0) {
-        soup_cookie_set_expires(new_cookie, NULL);
-
-    } else if (vb.config.cookie_expire_time > 0 && new_cookie->expires) {
-        expire = soup_date_new_from_now(vb.config.cookie_expire_time);
-        if (soup_date_to_time_t(expire) < soup_date_to_time_t(new_cookie->expires)) {
-            soup_cookie_set_expires(new_cookie, expire);
-        }
-        soup_date_free(expire);
-    }
-
-    /* session-cookie handling */
-    if (!new_cookie->expires && vb.config.cookie_timeout) {
-        expire = soup_date_new_from_now(vb.config.cookie_timeout);
-        soup_cookie_set_expires(new_cookie, expire);
-        soup_date_free(expire);
-    }
-    }
-    SOUP_COOKIE_JAR_CLASS(cookiejar_parent_class)->changed(self, old_cookie, new_cookie);
-    flock(COOKIEJAR(self)->lock, LOCK_UN);
-}
-
-static void cookiejar_class_init(CookieJarClass *klass)
-{
-    SOUP_COOKIE_JAR_CLASS(klass)->changed = cookiejar_changed;
-    G_OBJECT_CLASS(klass)->get_property   = G_OBJECT_CLASS(cookiejar_parent_class)->get_property;
-    G_OBJECT_CLASS(klass)->set_property   = cookiejar_set_property;
-    G_OBJECT_CLASS(klass)->finalize       = cookiejar_finalize;
-    g_object_class_override_property(G_OBJECT_CLASS(klass), 1, "filename");
-}
-
-static void cookiejar_finalize(GObject *self)
-{
-    close(COOKIEJAR(self)->lock);
-    G_OBJECT_CLASS(cookiejar_parent_class)->finalize(self);
-}
-
-static void cookiejar_init(CookieJar *self)
-{
-    self->lock = open(vb.files[FILES_COOKIE], O_RDWR);
-}
-
-static void cookiejar_set_property(GObject *self, guint prop_id, const
-    GValue *value, GParamSpec *pspec)
-{
-    flock(COOKIEJAR(self)->lock, LOCK_SH);
-    G_OBJECT_CLASS(cookiejar_parent_class)->set_property(self, prop_id, value, pspec);
-    flock(COOKIEJAR(self)->lock, LOCK_UN);
-}
-#endif
diff --git a/src/cookiejar.h b/src/cookiejar.h
deleted file mode 100644 (file)
index bca9758..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_COOKIE
-
-#ifndef _COOKIEJAR_H
-#define _COOKIEJAR_H
-
-#define COOKIEJAR_TYPE (cookiejar_get_type())
-#define COOKIEJAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), COOKIEJAR_TYPE, CookieJar))
-
-
-typedef struct {
-    SoupCookieJarText parent_instance;
-    int lock;
-} CookieJar;
-
-typedef struct {
-    SoupCookieJarTextClass parent_class;
-} CookieJarClass;
-
-GType cookiejar_get_type(void);
-SoupCookieJar *cookiejar_new(const char *file, gboolean ro);
-
-#endif /* end of include guard: _COOKIEJAR_H */
-#endif
diff --git a/src/dom.c b/src/dom.c
deleted file mode 100644 (file)
index 32b6bea..0000000
--- a/src/dom.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#include "main.h"
-#include "dom.h"
-
-extern VbCore vb;
-
-static gboolean element_is_visible(WebKitDOMDOMWindow* win, WebKitDOMElement* element);
-static gboolean auto_insert(Element *element);
-static gboolean editable_blur_cb(Element *element, Event *event);
-static gboolean editable_focus_cb(Element *element, Event *event);
-static Element *get_active_element(Document *doc);
-
-
-void dom_install_focus_blur_callbacks(Document *doc)
-{
-    HtmlElement *element = webkit_dom_document_get_body(doc);
-
-    if (!element) {
-        element = WEBKIT_DOM_HTML_ELEMENT(webkit_dom_document_get_document_element(doc));
-    }
-    if (element) {
-        webkit_dom_event_target_add_event_listener(
-            WEBKIT_DOM_EVENT_TARGET(element), "blur", G_CALLBACK(editable_blur_cb), true, NULL
-        );
-        webkit_dom_event_target_add_event_listener(
-            WEBKIT_DOM_EVENT_TARGET(element), "focus", G_CALLBACK(editable_focus_cb), true, NULL
-        );
-    }
-}
-
-void dom_check_auto_insert(Document *doc)
-{
-    Element *active = webkit_dom_html_document_get_active_element(WEBKIT_DOM_HTML_DOCUMENT(doc));
-
-    if (active) {
-        if (!vb.config.strict_focus) {
-            auto_insert(active);
-        } else if (vb.mode->id != 'i') {
-            /* If strict-focus is enabled and the editable element becomes
-             * focus, we explicitely remove the focus. But only if vimb isn't
-             * in input mode at the time. This prevents from leaving input
-             * mode that was started by user interaction like click to
-             * editable element, or the 'gi' normal mode command. */
-            webkit_dom_element_blur(active);
-        }
-    }
-    dom_install_focus_blur_callbacks(doc);
-}
-
-/**
- * Remove focus from active and editable elements.
- */
-void dom_clear_focus(WebKitWebView *view)
-{
-    Element *active = dom_get_active_element(view);
-    if (active) {
-        webkit_dom_element_blur(active);
-    }
-}
-
-/**
- * Give the focus to element.
- */
-void dom_give_focus(Element *element)
-{
-    webkit_dom_element_focus(element);
-}
-
-/**
- * Find the first editable element and set the focus on it and enter input
- * mode.
- * Returns true if there was an editable element focused.
- */
-gboolean dom_focus_input(Document *doc)
-{
-    WebKitDOMNode *html, *node;
-    WebKitDOMDOMWindow *win;
-    WebKitDOMNodeList *list;
-    WebKitDOMXPathNSResolver *resolver;
-    WebKitDOMXPathResult* result;
-    Document *frame_doc;
-    guint i, len;
-
-    win  = webkit_dom_document_get_default_view(doc);
-    list = webkit_dom_document_get_elements_by_tag_name(doc, "html");
-    if (!list) {
-        return false;
-    }
-
-    html = webkit_dom_node_list_item(list, 0);
-    g_object_unref(list);
-
-    resolver = webkit_dom_document_create_ns_resolver(doc, html);
-    if (!resolver) {
-        return false;
-    }
-
-    /* Use translate to match xpath expression case insensitive so that also
-     * intput filed of type="TEXT" are matched. */
-    result = webkit_dom_document_evaluate(
-        doc, "//input[not(@type) "
-        "or translate(@type,'ETX','etx')='text' "
-        "or translate(@type,'ADOPRSW','adoprsw')='password' "
-        "or translate(@type,'CLOR','clor')='color' "
-        "or translate(@type,'ADET','adet')='date' "
-        "or translate(@type,'ADEIMT','adeimt')='datetime' "
-        "or translate(@type,'ACDEILMOT','acdeilmot')='datetime-local' "
-        "or translate(@type,'AEILM','aeilm')='email' "
-        "or translate(@type,'HMNOT','hmnot')='month' "
-        "or translate(@type,'BEMNRU','bemnru')='number' "
-        "or translate(@type,'ACEHRS','acehrs')='search' "
-        "or translate(@type,'ELT','elt')='tel' "
-        "or translate(@type,'EIMT','eimt')='time' "
-        "or translate(@type,'LRU','lru')='url' "
-        "or translate(@type,'EKW','ekw')='week' "
-        "]|//textarea",
-        html, resolver, 5, NULL, NULL
-    );
-    if (!result) {
-        return false;
-    }
-    while ((node = webkit_dom_xpath_result_iterate_next(result, NULL))) {
-        if (element_is_visible(win, WEBKIT_DOM_ELEMENT(node))) {
-            vb_enter('i');
-            webkit_dom_element_focus(WEBKIT_DOM_ELEMENT(node));
-            return true;
-        }
-    }
-
-    /* Look for editable elements in frames too. */
-    list = webkit_dom_document_get_elements_by_tag_name(doc, "iframe");
-    len  = webkit_dom_node_list_get_length(list);
-
-    for (i = 0; i < len; i++) {
-        node      = webkit_dom_node_list_item(list, i);
-        frame_doc = webkit_dom_html_iframe_element_get_content_document(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node));
-        /* Stop on first frame with focused element. */
-        if (dom_focus_input(frame_doc)) {
-            g_object_unref(list);
-            return true;
-        }
-    }
-    g_object_unref(list);
-
-    return false;
-}
-
-/**
- * Indicates if the given dom element is an editable element like text input,
- * password or textarea.
- */
-gboolean dom_is_editable(Element *element)
-{
-    gboolean result = false;
-    char *tagname, *type, *editable;
-
-    if (!element) {
-        return result;
-    }
-
-    tagname  = webkit_dom_element_get_tag_name(element);
-    type     = webkit_dom_element_get_attribute(element, "type");
-    editable = webkit_dom_element_get_attribute(element, "contenteditable"); 
-    /* element is editable if it's a text area or input with no type, text or
-     * pasword */
-    if (!g_ascii_strcasecmp(tagname, "textarea")) {
-        result = true;
-    } else if (!g_ascii_strcasecmp(tagname, "input")
-        && (!*type
-            || !g_ascii_strcasecmp(type, "text")
-            || !g_ascii_strcasecmp(type, "password")
-            || !g_ascii_strcasecmp(type, "color")
-            || !g_ascii_strcasecmp(type, "date")
-            || !g_ascii_strcasecmp(type, "datetime")
-            || !g_ascii_strcasecmp(type, "datetime-local")
-            || !g_ascii_strcasecmp(type, "email")
-            || !g_ascii_strcasecmp(type, "month")
-            || !g_ascii_strcasecmp(type, "number")
-            || !g_ascii_strcasecmp(type, "search")
-            || !g_ascii_strcasecmp(type, "tel")
-            || !g_ascii_strcasecmp(type, "time")
-            || !g_ascii_strcasecmp(type, "url")
-            || !g_ascii_strcasecmp(type, "week"))
-    ) {
-        result = true;
-    } else if (!g_ascii_strcasecmp(editable, "true")) {
-        result = true;
-    } else {
-        result = false;
-    }
-    g_free(tagname);
-    g_free(type);
-    g_free(editable);
-
-    return result;
-}
-
-Element *dom_get_active_element(WebKitWebView *view)
-{
-    return get_active_element(webkit_web_view_get_dom_document(view));
-}
-
-const char *dom_editable_element_get_value(Element *element)
-{
-    const char *value = NULL;
-
-    if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT((HtmlInputElement*)element)) {
-        value = webkit_dom_html_input_element_get_value((HtmlInputElement*)element);
-    } else {
-        /* we should check WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT, but this
-         * seems to return alway false */
-        value = webkit_dom_html_text_area_element_get_value((HtmlTextareaElement*)element);
-    }
-
-    return value;
-}
-
-void dom_editable_element_set_value(Element *element, const char *value)
-{
-    if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) {
-        webkit_dom_html_input_element_set_value((HtmlInputElement*)element, value);
-    } else {
-        webkit_dom_html_text_area_element_set_value((HtmlTextareaElement*)element, value);
-    }
-}
-
-void dom_editable_element_set_disable(Element *element, gboolean value)
-{
-    if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) {
-        webkit_dom_html_input_element_set_disabled((HtmlInputElement*)element, value);
-    } else {
-        webkit_dom_html_text_area_element_set_disabled((HtmlTextareaElement*)element, value);
-    }
-}
-
-static gboolean element_is_visible(WebKitDOMDOMWindow* win, WebKitDOMElement* element)
-{
-    gchar* value = NULL;
-
-    WebKitDOMCSSStyleDeclaration* style = webkit_dom_dom_window_get_computed_style(win, element, "");
-    value = webkit_dom_css_style_declaration_get_property_value(style, "visibility");
-    if (value && g_ascii_strcasecmp(value, "hidden") == 0) {
-        return false;
-    }
-    value = webkit_dom_css_style_declaration_get_property_value(style, "display");
-    if (value && g_ascii_strcasecmp(value, "none") == 0) {
-        return false;
-    }
-
-    return true;
-}
-
-static gboolean auto_insert(Element *element)
-{
-    /* Change only the mode if we are in normal mode - passthrough should not
-     * be disturbed by this and if the user iy typing into inputbox we don't
-     * want to remove the content and force a switch to input mode. And to
-     * switch to input mode when this is already active makes no sense. */
-    if (vb.mode->id == 'n' && dom_is_editable(element)) {
-        vb_enter('i');
-
-        return true;
-    }
-    return false;
-}
-
-static gboolean editable_blur_cb(Element *element, Event *event)
-{
-    if (vb.state.window_has_focus && vb.mode->id == 'i') {
-        vb_enter('n');
-    }
-
-    return false;
-}
-
-static gboolean editable_focus_cb(Element *element, Event *event)
-{
-    if (vb.state.done_loading_page || !vb.config.strict_focus) {
-        auto_insert((Element*)webkit_dom_event_get_target(event));
-    }
-
-    return false;
-}
-
-static Element *get_active_element(Document *doc)
-{
-    char *tagname;
-    Document *d = NULL;
-    Element *active, *result = NULL;
-
-    active = webkit_dom_html_document_get_active_element((void*)doc);
-    if (!active) {
-        return result;
-    }
-    tagname = webkit_dom_element_get_tag_name(active);
-
-    if (!g_strcmp0(tagname, "FRAME")) {
-        d = webkit_dom_html_frame_element_get_content_document(WEBKIT_DOM_HTML_FRAME_ELEMENT(active));
-        result = get_active_element(d);
-    } else if (!g_strcmp0(tagname, "IFRAME")) {
-        d = webkit_dom_html_iframe_element_get_content_document(WEBKIT_DOM_HTML_IFRAME_ELEMENT(active));
-        result = get_active_element(d);
-    }
-    g_free(tagname);
-
-    if (result) {
-        return result;
-    }
-
-    return active;
-}
diff --git a/src/dom.h b/src/dom.h
deleted file mode 100644 (file)
index 03eaf37..0000000
--- a/src/dom.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#ifndef _DOM_H
-#define _DOM_H
-
-#include <webkit/webkit.h>
-
-/* Types */
-#define Document            WebKitDOMDocument
-#define HtmlElement         WebKitDOMHTMLElement
-#define Element             WebKitDOMElement
-#define Node                WebKitDOMNode
-#define Event               WebKitDOMEvent
-#define EventTarget         WebKitDOMEventTarget
-#define HtmlInputElement    WebKitDOMHTMLInputElement
-#define HtmlTextareaElement WebKitDOMHTMLTextAreaElement
-
-void dom_install_focus_blur_callbacks(Document *doc);
-void dom_check_auto_insert(Document *doc);
-void dom_clear_focus(WebKitWebView *view);
-void dom_give_focus(Element *element);
-gboolean dom_focus_input(Document *doc);
-gboolean dom_is_editable(Element *element);
-Element *dom_get_active_element(WebKitWebView *view);
-const char *dom_editable_element_get_value(Element *element);
-void dom_editable_element_set_value(Element *element, const char *value);
-void dom_editable_element_set_disable(Element *element, gboolean value);
-
-#endif /* end of include guard: _DOM_H */
index bfbe418..914a554 100644 (file)
--- a/src/ex.c
+++ b/src/ex.c
  * This file contains function to handle input editing, parsing of called ex
  * commands from inputbox and the ex commands.
  */
-#include "config.h"
+
+#include <string.h>
 #include <sys/wait.h>
-#include "main.h"
-#include "ex.h"
+
 #include "ascii.h"
-#include "completion.h"
-#include "hints.h"
 #include "command.h"
+#include "completion.h"
+#include "config.h"
+#include "ex.h"
 #include "history.h"
-#include "dom.h"
+#include "main.h"
+#include "map.h"
 #include "setting.h"
 #include "util.h"
-#include "bookmark.h"
-#include "shortcut.h"
-#include "handlers.h"
-#include "map.h"
-#include "js.h"
-#ifdef FEATURE_AUTOCMD
-#include "autocmd.h"
-#endif
 
 typedef enum {
-#ifdef FEATURE_AUTOCMD
-    EX_AUTOCMD,
-    EX_AUGROUP,
-#endif
+    /* TODO add feature autocmd */
     EX_BMA,
     EX_BMR,
     EX_EVAL,
@@ -78,7 +69,6 @@ typedef enum {
     EX_SCR,
     EX_SET,
     EX_SHELLCMD,
-    EX_SOURCE,
     EX_TABOPEN,
 } ExCode;
 
@@ -98,7 +88,7 @@ typedef struct {
     int        flags;    /* flags for the already parsed command */
 } ExArg;
 
-typedef VbCmdResult (*ExFunc)(const ExArg *arg);
+typedef VbCmdResult (*ExFunc)(Client *c, const ExArg *arg);
 
 typedef struct {
     const char *name;         /* full name of the command even if called abbreviated */
@@ -118,43 +108,38 @@ static struct {
     Phase phase; /* current parsing phase */
 } info = {'\0', PHASE_START};
 
-static void input_activate(void);
-static gboolean parse(const char **input, ExArg *arg, gboolean *nohist);
+static void input_activate(Client *c);
+static gboolean parse(Client *c, const char **input, ExArg *arg, gboolean *nohist);
 static gboolean parse_count(const char **input, ExArg *arg);
-static gboolean parse_command_name(const char **input, ExArg *arg);
+static gboolean parse_command_name(Client *c, const char **input, ExArg *arg);
 static gboolean parse_bang(const char **input, ExArg *arg);
 static gboolean parse_lhs(const char **input, ExArg *arg);
-static gboolean parse_rhs(const char **input, ExArg *arg);
+static gboolean parse_rhs(Client *c, const char **input, ExArg *arg);
 static void skip_whitespace(const char **input);
 static void free_cmdarg(ExArg *arg);
-static VbCmdResult execute(const ExArg *arg);
-
-#ifdef FEATURE_AUTOCMD
-static VbCmdResult ex_augroup(const ExArg *arg);
-static VbCmdResult ex_autocmd(const ExArg *arg);
-#endif
-static VbCmdResult ex_bookmark(const ExArg *arg);
-static VbCmdResult ex_eval(const ExArg *arg);
-static VbCmdResult ex_hardcopy(const ExArg *arg);
-static VbCmdResult ex_map(const ExArg *arg);
-static VbCmdResult ex_unmap(const ExArg *arg);
-static VbCmdResult ex_normal(const ExArg *arg);
-static VbCmdResult ex_open(const ExArg *arg);
+static VbCmdResult execute(Client *c, const ExArg *arg);
+
+static VbCmdResult ex_bookmark(Client *c, const ExArg *arg);
+static VbCmdResult ex_eval(Client *c, const ExArg *arg);
+static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg);
+static VbCmdResult ex_map(Client *c, const ExArg *arg);
+static VbCmdResult ex_unmap(Client *c, const ExArg *arg);
+static VbCmdResult ex_normal(Client *c, const ExArg *arg);
+static VbCmdResult ex_open(Client *c, const ExArg *arg);
 #ifdef FEATURE_QUEUE
-static VbCmdResult ex_queue(const ExArg *arg);
+static VbCmdResult ex_queue(Client *c, const ExArg *arg);
 #endif
-static VbCmdResult ex_register(const ExArg *arg);
-static VbCmdResult ex_quit(const ExArg *arg);
-static VbCmdResult ex_save(const ExArg *arg);
-static VbCmdResult ex_set(const ExArg *arg);
-static VbCmdResult ex_shellcmd(const ExArg *arg);
-static VbCmdResult ex_shortcut(const ExArg *arg);
-static VbCmdResult ex_source(const ExArg *arg);
-static VbCmdResult ex_handlers(const ExArg *arg);
-
-static gboolean complete(short direction);
-static void completion_select(char *match);
-static gboolean history(gboolean prev);
+static VbCmdResult ex_register(Client *c, const ExArg *arg);
+static VbCmdResult ex_quit(Client *c, const ExArg *arg);
+static VbCmdResult ex_save(Client *c, const ExArg *arg);
+static VbCmdResult ex_set(Client *c, const ExArg *arg);
+static VbCmdResult ex_shellcmd(Client *c, const ExArg *arg);
+static VbCmdResult ex_shortcut(Client *c, const ExArg *arg);
+static VbCmdResult ex_handlers(Client *c, const ExArg *arg);
+
+static gboolean complete(Client *c, short direction);
+static void completion_select(Client *c, char *match);
+static gboolean history(Client *c, gboolean prev);
 static void history_rewind(void);
 
 /* The order of following command names is significant. If there exists
@@ -165,10 +150,6 @@ static void history_rewind(void);
  * match. */
 static ExInfo commands[] = {
     /* command           code            func           flags */
-#ifdef FEATURE_AUTOCMD
-    {"autocmd",          EX_AUTOCMD,     ex_autocmd,    EX_FLAG_CMD|EX_FLAG_BANG},
-    {"augroup",          EX_AUGROUP,     ex_augroup,    EX_FLAG_LHS|EX_FLAG_BANG},
-#endif
     {"bma",              EX_BMA,         ex_bookmark,   EX_FLAG_RHS},
     {"bmr",              EX_BMR,         ex_bookmark,   EX_FLAG_RHS},
     {"cmap",             EX_CMAP,        ex_map,        EX_FLAG_LHS|EX_FLAG_CMD},
@@ -200,7 +181,6 @@ static ExInfo commands[] = {
     {"shortcut-add",     EX_SCA,         ex_shortcut,   EX_FLAG_RHS},
     {"shortcut-default", EX_SCD,         ex_shortcut,   EX_FLAG_RHS},
     {"shortcut-remove",  EX_SCR,         ex_shortcut,   EX_FLAG_RHS},
-    {"source",           EX_SOURCE,      ex_source,     EX_FLAG_RHS|EX_FLAG_EXP},
     {"tabopen",          EX_TABOPEN,     ex_open,       EX_FLAG_CMD},
 };
 
@@ -217,45 +197,43 @@ static struct {
     GList *active;
 } exhist;
 
-extern VbCore vb;
-
+extern struct Vimb vb;
 
 /**
  * Function called when vimb enters the command mode.
  */
-void ex_enter(void)
+void ex_enter(Client *c)
 {
-    gtk_widget_grab_focus(GTK_WIDGET(vb.gui.input));
-    dom_clear_focus(vb.gui.webview);
+    gtk_widget_grab_focus(GTK_WIDGET(c->input));
+#if 0
+    dom_clear_focus(c->webview);
+#endif
 }
 
 /**
  * Called when the command mode is left.
  */
-void ex_leave(void)
+void ex_leave(Client *c)
 {
-    completion_clean();
+    completion_clean(c);
+#if 0
     hints_clear();
+#endif
 }
 
 /**
  * Handles the keypress events from webview and inputbox.
  */
-VbResult ex_keypress(int key)
+VbResult ex_keypress(Client *c, int key)
 {
     GtkTextIter start, end;
-    gboolean check_empty  = false;
-    GtkTextBuffer *buffer = vb.gui.buffer;
+    gboolean check_empty  = FALSE;
+    GtkTextBuffer *buffer = c->buffer;
     GtkTextMark *mark;
     VbResult res;
     const char *text;
 
-    /* delegate call to hint mode if this is active */
-    if (vb.mode->flags & FLAG_HINTING
-        && RESULT_COMPLETE == hints_keypress(key)) {
-
-        return RESULT_COMPLETE;
-    }
+    /* TODO delegate call to hint mode if this is active */
 
     /* process the register */
     if (info.phase == PHASE_REG) {
@@ -263,7 +241,7 @@ VbResult ex_keypress(int key)
         info.phase = PHASE_REG;
 
         /* insert the register text at cursor position */
-        text = vb_register_get((char)key);
+        text = vb_register_get(c, (char)key);
         if (text) {
             gtk_text_buffer_insert_at_cursor(buffer, text, strlen(text));
         }
@@ -273,29 +251,29 @@ VbResult ex_keypress(int key)
         res = RESULT_COMPLETE;
         switch (key) {
             case KEY_TAB:
-                complete(1);
+                complete(c, 1);
                 break;
 
             case KEY_SHIFT_TAB:
-                complete(-1);
+                complete(c, -1);
                 break;
 
-            case CTRL('['):
-            case CTRL('C'):
-                vb_enter('n');
-                vb_set_input_text("");
+            case KEY_UP:
+                history(c, TRUE);
                 break;
 
-            case KEY_CR:
-                input_activate();
+            case KEY_DOWN:
+                history(c, FALSE);
                 break;
 
-            case KEY_UP:
-                history(true);
+            case KEY_CR:
+                input_activate(c);
                 break;
 
-            case KEY_DOWN:
-                history(false);
+            case CTRL('['):
+            case CTRL('C'):
+                vb_enter(c, 'n');
+                vb_input_set_text(c, "");
                 break;
 
             /* basic command line editing */
@@ -303,8 +281,8 @@ VbResult ex_keypress(int key)
                 /* delete the last char before the cursor */
                 mark = gtk_text_buffer_get_insert(buffer);
                 gtk_text_buffer_get_iter_at_mark(buffer, &start, mark);
-                gtk_text_buffer_backspace(buffer, &start, true, true);
-                check_empty = true;
+                gtk_text_buffer_backspace(buffer, &start, TRUE, TRUE);
+                check_empty = TRUE;
                 break;
 
             case CTRL('W'):
@@ -319,12 +297,12 @@ VbResult ex_keypress(int key)
                 if (gtk_text_iter_backward_word_start(&start)) {
                     gtk_text_buffer_delete(buffer, &start, &end);
                 }
-                check_empty = true;
+                check_empty = TRUE;
                 break;
 
             case CTRL('B'):
                 /* move the cursor direct behind the prompt */
-                gtk_text_buffer_get_iter_at_offset(buffer, &start, strlen(vb.state.prompt));
+                gtk_text_buffer_get_iter_at_offset(buffer, &start, strlen(c->state.prompt));
                 gtk_text_buffer_place_cursor(buffer, &start);
                 break;
 
@@ -338,13 +316,13 @@ VbResult ex_keypress(int key)
                 /* remove everything between cursor and prompt */
                 mark = gtk_text_buffer_get_insert(buffer);
                 gtk_text_buffer_get_iter_at_mark(buffer, &end, mark);
-                gtk_text_buffer_get_iter_at_offset(buffer, &start, strlen(vb.state.prompt));
+                gtk_text_buffer_get_iter_at_offset(buffer, &start, strlen(c->state.prompt));
                 gtk_text_buffer_delete(buffer, &start, &end);
                 break;
 
             case CTRL('R'):
                 info.phase      = PHASE_REG;
-                vb.mode->flags |= FLAG_NOMAP;
+                c->mode->flags |= FLAG_NOMAP;
                 res             = RESULT_MORE;
                 break;
 
@@ -354,7 +332,7 @@ VbResult ex_keypress(int key)
                 if (key >= 0x20 && key <= 0x7e) {
                     gtk_text_buffer_insert_at_cursor(buffer, (char[2]){key, 0}, 1);
                 } else {
-                    vb.state.processed_key = false;
+                    c->state.processed_key = FALSE;
                 }
         }
     }
@@ -364,8 +342,8 @@ VbResult ex_keypress(int key)
     if (check_empty) {
         gtk_text_buffer_get_bounds(buffer, &start, &end);
         if (gtk_text_iter_equal(&start, &end)) {
-            vb_enter('n');
-            vb_set_input_text("");
+            vb_enter(c, 'n');
+            vb_input_set_text(c, "");
         }
     }
 
@@ -380,11 +358,10 @@ VbResult ex_keypress(int key)
 /**
  * Handles changes in the inputbox.
  */
-void ex_input_changed(const char *text)
+void ex_input_changed(Client *c, const char *text)
 {
-    gboolean forward = false;
     GtkTextIter start, end;
-    GtkTextBuffer *buffer = vb.gui.buffer;
+    GtkTextBuffer *buffer = c->buffer;
 
     /* don't add line breaks if content is pasted from clipboard into inputbox */
     if (gtk_text_buffer_get_line_count(buffer) > 1) {
@@ -399,15 +376,7 @@ void ex_input_changed(const char *text)
     switch (*text) {
         case ';': /* fall through - the modes are handled by hints_create */
         case 'g':
-            hints_create(text);
-            break;
-
-        case '/': forward = true; /* fall through */
-        case '?':
-#ifdef FEATURE_SEARCH_HIGHLIGHT
-            webkit_web_view_unmark_text_matches(vb.gui.webview);
-#endif
-            webkit_web_view_search_text(vb.gui.webview, &text[1], false, forward, false);
+            /* TODO create hints */
             break;
     }
 }
@@ -416,14 +385,14 @@ gboolean ex_fill_completion(GtkListStore *store, const char *input)
 {
     GtkTreeIter iter;
     ExInfo *cmd;
-    gboolean found = false;
+    gboolean found = FALSE;
 
     if (!input || *input == '\0') {
         for (int i = 0; i < LENGTH(commands); i++) {
             cmd = &commands[i];
             gtk_list_store_append(store, &iter);
             gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, cmd->name, -1);
-            found = true;
+            found = TRUE;
         }
     } else {
         for (int i = 0; i < LENGTH(commands); i++) {
@@ -431,7 +400,7 @@ gboolean ex_fill_completion(GtkListStore *store, const char *input)
             if (g_str_has_prefix(cmd->name, input)) {
                 gtk_list_store_append(store, &iter);
                 gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, cmd->name, -1);
-                found = true;
+                found = TRUE;
             }
         }
     }
@@ -442,33 +411,34 @@ gboolean ex_fill_completion(GtkListStore *store, const char *input)
 /**
  * This is called if the user typed <nl> or <cr> into the inputbox.
  */
-static void input_activate(void)
+static void input_activate(Client *c)
 {
     int count = -1;
     char *text, *cmd;
     VbCmdResult res;
-    text = vb_get_input_text();
+
+    text = vb_input_get_text(c);
 
     /* skip leading prompt char like ':' or '/' */
     cmd = text + 1;
     switch (*text) {
         case '/': count = 1; /* fall through */
         case '?':
-            vb_enter('n');
-            command_search(&((Arg){count, cmd}));
+            vb_enter(c, 'n');
+            command_search(c, &((Arg){count, cmd}));
             break;
 
         case ';': /* fall through */
         case 'g':
-            hints_fire();
+            /* TODO fire hints */
             break;
 
         case ':':
-            vb_enter('n');
-            res = ex_run_string(cmd, true);
-            if (!(res & VB_CMD_KEEPINPUT)) {
+            vb_enter(c, 'n');
+            res = ex_run_string(c, cmd, TRUE);
+            if (!(res & CMD_KEEPINPUT)) {
                 /* clear input on success if this is not explicit ommited */
-                vb_set_input_text("");
+                vb_input_set_text(c, "");
             }
             break;
 
@@ -476,25 +446,25 @@ static void input_activate(void)
     g_free(text);
 }
 
-VbCmdResult ex_run_string(const char *input, gboolean enable_history)
+VbCmdResult ex_run_string(Client *c, const char *input, gboolean enable_history)
 {
     /* copy to have original command for history */
     const char *in  = input;
-    gboolean nohist = false;
-    VbCmdResult res = VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+    gboolean nohist = FALSE;
+    VbCmdResult res = CMD_ERROR | CMD_KEEPINPUT;
     ExArg *arg = g_slice_new0(ExArg);
     arg->lhs   = g_string_new("");
     arg->rhs   = g_string_new("");
 
     while (in && *in) {
-        if (!parse(&in, arg, &nohist) || !(res = execute(arg))) {
+        if (!parse(c, &in, arg, &nohist) || !(res = execute(c, arg))) {
             break;
         }
     }
 
     if (enable_history && !nohist) {
-        history_add(HISTORY_COMMAND, input, NULL);
-        vb_register_add(':', input);
+        history_add(c, HISTORY_COMMAND, input, NULL);
+        vb_register_add(c, ':', input);
     }
 
     free_cmdarg(arg);
@@ -502,44 +472,13 @@ VbCmdResult ex_run_string(const char *input, gboolean enable_history)
     return res;
 }
 
-/**
- * Run all ex commands in a file.
- */
-VbCmdResult ex_run_file(const char *filename)
-{
-    char *line, **lines;
-    VbCmdResult res = VB_CMD_SUCCESS;
-
-    lines = util_get_lines(filename);
-
-    if (!lines) {
-        return res;
-    }
-
-    int length = g_strv_length(lines) - 1;
-    for (int i = 0; i < length; i++) {
-        line = lines[i];
-        /* skip commented or empty lines */
-        if (*line == '#' || !*line) {
-            continue;
-        }
-        if ((ex_run_string(line, false) & ~VB_CMD_KEEPINPUT) == VB_CMD_ERROR) {
-            res = VB_CMD_ERROR | VB_CMD_KEEPINPUT;
-            g_warning("Invalid command in %s: '%s'", filename, line);
-        }
-    }
-    g_strfreev(lines);
-
-    return res;
-}
-
 /**
  * Parses given input string into given ExArg pointer.
  */
-static gboolean parse(const char **input, ExArg *arg, gboolean *nohist)
+static gboolean parse(Client *c, const char **input, ExArg *arg, gboolean *nohist)
 {
     if (!*input || !**input) {
-        return false;
+        return FALSE;
     }
 
     /* truncate string from potentially previous run */
@@ -551,13 +490,13 @@ static gboolean parse(const char **input, ExArg *arg, gboolean *nohist)
         (*input)++;
         /* Command started with additional ':' or whitespce - don't record it
          * in history or registry. */
-        *nohist = true;
+        *nohist = TRUE;
     }
     parse_count(input, arg);
 
     skip_whitespace(input);
-    if (!parse_command_name(input, arg)) {
-        return false;
+    if (!parse_command_name(c, input, arg)) {
+        return FALSE;
     }
 
     /* parse the bang if this is allowed */
@@ -572,13 +511,13 @@ static gboolean parse(const char **input, ExArg *arg, gboolean *nohist)
     }
     /* parse the rhs if this is available */
     skip_whitespace(input);
-    parse_rhs(input, arg);
+    parse_rhs(c, input, arg);
 
     if (**input) {
         (*input)++;
     }
 
-    return true;
+    return TRUE;
 }
 
 /**
@@ -594,13 +533,13 @@ static gboolean parse_count(const char **input, ExArg *arg)
             (*input)++;
         } while (VB_IS_DIGIT(**input));
     }
-    return true;
+    return TRUE;
 }
 
 /**
  * Parse the command name from given input.
  */
-static gboolean parse_command_name(const char **input, ExArg *arg)
+static gboolean parse_command_name(Client *c, const char **input, ExArg *arg)
 {
     int len      = 0;
     int first    = 0;   /* number of first found command */
@@ -638,8 +577,8 @@ static gboolean parse_command_name(const char **input, ExArg *arg)
         }
         cmd[len] = '\0';
 
-        vb_echo(VB_MSG_ERROR, true, "Unknown command: %s", cmd);
-        return false;
+        vb_echo(c, MSG_ERROR, TRUE, "Unknown command: %s", cmd);
+        return FALSE;
     }
 
     arg->idx   = first;
@@ -647,7 +586,7 @@ static gboolean parse_command_name(const char **input, ExArg *arg)
     arg->name  = commands[first].name;
     arg->flags = commands[first].flags;
 
-    return true;
+    return TRUE;
 }
 
 /**
@@ -656,10 +595,10 @@ static gboolean parse_command_name(const char **input, ExArg *arg)
 static gboolean parse_bang(const char **input, ExArg *arg)
 {
     if (*input && **input == '!') {
-        arg->bang = true;
+        arg->bang = TRUE;
         (*input)++;
     }
-    return true;
+    return TRUE;
 }
 
 /**
@@ -670,7 +609,7 @@ static gboolean parse_lhs(const char **input, ExArg *arg)
     char quote = '\\';
 
     if (!*input || !**input) {
-        return false;
+        return FALSE;
     }
 
     /* get the char until the next none escaped whitespace and save it into
@@ -683,9 +622,8 @@ static gboolean parse_lhs(const char **input, ExArg *arg)
             if (!*input) {
                 /* if input ends here - use only the backslash */
                 g_string_append_c(arg->lhs, quote);
-            } else if (**input == ' ' || **input == quote) {
-                /* Escaped whitespace becomes only whitespace and escaped '\'
-                 * becomes '\' */
+            } else if (**input == ' ') {
+                /* escaped whitespace becomes only whitespace */
                 g_string_append_c(arg->lhs, **input);
             } else {
                 /* put escape char and next char into the result string */
@@ -698,7 +636,7 @@ static gboolean parse_lhs(const char **input, ExArg *arg)
         }
         (*input)++;
     }
-    return true;
+    return TRUE;
 }
 
 /**
@@ -706,7 +644,7 @@ static gboolean parse_lhs(const char **input, ExArg *arg)
  * command can contain any char accept of the newline, else the right hand
  * side end on the first none escaped | or newline.
  */
-static gboolean parse_rhs(const char **input, ExArg *arg)
+static gboolean parse_rhs(Client *c, const char **input, ExArg *arg)
 {
     int expflags, flags;
     gboolean cmdlist;
@@ -716,7 +654,7 @@ static gboolean parse_rhs(const char **input, ExArg *arg)
     if ((arg->flags & (EX_FLAG_RHS|EX_FLAG_CMD)) == 0
         || !*input || !**input
     ) {
-        return false;
+        return FALSE;
     }
 
     cmdlist  = (arg->flags & EX_FLAG_CMD) != 0;
@@ -729,7 +667,7 @@ static gboolean parse_rhs(const char **input, ExArg *arg)
      * EX_FLAG_CMD is not set also on | */
     while (**input && **input != '\n' && (cmdlist || **input != '|')) {
         /* check for expansion placeholder */
-        util_parse_expansion(input, arg->rhs, flags, "|\\");
+        util_parse_expansion(c, input, arg->rhs, flags, "|\\");
 
         if (VB_IS_SEPARATOR(**input)) {
             /* add tilde expansion for next loop needs to be first char or to
@@ -741,15 +679,15 @@ static gboolean parse_rhs(const char **input, ExArg *arg)
         }
         (*input)++;
     }
-    return true;
+    return TRUE;
 }
 
 /**
  * Executes the command given by ExArg.
  */
-static VbCmdResult execute(const ExArg *arg)
+static VbCmdResult execute(Client *c, const ExArg *arg)
 {
-    return (commands[arg->idx].func)(arg);
+    return (commands[arg->idx].func)(c, arg);
 }
 
 static void skip_whitespace(const char **input)
@@ -762,210 +700,136 @@ static void skip_whitespace(const char **input)
 static void free_cmdarg(ExArg *arg)
 {
     if (arg->lhs) {
-        g_string_free(arg->lhs, true);
+        g_string_free(arg->lhs, TRUE);
     }
     if (arg->rhs) {
-        g_string_free(arg->rhs, true);
+        g_string_free(arg->rhs, TRUE);
     }
     g_slice_free(ExArg, arg);
 }
 
-#ifdef FEATURE_AUTOCMD
-static VbCmdResult ex_augroup(const ExArg *arg)
-{
-    return autocmd_augroup(arg->lhs->str, arg->bang) ? VB_CMD_SUCCESS : VB_CMD_ERROR;
-}
-
-static VbCmdResult ex_autocmd(const ExArg *arg)
-{
-    return autocmd_add(arg->rhs->str, arg->bang) ? VB_CMD_SUCCESS : VB_CMD_ERROR;
-}
-#endif
-
-static VbCmdResult ex_bookmark(const ExArg *arg)
+static VbCmdResult ex_bookmark(Client *c, const ExArg *arg)
 {
-    if (arg->code == EX_BMR) {
-        if (bookmark_remove(*arg->rhs->str ? arg->rhs->str : vb.state.uri)) {
-            vb_echo_force(VB_MSG_NORMAL, true, "  Bookmark removed");
-
-            return VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
-        }
-    } else if (bookmark_add(vb.state.uri, webkit_web_view_get_title(vb.gui.webview), arg->rhs->str)) {
-        vb_echo_force(VB_MSG_NORMAL, true, "  Bookmark added");
-
-        return VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
-    }
-
-    return VB_CMD_ERROR;
+    /* TODO no implemented yet */
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_eval(const ExArg *arg)
+static VbCmdResult ex_eval(Client *c, const ExArg *arg)
 {
-    gboolean success;
-    char *value  = NULL;
-    VbCmdResult res = VB_CMD_SUCCESS;
-
-    if (!arg->rhs->len) {
-        return false;
-    }
-
-    success = js_eval(
-        webkit_web_frame_get_global_context(webkit_web_view_get_main_frame(vb.gui.webview)),
-        arg->rhs->str, NULL, &value
-    );
-    if (!arg->bang) {
-        if (success) {
-            vb_echo(VB_MSG_NORMAL, false, "%s", value);
-            res = VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
-        } else {
-            vb_echo(VB_MSG_ERROR, true, "%s", value);
-            res = VB_CMD_ERROR | VB_CMD_KEEPINPUT;
-        }
-    }
-    g_free(value);
-
-    return res;
+    /* TODO allow to get the return value and possible errors. */
+    webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, NULL, NULL);
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_hardcopy(const ExArg *arg)
+static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg)
 {
-    webkit_web_frame_print(webkit_web_view_get_main_frame(vb.gui.webview));
-    return VB_CMD_SUCCESS;
+    /* TODO no implemented yet */
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_map(const ExArg *arg)
+static VbCmdResult ex_map(Client *c, const ExArg *arg)
 {
     if (!arg->lhs->len || !arg->rhs->len) {
-        return VB_CMD_ERROR;
+        return CMD_ERROR;
     }
 
     /* instead of using the EX_XMAP constants we use the first char of the
      * command name as mode and the second to determine if noremap is used */
-    map_insert(arg->lhs->str, arg->rhs->str, arg->name[0], arg->name[1] != 'n');
+    map_insert(c, arg->lhs->str, arg->rhs->str, arg->name[0], arg->name[1] != 'n');
 
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_unmap(const ExArg *arg)
+static VbCmdResult ex_unmap(Client *c, const ExArg *arg)
 {
     char *lhs;
     if (!arg->lhs->len) {
-        return VB_CMD_ERROR;
+        return CMD_ERROR;
     }
 
     lhs = arg->lhs->str;
 
     if (arg->code == EX_NUNMAP) {
-        map_delete(lhs, 'n');
+        map_delete(c, lhs, 'n');
     } else if (arg->code == EX_CUNMAP) {
-        map_delete(lhs, 'c');
+        map_delete(c, lhs, 'c');
     } else {
-        map_delete(lhs, 'i');
+        map_delete(c, lhs, 'i');
     }
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_normal(const ExArg *arg)
+static VbCmdResult ex_normal(Client *c, const ExArg *arg)
 {
-    vb_enter('n');
+    vb_enter(c, 'n');
 
     /* if called with bang - don't apply mapping */
-    map_handle_string(arg->rhs->str, !arg->bang);
+    map_handle_string(c, arg->rhs->str, !arg->bang);
 
-    return VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
+    return CMD_SUCCESS | CMD_KEEPINPUT;
 }
 
-static VbCmdResult ex_open(const ExArg *arg)
+static VbCmdResult ex_open(Client *c, const ExArg *arg)
 {
     if (arg->code == EX_TABOPEN) {
-        return vb_load_uri(&((Arg){VB_TARGET_NEW, arg->rhs->str})) ? VB_CMD_SUCCESS : VB_CMD_ERROR;
+        return vb_load_uri(c, &((Arg){TARGET_NEW, arg->rhs->str})) ? CMD_SUCCESS : CMD_ERROR;
     }
-    return vb_load_uri(&((Arg){VB_TARGET_CURRENT, arg->rhs->str})) ? VB_CMD_SUCCESS :VB_CMD_ERROR;
+    return vb_load_uri(c, &((Arg){TARGET_CURRENT, arg->rhs->str})) ? CMD_SUCCESS :CMD_ERROR;
 }
 
 #ifdef FEATURE_QUEUE
-static VbCmdResult ex_queue(const ExArg *arg)
+static VbCmdResult ex_queue(Client *c, const ExArg *arg)
 {
-    Arg a = {0};
-
-    switch (arg->code) {
-        case EX_QPUSH:
-            a.i = COMMAND_QUEUE_PUSH;
-            break;
-
-        case EX_QUNSHIFT:
-            a.i = COMMAND_QUEUE_UNSHIFT;
-            break;
-
-        case EX_QPOP:
-            a.i = COMMAND_QUEUE_POP;
-            break;
-
-        case EX_QCLEAR:
-            a.i = COMMAND_QUEUE_CLEAR;
-            break;
-
-        default:
-            return VB_CMD_ERROR;
-    }
-
-    /* if no argument is found in rhs, keep the uri in arg null to force
-     * command_queue() to use current URI */
-    if (arg->rhs->len) {
-        a.s = arg->rhs->str;
-    }
-
-    return command_queue(&a)
-        ? VB_CMD_SUCCESS | VB_CMD_KEEPINPUT
-        : VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+    /* TODO no implemented yet */
+    return CMD_SUCCESS;
 }
 #endif
 
 /**
  * Show the contents of the registers :reg.
  */
-static VbCmdResult ex_register(const ExArg *arg)
+static VbCmdResult ex_register(Client *c, const ExArg *arg)
 {
     int idx;
     char *reg;
-    const char *regchars = VB_REG_CHARS;
+    const char *regchars = REG_CHARS;
     GString *str = g_string_new("-- Register --");
 
-    for (idx = 0; idx < VB_REG_SIZE; idx++) {
+    for (idx = 0; idx < REG_SIZE; idx++) {
         /* show only filled registers */
-        if (vb.state.reg[idx]) {
+        if (c->state.reg[idx]) {
             /* replace all newlines */
-            reg = util_str_replace("\n", "^J", vb.state.reg[idx]);
+            reg = util_str_replace("\n", "^J", c->state.reg[idx]);
             g_string_append_printf(str, "\n\"%c   %s", regchars[idx], reg);
             g_free(reg);
         }
     }
 
-    vb_echo(VB_MSG_NORMAL, false, "%s", str->str);
-    g_string_free(str, true);
+    vb_echo(c, MSG_NORMAL, FALSE, "%s", str->str);
+    g_string_free(str, TRUE);
 
-    return VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
+    return CMD_SUCCESS | CMD_KEEPINPUT;
 }
 
-static VbCmdResult ex_quit(const ExArg *arg)
+static VbCmdResult ex_quit(Client *c, const ExArg *arg)
 {
-    vb_quit(arg->bang);
-    return VB_CMD_SUCCESS;
+    vb_quit(c, arg->bang);
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_save(const ExArg *arg)
+static VbCmdResult ex_save(Client *c, const ExArg *arg)
 {
-    return command_save(&((Arg){COMMAND_SAVE_CURRENT, arg->rhs->str}))
-        ? VB_CMD_SUCCESS | VB_CMD_KEEPINPUT
-        : VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+    return command_save(c, &((Arg){COMMAND_SAVE_CURRENT, arg->rhs->str}))
+        ? CMD_SUCCESS | CMD_KEEPINPUT
+        : CMD_ERROR | CMD_KEEPINPUT;
 }
 
-static VbCmdResult ex_set(const ExArg *arg)
+static VbCmdResult ex_set(Client *c, const ExArg *arg)
 {
     char *param = NULL;
 
     if (!arg->rhs->len) {
-        return false;
+        return FALSE;
     }
 
     /* split the input string into parameter and value part */
@@ -974,13 +838,13 @@ static VbCmdResult ex_set(const ExArg *arg)
         g_strstrip(arg->rhs->str);
         g_strstrip(param);
 
-        return setting_run(arg->rhs->str, param);
+        return setting_run(c, arg->rhs->str, param);
     }
 
-    return setting_run(arg->rhs->str, NULL);
+    return setting_run(c, arg->rhs->str, NULL);
 }
 
-static VbCmdResult ex_shellcmd(const ExArg *arg)
+static VbCmdResult ex_shellcmd(Client *c, const ExArg *arg)
 {
     int status;
     char *stdOut = NULL, *stdErr = NULL;
@@ -988,94 +852,48 @@ static VbCmdResult ex_shellcmd(const ExArg *arg)
     GError *error = NULL;
 
     if (!*arg->rhs->str) {
-        return VB_CMD_ERROR;
+        return CMD_ERROR;
     }
 
     if (arg->bang) {
         if (!g_spawn_command_line_async(arg->rhs->str, &error)) {
             g_warning("Can't run '%s': %s", arg->rhs->str, error->message);
             g_clear_error(&error);
-            res = VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+            res = CMD_ERROR | CMD_KEEPINPUT;
         } else {
-            res = VB_CMD_SUCCESS;
+            res = CMD_SUCCESS;
         }
     } else {
         if (!g_spawn_command_line_sync(arg->rhs->str, &stdOut, &stdErr, &status, &error)) {
             g_warning("Can't run '%s': %s", arg->rhs->str, error->message);
             g_clear_error(&error);
-            res = VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+            res = CMD_ERROR | CMD_KEEPINPUT;
         } else {
             /* the commands success depends not on the return code of the
              * called shell command, so we know the result already here */
-            res = VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
+            res = CMD_SUCCESS | CMD_KEEPINPUT;
         }
 
         if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
-            vb_echo(VB_MSG_NORMAL, false, "%s", stdOut);
+            vb_echo(c, MSG_NORMAL, FALSE, "%s", stdOut);
         } else {
-            vb_echo(VB_MSG_ERROR, true, "[%d] %s", WEXITSTATUS(status), stdErr);
+            vb_echo(c, MSG_ERROR, TRUE, "[%d] %s", WEXITSTATUS(status), stdErr);
         }
     }
 
     return res;
 }
 
-static VbCmdResult ex_source(const ExArg *arg)
+static VbCmdResult ex_handlers(Client *c, const ExArg *arg)
 {
-    return ex_run_file(arg->rhs->str);
+    /* TODO no implemented yet */
+    return CMD_SUCCESS;
 }
 
-static VbCmdResult ex_handlers(const ExArg *arg)
+static VbCmdResult ex_shortcut(Client *c, const ExArg *arg)
 {
-    char *p;
-    gboolean success = false;
-
-    switch (arg->code) {
-        case EX_HANDADD:
-            if (arg->rhs->len && (p = strchr(arg->rhs->str, '='))) {
-                *p++ = '\0';
-                success = handler_add(arg->rhs->str, p);
-            }
-            break;
-
-        case EX_HANDREM:
-            success = handler_remove(arg->rhs->str);
-            break;
-
-        default:
-            break;
-    }
-
-    return success ? VB_CMD_SUCCESS : VB_CMD_ERROR;
-}
-
-static VbCmdResult ex_shortcut(const ExArg *arg)
-{
-    char *p;
-    gboolean success = false;
-
-    /* TODO allow to set shortcuts with set command like ':set
-     * shortcut[name]=http://donain.tld/?q=$0' */
-    switch (arg->code) {
-        case EX_SCA:
-            if (arg->rhs->len && (p = strchr(arg->rhs->str, '='))) {
-                *p++ = '\0';
-                success = shortcut_add(arg->rhs->str, p);
-            }
-            break;
-
-        case EX_SCR:
-            success = shortcut_remove(arg->rhs->str);
-            break;
-
-        case EX_SCD:
-            success = shortcut_set_default(arg->rhs->str);
-            break;
-
-        default:
-            break;
-    }
-    return success ? VB_CMD_SUCCESS : VB_CMD_ERROR;
+    /* TODO no implemented yet */
+    return CMD_SUCCESS;
 }
 
 /**
@@ -1084,38 +902,38 @@ static VbCmdResult ex_shortcut(const ExArg *arg)
  * put hte matched data back to inputbox, and prepares the tree list store
  * model containing matched values.
  */
-static gboolean complete(short direction)
+static gboolean complete(Client *c, short direction)
 {
     char *input;            /* input read from inputbox */
     const char *in;         /* pointer to input that we move */
-    gboolean found = false;
-    gboolean sort  = true;
+    gboolean found = FALSE;
+    gboolean sort  = TRUE;
     GtkListStore *store;
 
     /* if direction is 0 stop the completion */
     if (!direction) {
-        completion_clean();
+        completion_clean(c);
 
-        return true;
+        return TRUE;
     }
 
-    input = vb_get_input_text();
+    input = vb_input_get_text(c);
     /* if completion was already started move to the next/prev item */
-    if (vb.mode->flags & FLAG_COMPLETION) {
+    if (c->mode->flags & FLAG_COMPLETION) {
         if (excomp.current && !strcmp(input, excomp.current)) {
             /* Step through the next/prev completion item. */
-            if (!completion_next(direction < 0)) {
+            if (!completion_next(c, direction < 0)) {
                 /* If we stepped over the last/first item - put the initial content in */
-                completion_select(excomp.token);
+                completion_select(c, excomp.token);
             }
             g_free(input);
 
-            return true;
+            return TRUE;
         }
 
         /* if current input isn't the content of the completion item, stop
          * completion and start it after that again */
-        completion_clean();
+        completion_clean(c);
     }
 
     store = gtk_list_store_new(COMPLETION_STORE_NUM, G_TYPE_STRING, G_TYPE_STRING);
@@ -1137,7 +955,7 @@ static gboolean complete(short direction)
 
         /* Do ex command specific completion if the comman is recognized and
          * there is a space after the command and the optional '!' bang. */
-        if (parse_command_name(&in, arg) && parse_bang(&in, arg) && VB_IS_SPACE(*in)) {
+        if (parse_command_name(c, &in, arg) && parse_bang(&in, arg) && VB_IS_SPACE(*in)) {
             const char *token;
             /* Get only the last word of input string for the completion for
              * bookmark tag completion. */
@@ -1165,43 +983,33 @@ static gboolean complete(short direction)
             switch (arg->code) {
                 case EX_OPEN:
                 case EX_TABOPEN:
-                    if (*token == '!') {
-                        found = bookmark_fill_completion(store, token + 1);
-                    } else {
-                        found = history_fill_completion(store, HISTORY_URL, token);
-                    }
-                    sort = false;
+                    /* TODO add bookmark completion if *token == '!' */
+                    found = history_fill_completion(store, HISTORY_URL, token);
                     break;
 
                 case EX_SET:
-                    found = setting_fill_completion(store, token);
+                    sort  = TRUE;
+                    found = setting_fill_completion(c, store, token);
                     break;
 
                 case EX_BMA:
-                    found = bookmark_fill_tag_completion(store, token);
+                    sort  = TRUE;
+                    /* TODO fill bookmark completion */
                     break;
 
                 case EX_SCR:
-                    found = shortcut_fill_completion(store, token);
+                    sort  = TRUE;
+                    /* TODO fill shortcut completion */
                     break;
 
                 case EX_HANDREM:
-                    found = handler_fill_completion(store, token);
-                    break;
-
-#ifdef FEATURE_AUTOCMD
-                case EX_AUTOCMD:
-                    found = autocmd_fill_event_completion(store, token);
+                    sort  = TRUE;
+                    /* TODO fill handler completion */
                     break;
 
-                case EX_AUGROUP:
-                    found = autocmd_fill_group_completion(store, token);
-                    break;
-#endif
-
                 case EX_SAVE:
-                case EX_SOURCE:
-                    found = util_filename_fill_completion(store, token);
+                    sort  = TRUE;
+                    found = util_filename_fill_completion(c, store, token);
                     break;
 
                 default:
@@ -1218,8 +1026,8 @@ static gboolean complete(short direction)
 
             if (ex_fill_completion(store, in)) {
                 OVERWRITE_STRING(excomp.prefix, ":");
-                found = true;
-                sort  = false;
+                found = TRUE;
+                sort  = FALSE;
             }
         }
         free_cmdarg(arg);
@@ -1227,7 +1035,7 @@ static gboolean complete(short direction)
         if (history_fill_completion(store, HISTORY_SEARCH, in + 1)) {
             OVERWRITE_STRING(excomp.token, in + 1);
             OVERWRITE_NSTRING(excomp.prefix, in, 1);
-            found = true;
+            found = TRUE;
         }
     }
 
@@ -1239,11 +1047,11 @@ static gboolean complete(short direction)
     }
 
     if (found) {
-        completion_create(GTK_TREE_MODEL(store), completion_select, direction < 0);
+        completion_create(c, GTK_TREE_MODEL(store), completion_select, direction < 0);
     }
 
     g_free(input);
-    return true;
+    return TRUE;
 }
 
 /**
@@ -1251,7 +1059,7 @@ static gboolean complete(short direction)
  * matched item according with previously saved prefix and command name to the
  * inputbox.
  */
-static void completion_select(char *match)
+static void completion_select(Client *c, char *match)
 {
     OVERWRITE_STRING(excomp.current, NULL);
 
@@ -1260,15 +1068,15 @@ static void completion_select(char *match)
     } else {
         excomp.current = g_strconcat(excomp.prefix, match, NULL);
     }
-    vb_set_input_text(excomp.current);
+    vb_input_set_text(c, excomp.current);
 }
 
-static gboolean history(gboolean prev)
+static gboolean history(Client *c, gboolean prev)
 {
     char *input;
     GList *new = NULL;
 
-    input = vb_get_input_text();
+    input = vb_input_get_text(c);
     if (exhist.active) {
         /* calculate the actual content of the inpubox from history data, if
          * the theoretical content and the actual given input are different
@@ -1293,11 +1101,11 @@ static gboolean history(gboolean prev)
 
         /* check which type of history we should use */
         if (*in == ':') {
-            type = VB_INPUT_COMMAND;
+            type = INPUT_COMMAND;
         } else if (*in == '/' || *in == '?') {
             /* the history does not distinguish between forward and backward
              * search, so we don't need the backward search here too */
-            type = VB_INPUT_SEARCH_FORWARD;
+            type = INPUT_SEARCH_FORWARD;
         } else {
             goto failed;
         }
@@ -1317,14 +1125,14 @@ static gboolean history(gboolean prev)
         exhist.active = new;
     }
 
-    vb_echo_force(VB_MSG_NORMAL, false, "%s%s", exhist.prefix, (char*)exhist.active->data);
+    vb_echo_force(c, MSG_NORMAL, FALSE, "%s%s", exhist.prefix, (char*)exhist.active->data);
 
     g_free(input);
-    return true;
+    return TRUE;
 
 failed:
     g_free(input);
-    return false;
+    return FALSE;
 }
 
 static void history_rewind(void)
index cbd1281..1173d90 100644 (file)
--- a/src/ex.h
+++ b/src/ex.h
 #include "config.h"
 #include "main.h"
 
-void ex_enter(void);
-void ex_leave(void);
-VbResult ex_keypress(int key);
-void ex_input_changed(const char *text);
+void ex_enter(Client *c);
+void ex_leave(Client *c);
+VbResult ex_keypress(Client *c, int key);
+void ex_input_changed(Client *c, const char *text);
 gboolean ex_fill_completion(GtkListStore *store, const char *input);
-VbCmdResult ex_run_string(const char *input, gboolean enable_history);
-VbCmdResult ex_run_file(const char *filename);
+VbCmdResult ex_run_string(Client *c, const char *input, gboolean enable_history);
 
 #endif /* end of include guard: _EX_H */
diff --git a/src/ext-proxy.c b/src/ext-proxy.c
new file mode 100644 (file)
index 0000000..75ccbd7
--- /dev/null
@@ -0,0 +1,214 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#include "ext-proxy.h"
+#include "main.h"
+#include "webextension/ext-main.h"
+
+static void dbus_call(Client *c, const char *method, GVariant *param,
+        GAsyncReadyCallback callback);
+static void on_editable_change_focus(GDBusConnection *connection,
+        const char *sender_name, const char *object_path,
+        const char *interface_name, const char *signal_name,
+        GVariant *parameters, gpointer data);
+static void on_name_appeared(GDBusConnection *connection, const char *name,
+        const char *owner, gpointer data);
+static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result,
+        gpointer data);
+static void on_web_extension_page_created(GDBusConnection *connection,
+        const char *sender_name, const char *object_path,
+        const char *interface_name, const char *signal_name,
+        GVariant *parameters, gpointer data);
+
+/* TODO we need potentially multiple proxies. Because a single instance of
+ * vimb may hold multiple clients which may use more than one webprocess and
+ * therefore multiple webextension instances. */
+extern struct Vimb vb;
+
+
+/**
+ * Request the web extension to focus first editable element.
+ * Returns whether an focusable element was found or not.
+ */
+void ext_proxy_focus_input(Client *c)
+{
+    dbus_call(c, "FocusInput", NULL, NULL);
+}
+
+/**
+ * Initialize the dbus proxy by watching for appearing dbus name.
+ */
+void ext_proxy_init(const char *id)
+{
+    char *service_name;
+
+    service_name = g_strdup_printf("%s-%s", VB_WEBEXTENSION_SERVICE_NAME, id);
+    g_bus_watch_name(
+            G_BUS_TYPE_SESSION,
+            service_name,
+            G_BUS_NAME_WATCHER_FLAGS_NONE,
+            (GBusNameAppearedCallback)on_name_appeared,
+            NULL,
+            NULL,
+            NULL);
+    g_free(service_name);
+}
+
+/**
+ * Send the headers string to the webextension.
+ */
+void ext_proxy_set_header(Client *c, const char *headers)
+{
+    dbus_call(c, "SetHeaderSetting", g_variant_new("(s)", headers), NULL);
+}
+
+/**
+ * Call a dbus method.
+ */
+static void dbus_call(Client *c, const char *method, GVariant *param,
+        GAsyncReadyCallback callback)
+{
+    /* TODO add function to queue calls until the proxy connection is
+     * established */
+    if (!c->dbusproxy) {
+        return;
+    }
+    g_dbus_proxy_call(c->dbusproxy, method, param, G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, c);
+}
+
+/**
+ * Callback called if a editable element changes it's focus state.
+ */
+static void on_editable_change_focus(GDBusConnection *connection,
+        const char *sender_name, const char *object_path,
+        const char *interface_name, const char *signal_name,
+        GVariant *parameters, gpointer data)
+{
+    gboolean is_focused;
+    Client *c = (Client*)data;
+    g_variant_get(parameters, "(b)", &is_focused);
+
+    /* Don't change the mode if we are in pass through mode. */
+    if (c->mode->id == 'n' && is_focused) {
+        vb_enter(c, 'i');
+    } else if (c->mode->id == 'i' && !is_focused) {
+        vb_enter(c, 'n');
+    }
+    /* TODO allo strict-focus to ignore focus event for initial set focus */
+}
+
+/**
+ * Called when the name of the webextension appeared on the dbus session bus.
+ */
+static void on_name_appeared(GDBusConnection *connection, const char *name,
+        const char *owner, gpointer data)
+{
+    int flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START
+        | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+        | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS;
+
+    /* Create the proxy to communicate over dbus. */
+    g_dbus_proxy_new(connection, flags, NULL, name,
+            VB_WEBEXTENSION_OBJECT_PATH, VB_WEBEXTENSION_INTERFACE, NULL,
+            (GAsyncReadyCallback)on_proxy_created, NULL);
+}
+
+/**
+ * Callback called when the dbus proxy is created.
+ */
+static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, gpointer data)
+{
+    GDBusConnection *connection;
+    GDBusProxy *proxy;
+    GError *error = NULL;
+
+    proxy      = g_dbus_proxy_new_finish(result, &error);
+    connection = g_dbus_proxy_get_connection(proxy);
+
+    if (!proxy) {
+        g_warning("Error creating web extension proxy: %s", error->message);
+        g_error_free(error);
+        return;
+    }
+    g_dbus_proxy_set_default_timeout(proxy, 100);
+    g_dbus_connection_signal_subscribe(
+            connection,
+            NULL,
+            VB_WEBEXTENSION_INTERFACE,
+            "PageCreated",
+            VB_WEBEXTENSION_OBJECT_PATH,
+            NULL,
+            G_DBUS_SIGNAL_FLAGS_NONE,
+            (GDBusSignalCallback)on_web_extension_page_created,
+            proxy,
+            NULL);
+}
+
+/**
+ * Listen to the VerticalScroll signal of the webextension and set the scroll
+ * percent value on the client to update the statusbar.
+ */
+static void on_vertical_scroll(GDBusConnection *connection,
+        const char *sender_name, const char *object_path,
+        const char *interface_name, const char *signal_name,
+        GVariant *parameters, gpointer data)
+{
+    Client *c = (Client*)data;
+    g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent);
+
+    vb_statusbar_update(c);
+}
+
+/**
+ * Called when the web context created the page.
+ *
+ * Find the right client to the page id returned from the webextension.
+ * Add the proxy connection to the client for later calls.
+ */
+static void on_web_extension_page_created(GDBusConnection *connection,
+        const char *sender_name, const char *object_path,
+        const char *interface_name, const char *signal_name,
+        GVariant *parameters, gpointer data)
+{
+    Client *p;
+    guint64 page_id;
+
+    g_variant_get(parameters, "(t)", &page_id);
+
+    /* Search for the client with the same page id as returned by the
+     * webextension. */
+    for (p = vb.clients; p && p->page_id != page_id; p = p->next);
+
+    if (p) {
+        /* Set the dbus proxy on the right client based on page id. */
+        p->dbusproxy = data;
+
+        g_dbus_connection_signal_subscribe(connection, NULL,
+                VB_WEBEXTENSION_INTERFACE, "VerticalScroll",
+                VB_WEBEXTENSION_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+                (GDBusSignalCallback)on_vertical_scroll, p, NULL);
+        g_dbus_connection_signal_subscribe(connection, NULL,
+                VB_WEBEXTENSION_INTERFACE, "EditableChangeFocus",
+                VB_WEBEXTENSION_OBJECT_PATH, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+                (GDBusSignalCallback)on_editable_change_focus, p, NULL);
+    }
+}
diff --git a/src/ext-proxy.h b/src/ext-proxy.h
new file mode 100644 (file)
index 0000000..b5b33a1
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef _EXT_PROXY_H
+#define _EXT_PROXY_H
+
+#include "main.h"
+
+void ext_proxy_focus_input(Client *c);
+void ext_proxy_init(const char *id);
+void ext_proxy_set_header(Client *c, const char *headers);
+
+#endif /* end of include guard: _EXT_PROXY_H */
diff --git a/src/handlers.c b/src/handlers.c
deleted file mode 100644 (file)
index 66625bf..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "main.h"
-#include "handlers.h"
-#include "util.h"
-
-static GHashTable *handlers = NULL;
-
-static char *handler_lookup(const char *uri);
-
-
-void handlers_init(void)
-{
-    handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-}
-
-void handlers_cleanup(void)
-{
-    if (handlers) {
-        g_hash_table_destroy(handlers);
-    }
-}
-
-gboolean handler_add(const char *key, const char *cmd)
-{
-    g_hash_table_insert(handlers, g_strdup(key), g_strdup(cmd));
-
-    return true;
-}
-
-gboolean handler_remove(const char *key)
-{
-    return g_hash_table_remove(handlers, key);
-}
-
-gboolean handle_uri(const char *uri)
-{
-    char *handler, *cmd;
-    GError *error = NULL;
-    gboolean result;
-
-    if (!(handler = handler_lookup(uri))) {
-        return false;
-    }
-
-    cmd = g_strdup_printf(handler, uri);
-    if (!g_spawn_command_line_async(cmd, &error)) {
-        g_warning("Can't run '%s': %s", cmd, error->message);
-        g_clear_error(&error);
-        result = false;
-    } else {
-        result = true;
-    }
-
-    g_free(cmd);
-    return result;
-}
-
-gboolean handler_fill_completion(GtkListStore *store, const char *input)
-{
-    GList *src = g_hash_table_get_keys(handlers);
-    gboolean found = util_fill_completion(store, input, src);
-    g_list_free(src);
-
-    return found;
-}
-
-static char *handler_lookup(const char *uri)
-{
-    char *p, *schema, *handler = NULL;
-
-    if ((p = strchr(uri, ':'))) {
-        schema  = g_strndup(uri, p - uri);
-        handler = g_hash_table_lookup(handlers, schema);
-        g_free(schema);
-    }
-
-    return handler;
-}
diff --git a/src/handlers.h b/src/handlers.h
deleted file mode 100644 (file)
index a38d9e1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#ifndef _HANDLERS_H
-#define _HANDLERS_H
-
-void handlers_init(void);
-void handlers_cleanup(void);
-gboolean handler_add(const char *key, const char *cmd);
-gboolean handler_remove(const char *key);
-gboolean handle_uri(const char *uri);
-gboolean handler_fill_completion(GtkListStore *store, const char *input);
-
-#endif /* end of include guard: _HANDLERS_H */
diff --git a/src/hints.c b/src/hints.c
deleted file mode 100644 (file)
index 91bb869..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#include <gdk/gdkkeysyms.h>
-#include <gdk/gdkkeysyms-compat.h>
-#include "hints.h"
-#include "main.h"
-#include "ascii.h"
-#include "dom.h"
-#include "command.h"
-#include "hints.js.h"
-#include "input.h"
-#include "map.h"
-#include "js.h"
-
-#define HINT_FILE "hints.js"
-
-static struct {
-    JSObjectRef    obj;       /* the js object */
-    char           mode;      /* mode identifying char - that last char of the hint prompt */
-    int            promptlen; /* length of the hint prompt chars 2 or 3 */
-    gboolean       gmode;     /* indicate if the hints 'g' mode is used */
-    JSContextRef   ctx;
-#if WEBKIT_CHECK_VERSION(2, 0, 0)
-    /* holds the setting if JavaScript can open windows automatically that we
-     * have to change to open windows via hinting */
-    gboolean       allow_open_win;
-#endif
-    guint          timeout_id;
-} hints;
-
-extern VbCore vb;
-
-static gboolean call_hints_function(const char *func, int count, JSValueRef params[]);
-static void fire_timeout(gboolean on);
-static gboolean fire_cb(gpointer data);
-
-
-void hints_init(WebKitWebFrame *frame)
-{
-    if (hints.obj) {
-        JSValueUnprotect(hints.ctx, hints.obj);
-        hints.obj = NULL;
-    }
-    if (!hints.obj) {
-        hints.ctx = webkit_web_frame_get_global_context(frame);
-        hints.obj = js_create_object(hints.ctx, HINTS_JS);
-    }
-}
-
-VbResult hints_keypress(int key)
-{
-    JSValueRef arguments[1];
-
-    if (key == KEY_CR) {
-        hints_fire();
-
-        return RESULT_COMPLETE;
-    } else if (key == CTRL('H')) {
-        fire_timeout(false);
-        arguments[0] = JSValueMakeNull(hints.ctx);
-        if (call_hints_function("update", 1, arguments)) {
-            return RESULT_COMPLETE;
-        }
-    } else if (key == KEY_TAB) {
-        fire_timeout(false);
-        hints_focus_next(false);
-
-        return RESULT_COMPLETE;
-    } else if (key == KEY_SHIFT_TAB) {
-        fire_timeout(false);
-        hints_focus_next(true);
-
-        return RESULT_COMPLETE;
-    } else {
-        fire_timeout(true);
-        /* try to handle the key by the javascript */
-        arguments[0] = js_string_to_ref(hints.ctx, (char[]){key, '\0'});
-        if (call_hints_function("update", 1, arguments)) {
-            return RESULT_COMPLETE;
-        }
-    }
-
-    fire_timeout(false);
-    return RESULT_ERROR;
-}
-
-void hints_clear(void)
-{
-    if (vb.mode->flags & FLAG_HINTING) {
-        vb.mode->flags &= ~FLAG_HINTING;
-        vb_set_input_text("");
-
-        call_hints_function("clear", 0, NULL);
-
-        g_signal_emit_by_name(vb.gui.webview, "hovering-over-link", NULL, NULL);
-
-#if WEBKIT_CHECK_VERSION(2, 0, 0)
-        /* if open window was not allowed for JavaScript, restore this */
-        if (!hints.allow_open_win) {
-            WebKitWebSettings *setting = webkit_web_view_get_settings(vb.gui.webview);
-            g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL);
-        }
-#endif
-    }
-}
-
-void hints_create(const char *input)
-{
-    /* don't start hinting if the hinting object isn't created - for example
-     * if hinting is started before the first data of page are received */
-    if (!hints.obj) {
-        return;
-    }
-
-    /* check if the input contains a valid hinting prompt */
-    if (!hints_parse_prompt(input, &hints.mode, &hints.gmode)) {
-        /* if input is not valid, clear possible previous hint mode */
-        if (vb.mode->flags & FLAG_HINTING) {
-            vb_enter('n');
-        }
-        return;
-    }
-
-    if (!(vb.mode->flags & FLAG_HINTING)) {
-        vb.mode->flags |= FLAG_HINTING;
-
-#if WEBKIT_CHECK_VERSION(2, 0, 0)
-        WebKitWebSettings *setting = webkit_web_view_get_settings(vb.gui.webview);
-
-        /* before we enable JavaScript to open new windows, we save the actual
-         * value to be able restore it after hints where fired */
-        g_object_get(G_OBJECT(setting), "javascript-can-open-windows-automatically", &(hints.allow_open_win), NULL);
-
-        /* if window open is already allowed there's no need to allow it again */
-        if (!hints.allow_open_win) {
-            g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", true, NULL);
-        }
-#endif
-
-        hints.promptlen = hints.gmode ? 3 : 2;
-
-        JSValueRef arguments[] = {
-            js_string_to_ref(hints.ctx, (char[]){hints.mode, '\0'}),
-            JSValueMakeBoolean(hints.ctx, hints.gmode),
-            JSValueMakeNumber(hints.ctx, MAXIMUM_HINTS),
-            js_string_to_ref(hints.ctx, GET_CHAR("hintkeys")),
-            JSValueMakeBoolean(hints.ctx, GET_BOOL("hint-follow-last")),
-            JSValueMakeBoolean(hints.ctx, GET_BOOL("hint-number-same-length"))
-        };
-        call_hints_function("init", 6, arguments);
-
-        /* if hinting is started there won't be any additional filter given and
-         * we can go out of this function */
-        return;
-    }
-
-    JSValueRef arguments[] = {js_string_to_ref(hints.ctx, *(input + hints.promptlen) ? input + hints.promptlen : "")};
-    call_hints_function("filter", 1, arguments);
-}
-
-void hints_focus_next(const gboolean back)
-{
-    JSValueRef arguments[] = {
-        JSValueMakeNumber(hints.ctx, back)
-    };
-    call_hints_function("focus", 1, arguments);
-}
-
-void hints_fire(void)
-{
-    call_hints_function("fire", 0, NULL);
-}
-
-void hints_follow_link(const gboolean back, int count)
-{
-    char *json = g_strdup_printf(
-        "[%s]",
-        back ? vb.config.prevpattern : vb.config.nextpattern
-    );
-
-    JSValueRef arguments[] = {
-        js_string_to_ref(hints.ctx, back ? "prev" : "next"),
-        js_object_to_ref(hints.ctx, json),
-        JSValueMakeNumber(hints.ctx, count)
-    };
-    g_free(json);
-
-    call_hints_function("followLink", 3, arguments);
-}
-
-void hints_increment_uri(int count)
-{
-    JSValueRef arguments[] = {
-        JSValueMakeNumber(hints.ctx, count)
-    };
-
-    call_hints_function("incrementUri", 1, arguments);
-}
-
-/**
- * Checks if the given hint prompt belong to a known and valid hints mode and
- * parses the mode and is_gmode into given pointers.
- *
- * The given prompt sting may also contain additional chars after the prompt.
- *
- * @prompt:   String to be parsed as prompt. The Prompt can be followed by
- *            additional characters.
- * @mode:     Pointer to char that will be filled with mode char if prompt was
- *            valid and given pointer is not NULL.
- * @is_gmode: Pointer to gboolean to be filled with the flag to indicate gmode
- *            hinting.
- */
-gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode)
-{
-    gboolean res;
-    char pmode = '\0';
-#ifdef FEATURE_QUEUE
-    static char *modes   = "eiIoOpPstTxyY";
-    static char *g_modes = "IpPstyY";
-#else
-    static char *modes   = "eiIoOstTxyY";
-    static char *g_modes = "IstyY";
-#endif
-
-    if (!prompt) {
-        return false;
-    }
-
-    /* get the mode identifying char from prompt */
-    if (*prompt == ';') {
-        pmode = prompt[1];
-    } else if (*prompt == 'g' && strlen(prompt) >= 3) {
-        /* get mode for g;X hint modes */
-        pmode = prompt[2];
-    }
-
-    /* no mode found in prompt */
-    if (!pmode) {
-        return false;
-    }
-
-    res = *prompt == 'g'
-        ? strchr(g_modes, pmode) != NULL
-        : strchr(modes, pmode) != NULL;
-
-    /* fill pointer only if the prompt was valid */
-    if (res) {
-        if (mode != NULL) {
-            *mode = pmode;
-        }
-        if (is_gmode != NULL) {
-            *is_gmode = *prompt == 'g';
-        }
-    }
-
-    return res;
-}
-
-static gboolean call_hints_function(const char *func, int count, JSValueRef params[])
-{
-    char *value = js_object_call_function(hints.ctx, hints.obj, func, count, params);
-
-    g_return_val_if_fail(value != NULL, false);
-
-    if (!strncmp(value, "ERROR:", 6)) {
-        g_free(value);
-        return false;
-    }
-
-    if (!strncmp(value, "OVER:", 5)) {
-        g_signal_emit_by_name(
-            vb.gui.webview, "hovering-over-link", NULL, *(value + 5) == '\0' ? NULL : (value + 5)
-        );
-        g_free(value);
-
-        return true;
-    }
-
-    /* following return values mark fired hints */
-    if (!strncmp(value, "DONE:", 5)) {
-        fire_timeout(false);
-        /* Change to normal mode only if we are currently in command mode and
-         * we are not in g-mode hinting. This is required to not switch to
-         * normal mode when the hinting triggered a click that set focus on
-         * editable element that lead vimb to switch to input mode. */
-        if (!hints.gmode && vb.mode->id == 'c') {
-            vb_enter('n');
-        }
-    } else if (!strncmp(value, "INSERT:", 7)) {
-        fire_timeout(false);
-        vb_enter('i');
-        if (hints.mode == 'e') {
-            input_open_editor();
-        }
-    } else if (!strncmp(value, "DATA:", 5)) {
-        fire_timeout(false);
-        /* switch first to normal mode - else we would clear the inputbox
-         * on switching mode also if we want to show yanked data */
-        if (!hints.gmode) {
-            vb_enter('n');
-        }
-
-        char *v = (value + 5);
-        Arg a   = {0};
-        /* put the hinted value into register "; */
-        vb_register_add(';', v);
-        switch (hints.mode) {
-            /* used if images should be opened */
-            case 'i':
-            case 'I':
-                a.s = v;
-                a.i = (hints.mode == 'I') ? VB_TARGET_NEW : VB_TARGET_CURRENT;
-                vb_load_uri(&a);
-                break;
-
-            case 'O':
-            case 'T':
-                vb_echo(VB_MSG_NORMAL, false, "%s %s", (hints.mode == 'T') ? ":tabopen" : ":open", v);
-                if (!hints.gmode) {
-                    vb_enter('c');
-                }
-                break;
-
-            case 's':
-                a.s = v;
-                a.i = COMMAND_SAVE_URI;
-                command_save(&a);
-                break;
-
-            case 'x':
-                map_handle_string(GET_CHAR("x-hint-command"), true);
-                break;
-
-            case 'y':
-            case 'Y':
-                a.i = COMMAND_YANK_ARG;
-                a.s = v;
-                command_yank(&a, vb.state.current_register);
-                break;
-
-#ifdef FEATURE_QUEUE
-            case 'p':
-            case 'P':
-                a.s = v;
-                a.i = (hints.mode == 'P') ? COMMAND_QUEUE_UNSHIFT : COMMAND_QUEUE_PUSH;
-                command_queue(&a);
-                break;
-#endif
-        }
-    }
-    g_free(value);
-    return true;
-}
-
-static void fire_timeout(gboolean on)
-{
-    int millis;
-    /* remove possible timeout function */
-    if (hints.timeout_id) {
-        g_source_remove(hints.timeout_id);
-        hints.timeout_id = 0;
-    }
-
-    if (on) {
-        millis = GET_INT("hint-timeout");
-        if (millis) {
-            hints.timeout_id = g_timeout_add(millis, (GSourceFunc)fire_cb, NULL);
-        }
-    }
-}
-
-static gboolean fire_cb(gpointer data)
-{
-    hints_fire();
-
-    /* remove timeout id for the timeout that is removed by return value of
-     * false automatic */
-    hints.timeout_id = 0;
-    return false;
-}
diff --git a/src/hints.h b/src/hints.h
deleted file mode 100644 (file)
index 4ced9d8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#ifndef _HINTS_H
-#define _HINTS_H
-
-#include "main.h"
-
-void hints_init(WebKitWebFrame *frame);
-VbResult hints_keypress(int key);
-void hints_create(const char *input);
-void hints_fire(void);
-void hints_follow_link(const gboolean back, int count);
-void hints_increment_uri(int count);
-gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode);
-void hints_clear(void);
-void hints_focus_next(const gboolean back);
-
-#endif /* end of include guard: _HINTS_H */
diff --git a/src/hints.js b/src/hints.js
deleted file mode 100644 (file)
index 458d85c..0000000
+++ /dev/null
@@ -1,611 +0,0 @@
-Object.freeze((function(){
-    'use strict';
-
-    var hints      = [],               /* holds all hint data (hinted element, label, number) in view port */
-        docs       = [],               /* hold the affected document with the start and end index of the hints */
-        validHints = [],               /* holds the valid hinted elements matching the filter condition */
-        activeHint,                    /* holds the active hint object */
-        filterText = "",               /* holds the typed filter text */
-        filterNum  = 0,                /* holds the numeric filter */
-        /* TODO remove these classes and use the 'vimbhint' attribute for */
-        /* styling the hints and labels - but this might break user */
-        /* stylesheets that use the classes for styling */
-        cId      = "_hintContainer",   /* id of the container holding the hint labels */
-        lClass   = "_hintLabel",       /* class used on the hint labels with the hint numbers */
-        hClass   = "_hintElem",        /* marks hinted elements */
-        fClass   = "_hintFocus",       /* marks focused element and focussed hint */
-        config;
-    /* the hint class used to maintain hinted element and labels */
-    function Hint() {
-        /* hide hint label and remove coloring from hinted element */
-        this.hide = function() {
-            /* remove hint labels from no more visible hints */
-            this.label.style.display = "none";
-            this.e.classList.remove(fClass);
-            this.e.classList.remove(hClass);
-        };
-
-        /* show the hint element colored with the hint label */
-        this.show = function() {
-            this.label.style.display = "";
-            this.e.classList.add(hClass);
-
-            /* create the label with the hint number */
-            var text = [];
-            if (this.e instanceof HTMLInputElement) {
-                var type = this.e.type;
-                if (type === "checkbox") {
-                    text.push(this.e.checked ? "☑" : "☐");
-                } else if (type === "radio") {
-                    text.push(this.e.checked ? "⊙" : "○");
-                }
-            }
-            if (this.showText && this.text) {
-                text.push(this.text.substr(0, 20));
-            }
-            /* use \x20 instead of ' ' to keep this space during js2h.sh processing */
-            this.label.innerText = this.num + (text.length ? ":\x20" + text.join("\x20") : "");
-        };
-    }
-
-    function clear() {
-        var i, j, doc, e;
-        for (i = 0; i < docs.length; i++) {
-            doc = docs[i];
-            /* find all hinted elements vimbhint 'hint' */
-            var res = xpath(doc.doc, "//*[contains(@vimbhint, 'hint')]");
-            for (j = 0; j < res.snapshotLength; j++) {
-                e = res.snapshotItem(j);
-                e.removeAttribute("vimbhint");
-                e.classList.remove(fClass);
-                e.classList.remove(hClass);
-            }
-            doc.div.parentNode.removeChild(doc.div);
-        }
-        docs       = [];
-        hints      = [];
-        validHints = [];
-        filterText = "";
-        filterNum  = 0;
-    }
-
-    function create() {
-        var count = 0;
-
-        function helper(win, offsets) {
-            /* document may be undefined for frames out of the same origin */
-            /* policy and will break the whole code - so we check this before */
-            if (typeof win.document == "undefined") {
-                return;
-            }
-
-            offsets        = offsets || {left: 0, right: 0, top: 0, bottom: 0};
-            offsets.right  = win.innerWidth  - offsets.right;
-            offsets.bottom = win.innerHeight - offsets.bottom;
-
-            /* checks if given elemente is in viewport and visible */
-            function isVisible(e) {
-                if (typeof e == "undefined") {
-                    return false;
-                }
-                var rect = e.getBoundingClientRect();
-                if (!rect ||
-                    rect.top > offsets.bottom || rect.bottom < offsets.top ||
-                    rect.left > offsets.right || rect.right < offsets.left
-                ) {
-                    return false;
-                }
-
-                if ((!rect.width || !rect.height) && (e.textContent || !e.name)) {
-                    var arr   = Array.prototype.slice.call(e.childNodes);
-                    var check = function(e) {
-                        return e instanceof Element
-                            && e.style.float != "none"
-                            && isVisible(e);
-                    };
-                    if (!arr.some(check)) {
-                        return false;
-                    }
-                }
-
-                var s = win.getComputedStyle(e, null);
-                return s.display !== "none" && s.visibility == "visible";
-            }
-
-            var doc       = win.document,
-                res       = xpath(doc, config.xpath),
-                /* generate basic hint element which will be cloned and updated later */
-                labelTmpl = doc.createElement("span"),
-                e, i;
-
-            labelTmpl.className = lClass;
-            labelTmpl.setAttribute("vimbhint", "label");
-
-            var containerOffsets = getOffsets(doc),
-                offsetX = containerOffsets[0],
-                offsetY = containerOffsets[1],
-                fragment = doc.createDocumentFragment(),
-                rect, label, text, showText, start = hints.length;
-
-            /* collect all visible elements in hints array */
-            for (i = 0; i < res.snapshotLength; i++) {
-                e = res.snapshotItem(i);
-                if (!isVisible(e)) {
-                    continue;
-                }
-
-                count++;
-
-                /* create the hint label with number */
-                rect  = e.getBoundingClientRect();
-                label = labelTmpl.cloneNode(false);
-                label.setAttribute(
-                    "style", [
-                        "display:none;",
-                        "left:", Math.max((rect.left + offsetX), offsetX), "px;",
-                        "top:", Math.max((rect.top + offsetY), offsetY), "px;"
-                    ].join("")
-                );
-
-                /* if hinted element is an image - show title or alt of the image in hint label */
-                /* this allows to see how to filter for the image */
-                text = "";
-                showText = false;
-                if (e instanceof HTMLImageElement) {
-                    text     = e.title || e.alt;
-                    showText = true;
-                } else if (e.firstElementChild instanceof HTMLImageElement && /^\s*$/.test(e.textContent)) {
-                    text     = e.firstElementChild.title || e.firstElementChild.alt;
-                    showText = true;
-                } else if (e instanceof HTMLInputElement) {
-                    var type = e.type;
-                    if (type === "image") {
-                        text = e.alt || "";
-                    } else if (e.value && type !== "password") {
-                        text     = e.value;
-                        showText = (type === "radio" || type === "checkbox");
-                    }
-                } else if (e instanceof HTMLSelectElement) {
-                    if (e.selectedIndex >= 0) {
-                        text = e.item(e.selectedIndex).text;
-                    }
-                } else {
-                    text = e.textContent;
-                }
-                /* add the hint class to the hinted element */
-                fragment.appendChild(label);
-                e.setAttribute("vimbhint", "hint");
-
-                hints.push({
-                    e:         e,
-                    label:     label,
-                    text:      text,
-                    showText:  showText,
-                    __proto__: new Hint
-                });
-
-                if (count >= config.maxHints) {
-                    break;
-                }
-            }
-
-            /* append the fragment to the document */
-            var hDiv = doc.createElement("div");
-            hDiv.id  = cId;
-            hDiv.setAttribute("vimbhint", "container");
-            hDiv.appendChild(fragment);
-            if (doc.body) {
-                doc.body.appendChild(hDiv);
-            }
-            /* create the default style sheet */
-            createStyle(doc);
-
-            docs.push({
-                doc:   doc,
-                start: start,
-                end:   hints.length - 1,
-                div:   hDiv
-            });
-
-            /* recurse into any iframe or frame element */
-            for (i = 0; i < win.frames.length; i++) {
-                var rect,
-                    f = win.frames[i],
-                    e = f.frameElement;
-
-                if (isVisible(e)) {
-                    rect = e.getBoundingClientRect();
-                    helper(f, {
-                        left:   Math.max(offsets.left - rect.left, 0),
-                        right:  Math.max(rect.right   - offsets.right, 0),
-                        top:    Math.max(offsets.top  - rect.top, 0),
-                        bottom: Math.max(rect.bottom  - offsets.bottom, 0)
-                    });
-                }
-            }
-        }
-
-        helper(window);
-    }
-
-    function show(fireLast) {
-        var i, hint, newIdx,
-            n       = 1,
-            matcher = getMatcher(filterText),
-            str     = getHintString(filterNum);
-
-        if (config.hintNumSameLength) {
-            /* get number of hints to be shown */
-            var hintCount = 0;
-            for (i = 0; i < hints.length; i++) {
-                if (matcher(hints[i].text)) {
-                    hintCount++;
-                }
-            }
-            /* increase starting point of hint numbers until there are */
-            /* enough available numbers */
-            var len = config.hintKeys.length;
-            while (n * (len - 1) < hintCount) {
-                n *= len;
-            }
-        }
-
-        /* clear the array of valid hints */
-        validHints = [];
-        for (i = 0; i < hints.length; i++) {
-            hint = hints[i];
-            /* hide hints not matching the filter text */
-            if (!matcher(hint.text)) {
-                hint.hide();
-            } else {
-                /* assign the new hint number/letters as label to the hint */
-                hint.num = getHintString(n++);
-                /* check for number filter */
-                if (!filterNum || 0 === hint.num.indexOf(str)) {
-                    hint.show();
-                    validHints.push(hint);
-                } else {
-                    hint.hide();
-                }
-            }
-        }
-        if (fireLast && config.followLast && validHints.length <= 1) {
-            focusHint(0);
-            return fire();
-        }
-
-        /* if the previous active hint isn't valid set focus to first */
-        if (!activeHint || validHints.indexOf(activeHint) < 0) {
-            return focusHint(0);
-        }
-    }
-
-    /* Returns a validator method to check if the hint elements text matches */
-    /* the given filter text. */
-    function getMatcher(text) {
-        var tokens = text.toLowerCase().split(/\s+/);
-        return function (itemText) {
-            itemText = itemText.toLowerCase();
-            return tokens.every(function (token) {
-                return 0 <= itemText.indexOf(token);
-            });
-        };
-    }
-
-    /* Retrun the hint string for a given number based on configured hintkeys */
-    function getHintString(n) {
-        var res = [],
-            len = config.hintKeys.length;
-        do {
-            res.push(config.hintKeys[n % len]);
-            n = Math.floor(n / len);
-        } while (n > 0);
-
-        return res.reverse().join("");
-    }
-
-    function getOffsets(doc) {
-        var body  = doc.body || doc.documentElement,
-            style = body.style,
-            rect;
-
-        if (style && /^(absolute|fixed|relative)$/.test(style.position)) {
-            rect = body.getClientRects()[0];
-            return [-rect.left, -rect.top];
-        }
-        return [doc.defaultView.scrollX, doc.defaultView.scrollY];
-    }
-
-    function createStyle(doc) {
-        if (doc.hasStyle) {
-            return;
-        }
-        var e = doc.createElement("style");
-        /* HINT_CSS is replaces by the contents of the HINT_CSS constant from config.h */
-        e.innerHTML = "HINT_CSS";
-        doc.head.appendChild(e);
-        /* prevent us from adding the style multiple times */
-        doc.hasStyle = true;
-    }
-
-    function focus(back) {
-        var idx = validHints.indexOf(activeHint);
-        /* previous active hint not found */
-        if (idx < 0) {
-            idx = 0;
-        }
-
-        if (back) {
-            if (--idx < 0) {
-                idx = validHints.length - 1;
-            }
-        } else {
-            if (++idx >= validHints.length) {
-                idx = 0;
-            }
-        }
-        return focusHint(idx);
-    }
-
-    function fire() {
-        if (!activeHint) {
-            return "ERROR:";
-        }
-
-        var e = activeHint.e,
-            res;
-
-        /* process form actions like focus toggling inputs */
-        if (config.handleForm) {
-            res = handleForm(e);
-        }
-
-        if (config.keepOpen) {
-            /* reset the filter number */
-            filterNum = 0;
-            show(false);
-        } else {
-            clear();
-        }
-
-        return res || config.action(e);
-    }
-
-    /* focus or toggle form fields */
-    function handleForm(e) {
-        var tag  = e.nodeName.toLowerCase(),
-            type = e.type || "";
-
-        if (tag === "input" || tag === "textarea" || tag === "select") {
-            if (type === "radio" || type === "checkbox") {
-                e.focus();
-                click(e);
-                return "DONE:";
-            }
-            if (type === "submit" || type === "reset" || type  === "button" || type === "image") {
-                click(e);
-                return "DONE:";
-            }
-            e.focus();
-            return "INSERT:";
-        }
-        if (tag === "iframe" || tag === "frame") {
-            e.focus();
-            return "DONE:";
-        }
-    }
-
-    /* internal used methods */
-    function open(e, newWin) {
-        var oldTarget = e.target;
-        if (newWin) {
-            /* set target to open in new window */
-            e.target = "_blank";
-        } else if (e.target === "_blank") {
-            e.removeAttribute("target");
-        }
-        /* to open links in new window the mouse events are fired with ctrl */
-        /* key - otherwise some ugly pages will ignore this attribute in their */
-        /* mouse event observers like duckduckgo */
-        click(e, newWin);
-        e.target = oldTarget;
-    }
-
-    /* set focus on hint with given index valid hints array */
-    function focusHint(newIdx) {
-        /* reset previous focused hint */
-        if (activeHint) {
-            activeHint.e.classList.remove(fClass);
-            activeHint.label.classList.remove(fClass);
-            mouseEvent(activeHint.e, "mouseout");
-        }
-        /* get the new active hint */
-        if ((activeHint = validHints[newIdx])) {
-            activeHint.e.classList.add(fClass);
-            activeHint.label.classList.add(fClass);
-            mouseEvent(activeHint.e, "mouseover");
-
-            return "OVER:" + getSrc(activeHint.e);;
-        }
-    }
-
-    function click(e, ctrl) {
-        mouseEvent(e, "mouseover", ctrl);
-        mouseEvent(e, "mousedown", ctrl);
-        mouseEvent(e, "mouseup", ctrl);
-        mouseEvent(e, "click", ctrl);
-    }
-
-    function mouseEvent(e, name, ctrl) {
-        var evObj = e.ownerDocument.createEvent("MouseEvents");
-        evObj.initMouseEvent(
-            name, true, true, e.ownerDocument.defaultView,
-            0, 0, 0, 0, 0,
-            (typeof ctrl != "undefined") ? ctrl : false, false, false, false, 0, null
-        );
-        e.dispatchEvent(evObj);
-    }
-
-    /* retrieves the url of given element */
-    function getSrc(e) {
-        return e.href || e.src || "";
-    }
-
-    function xpath(doc, expr) {
-        return doc.evaluate(
-            expr, doc, function (p) {return "http://www.w3.org/1999/xhtml";},
-            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
-        );
-    }
-
-    /* follow the count last link on pagematching the given regex list */
-    function followLink(rel, patterns, count) {
-        /* returns array of matching elements */
-        function followFrame(frame) {
-            var i, p, reg, res = [],
-                doc = frame.document,
-                elems = [],
-                all = doc.getElementsByTagName("a");
-
-            /* first match links by rel attribute */
-            for (i = all.length - 1; i >= 0; i--) {
-                /* collect visible elements */
-                var s = doc.defaultView.getComputedStyle(all[i], null);
-                if (s.display !== "none" && s.visibility === "visible") {
-                    /* if there are rel attributes elements, put them in the result */
-                    if (all[i].rel.toLowerCase() === rel) {
-                        res.push(all[i]);
-                    } else {
-                        /* save to match them later */
-                        elems.push(all[i]);
-                    }
-                }
-            }
-            /* match each pattern successively against each link in the page */
-            for (p = 0; p < patterns.length; p++) {
-                reg = patterns[p];
-                /* begin with the last link on page */
-                for (i = elems.length - 1; i >= 0; i--) {
-                    if (elems[i].innerText.match(reg)) {
-                        res.push(elems[i]);
-                    }
-                }
-            }
-            return res;
-        }
-        var i, j, elems, frames = allFrames(window);
-        for (i = 0; i < frames.length; i++) {
-            elems = followFrame(frames[i]);
-            for (j = 0; j < elems.length; j++) {
-                if (--count == 0) {
-                    open(elems[j], false);
-                    return "DONE:";
-                }
-            }
-        }
-        return "ERROR:";
-    }
-
-    function incrementUri(count) {
-        var oldnum, newnum, matches = location.href.match(/(.*?)(\d+)(\D*)$/);
-        if (matches) {
-            oldnum = matches[2];
-            newnum = String(Math.max(parseInt(oldnum) + count, 0));
-            /* keep prepending zeros */
-            if (/^0/.test(oldnum)) {
-                while (newnum.length < oldnum.length) {
-                    newnum = "0" + newnum;
-                }
-            }
-            matches[2] = newnum;
-
-            location.href = matches.slice(1).join("");
-
-            return "DONE:";
-        }
-        return "ERROR:";
-    }
-
-    function allFrames(win) {
-        var i, f, frames = [win];
-        for (i = 0; i < win.frames.length; i++) {
-            frames.push(win.frames[i].frameElement);
-        }
-        return frames;
-    }
-
-    /* the api */
-    return {
-        init: function init(mode, keepOpen, maxHints, hintKeys, followLast, hintNumSameLength) {
-            var prop,
-                /* holds the xpaths for the different modes */
-                xpathmap = {
-                    otY:     "//*[@href] | //*[@onclick or @tabindex or @class='lk' or @role='link' or @role='button'] | //input[not(@type='hidden' or @disabled or @readonly)] | //textarea[not(@disabled or @readonly)] | //button | //select",
-                    e:       "//input[not(@type) or @type='text'] | //textarea",
-                    iI:      "//img[@src]",
-                    OpPsTxy: "//*[@href] | //img[@src and not(ancestor::a)] | //iframe[@src]"
-                },
-                /* holds the actions to perform on hint fire */
-                actionmap = {
-                    o:          function(e) {open(e, false); return "DONE:";},
-                    t:          function(e) {open(e, true); return "DONE:";},
-                    eiIOpPsTxy: function(e) {return "DATA:" + getSrc(e);},
-                    Y:          function(e) {return "DATA:" + (e.textContent || "");}
-                };
-
-            config = {
-                maxHints:   maxHints,
-                keepOpen:   keepOpen,
-                /* handle forms only useful when there are form fields in xpath */
-                /* don't handle form for Y to allow to yank form filed content */
-                /* instead of switching to input mode */
-                handleForm:        ("eot".indexOf(mode) >= 0),
-                hintKeys:          hintKeys,
-                followLast:        followLast,
-                hintNumSameLength: hintNumSameLength,
-            };
-            for (prop in xpathmap) {
-                if (prop.indexOf(mode) >= 0) {
-                    config["xpath"] = xpathmap[prop];
-                    break;
-                }
-            }
-            for (prop in actionmap) {
-                if (prop.indexOf(mode) >= 0) {
-                    config["action"] = actionmap[prop];
-                    break;
-                }
-            }
-
-            create();
-            return show(true);
-        },
-        filter: function filter(text) {
-            /* remove previously set number filters to make the filter */
-            /* easier to understand for the users */
-            filterNum  = 0;
-            filterText = text || "";
-            return show(true);
-        },
-        update: function update(n) {
-            var pos,
-                keys = config.hintKeys;
-            /* delete last filter number digit */
-            if (null === n && filterNum) {
-                filterNum = Math.floor(filterNum / keys.length);
-                return show(false);
-            }
-            if ((pos = keys.indexOf(n)) >= 0) {
-                filterNum = filterNum * keys.length + pos;
-                return show(true);
-            }
-            return "ERROR:";
-        },
-        clear:        clear,
-        fire:         fire,
-        focus:        focus,
-        /* not really hintings but uses similar logic */
-        followLink:   followLink,
-        incrementUri: incrementUri,
-    };
-})());
diff --git a/src/hints.js.h b/src/hints.js.h
new file mode 100644 (file)
index 0000000..102d9c6
--- /dev/null
@@ -0,0 +1 @@
+#define HINTS_JS "Object.freeze((function(){'use strict';var hints=[],docs=[],validHints=[],activeHint,filterText=\"\",filterNum=0,cId=\"_hintContainer\",lClass=\"_hintLabel\",hClass=\"_hintElem\",fClass=\"_hintFocus\",config;function Hint(){this.hide=function(){this.label.style.display=\"none\";this.e.classList.remove(fClass);this.e.classList.remove(hClass);};this.show=function(){this.label.style.display=\"\";this.e.classList.add(hClass);var text=[];if(this.e instanceof HTMLInputElement){var type=this.e.type;if(type===\"checkbox\"){text.push(this.e.checked?\"☑\":\"☐\");}else if(type===\"radio\"){text.push(this.e.checked?\"⊙\":\"○\");}}if(this.showText&&this.text){text.push(this.text.substr(0,20));}this.label.innerText=this.num+(text.length?\": \"+text.join(\" \"):\"\");};}function clear(){var i,j,doc,e;for(i=0;i<docs.length;i++){doc=docs[i];var res=xpath(doc.doc,\"//*[contains(@vimbhint,'hint')]\");for(j=0;j<res.snapshotLength;j++){e=res.snapshotItem(j);e.removeAttribute(\"vimbhint\");e.classList.remove(fClass);e.classList.remove(hClass);}doc.div.parentNode.removeChild(doc.div);}docs=[];hints=[];validHints=[];filterText=\"\";filterNum=0;}function create(){var count=0;function helper(win,offsets){if(typeof win.document==\"undefined\"){return;}offsets=offsets||{left:0,right:0,top:0,bottom:0};offsets.right=win.innerWidth-offsets.right;offsets.bottom=win.innerHeight-offsets.bottom;function isVisible(e){if(typeof e==\"undefined\"){return false;}var rect=e.getBoundingClientRect();if(!rect||rect.top>offsets.bottom||rect.bottom<offsets.top||rect.left>offsets.right||rect.right<offsets.left){return false;}if((!rect.width||!rect.height)&&(e.textContent||!e.name)){var arr=Array.prototype.slice.call(e.childNodes);var check=function(e){return e instanceof Element&&e.style.float!=\"none\"&&isVisible(e);};if(!arr.some(check)){return false;}}var s=win.getComputedStyle(e,null);return s.display!==\"none\"&&s.visibility==\"visible\";}var doc=win.document,res=xpath(doc,config.xpath),labelTmpl=doc.createElement(\"span\"),e,i;labelTmpl.className=lClass;labelTmpl.setAttribute(\"vimbhint\",\"label\");var containerOffsets=getOffsets(doc),offsetX=containerOffsets[0],offsetY=containerOffsets[1],fragment=doc.createDocumentFragment(),rect,label,text,showText,start=hints.length;for(i=0;i<res.snapshotLength;i++){e=res.snapshotItem(i);if(!isVisible(e)){continue;}count++;rect=e.getBoundingClientRect();label=labelTmpl.cloneNode(false);label.setAttribute(\"style\",[\"display:none;\",\"left:\",Math.max((rect.left+offsetX),offsetX),\"px;\",\"top:\",Math.max((rect.top+offsetY),offsetY),\"px;\"].join(\"\"));text=\"\";showText=false;if(e instanceof HTMLImageElement){text=e.title||e.alt;showText=true;}else if(e.firstElementChild instanceof HTMLImageElement&&/^\\s*$/.test(e.textContent)){text=e.firstElementChild.title||e.firstElementChild.alt;showText=true;}else if(e instanceof HTMLInputElement){var type=e.type;if(type===\"image\"){text=e.alt||\"\";}else if(e.value&&type!==\"password\"){text=e.value;showText=(type===\"radio\"||type===\"checkbox\");}}else if(e instanceof HTMLSelectElement){if(e.selectedIndex>=0){text=e.item(e.selectedIndex).text;}}else{text=e.textContent;}fragment.appendChild(label);e.setAttribute(\"vimbhint\",\"hint\");hints.push({e:e,label:label,text:text,showText:showText,__proto__:new Hint});if(count>=config.maxHints){break;}}var hDiv=doc.createElement(\"div\");hDiv.id=cId;hDiv.setAttribute(\"vimbhint\",\"container\");hDiv.appendChild(fragment);if(doc.body){doc.body.appendChild(hDiv);}createStyle(doc);docs.push({doc:doc,start:start,end:hints.length-1,div:hDiv});for(i=0;i<win.frames.length;i++){var rect,f=win.frames[i],e=f.frameElement;if(isVisible(e)){rect=e.getBoundingClientRect();helper(f,{left:Math.max(offsets.left-rect.left,0),right:Math.max(rect.right-offsets.right,0),top:Math.max(offsets.top-rect.top,0),bottom:Math.max(rect.bottom-offsets.bottom,0)});}}}helper(window);}function show(fireLast){var i,hint,newIdx,n=1,matcher=getMatcher(filterText),str=getHintString(filterNum);if(config.hintNumSameLength){var hintCount=0;for(i=0;i<hints.length;i++){if(matcher(hints[i].text)){hintCount++;}}var len=config.hintKeys.length;while(n *(len-1)<hintCount){n *=len;}}validHints=[];for(i=0;i<hints.length;i++){hint=hints[i];if(!matcher(hint.text)){hint.hide();}else{hint.num=getHintString(n++);if(!filterNum||0===hint.num.indexOf(str)){hint.show();validHints.push(hint);}else{hint.hide();}}}if(fireLast&&config.followLast&&validHints.length<=1){focusHint(0);return fire();}if(!activeHint||validHints.indexOf(activeHint)<0){return focusHint(0);}}function getMatcher(text){var tokens=text.toLowerCase().split(/\\s+/);return function(itemText){itemText=itemText.toLowerCase();return tokens.every(function(token){return 0<=itemText.indexOf(token);});};}function getHintString(n){var res=[],len=config.hintKeys.length;do{res.push(config.hintKeys[n % len]);n=Math.floor(n / len);}while(n>0);return res.reverse().join(\"\");}function getOffsets(doc){var body=doc.body||doc.documentElement,style=body.style,rect;if(style&&/^(absolute|fixed|relative)$/.test(style.position)){rect=body.getClientRects()[0];return [-rect.left,-rect.top];}return [doc.defaultView.scrollX,doc.defaultView.scrollY];}function createStyle(doc){if(doc.hasStyle){return;}var e=doc.createElement(\"style\");e.innerHTML=\"" HINT_CSS "\";doc.head.appendChild(e);doc.hasStyle=true;}function focus(back){var idx=validHints.indexOf(activeHint);if(idx<0){idx=0;}if(back){if(--idx<0){idx=validHints.length-1;}}else{if(++idx>=validHints.length){idx=0;}}return focusHint(idx);}function fire(){if(!activeHint){return\"ERROR:\";}var e=activeHint.e,res;if(config.handleForm){res=handleForm(e);}if(config.keepOpen){filterNum=0;show(false);}else{clear();}return res||config.action(e);}function handleForm(e){var tag=e.nodeName.toLowerCase(),type=e.type||\"\";if(tag===\"input\"||tag===\"textarea\"||tag===\"select\"){if(type===\"radio\"||type===\"checkbox\"){e.focus();click(e);return\"DONE:\";}if(type===\"submit\"||type===\"reset\"||type===\"button\"||type===\"image\"){click(e);return\"DONE:\";}e.focus();return\"INSERT:\";}if(tag===\"iframe\"||tag===\"frame\"){e.focus();return\"DONE:\";}}function open(e,newWin){var oldTarget=e.target;if(newWin){e.target=\"_blank\";}else if(e.target===\"_blank\"){e.removeAttribute(\"target\");}click(e,newWin);e.target=oldTarget;}function focusHint(newIdx){if(activeHint){activeHint.e.classList.remove(fClass);activeHint.label.classList.remove(fClass);mouseEvent(activeHint.e,\"mouseout\");}if((activeHint=validHints[newIdx])){activeHint.e.classList.add(fClass);activeHint.label.classList.add(fClass);mouseEvent(activeHint.e,\"mouseover\");return\"OVER:\"+getSrc(activeHint.e);;}}function click(e,ctrl){mouseEvent(e,\"mouseover\",ctrl);mouseEvent(e,\"mousedown\",ctrl);mouseEvent(e,\"mouseup\",ctrl);mouseEvent(e,\"click\",ctrl);}function mouseEvent(e,name,ctrl){var evObj=e.ownerDocument.createEvent(\"MouseEvents\");evObj.initMouseEvent(name,true,true,e.ownerDocument.defaultView,0,0,0,0,0,(typeof ctrl!=\"undefined\")?ctrl:false,false,false,false,0,null);e.dispatchEvent(evObj);}function getSrc(e){return e.href||e.src||\"\";}function xpath(doc,expr){return doc.evaluate(expr,doc,function(p){return\"http://www.w3.org/1999/xhtml\";},XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);}function followLink(rel,patterns,count){function followFrame(frame){var i,p,reg,res=[],doc=frame.document,elems=[],all=doc.getElementsByTagName(\"a\");for(i=all.length-1;i>=0;i--){var s=doc.defaultView.getComputedStyle(all[i],null);if(s.display!==\"none\"&&s.visibility===\"visible\"){if(all[i].rel.toLowerCase()===rel){res.push(all[i]);}else{elems.push(all[i]);}}}for(p=0;p<patterns.length;p++){reg=patterns[p];for(i=elems.length-1;i>=0;i--){if(elems[i].innerText.match(reg)){res.push(elems[i]);}}}return res;}var i,j,elems,frames=allFrames(window);for(i=0;i<frames.length;i++){elems=followFrame(frames[i]);for(j=0;j<elems.length;j++){if(--count==0){open(elems[j],false);return\"DONE:\";}}}return\"ERROR:\";}function incrementUri(count){var oldnum,newnum,matches=location.href.match(/(.*?)(\\d+)(\\D*)$/);if(matches){oldnum=matches[2];newnum=String(Math.max(parseInt(oldnum)+count,0));if(/^0/.test(oldnum)){while(newnum.length<oldnum.length){newnum=\"0\"+newnum;}}matches[2]=newnum;location.href=matches.slice(1).join(\"\");return\"DONE:\";}return\"ERROR:\";}function allFrames(win){var i,f,frames=[win];for(i=0;i<win.frames.length;i++){frames.push(win.frames[i].frameElement);}return frames;}return{init:function init(mode,keepOpen,maxHints,hintKeys,followLast,hintNumSameLength){var prop,xpathmap={otY:\"//*[@href]|//*[@onclick or @tabindex or @class='lk'or @role='link'or @role='button']|//input[not(@type='hidden'or @disabled or @readonly)]|//textarea[not(@disabled or @readonly)]|//button|//select\",e:\"//input[not(@type)or @type='text']|//textarea\",iI:\"//img[@src]\",OpPsTxy:\"//*[@href]|//img[@src and not(ancestor::a)]|//iframe[@src]\"},actionmap={o:function(e){open(e,false);return\"DONE:\";},t:function(e){open(e,true);return\"DONE:\";},eiIOpPsTxy:function(e){return\"DATA:\"+getSrc(e);},Y:function(e){return\"DATA:\"+(e.textContent||\"\");}};config={maxHints:maxHints,keepOpen:keepOpen,handleForm:(\"eot\".indexOf(mode)>=0),hintKeys:hintKeys,followLast:followLast,hintNumSameLength:hintNumSameLength,};for(prop in xpathmap){if(prop.indexOf(mode)>=0){config[\"xpath\"]=xpathmap[prop];break;}}for(prop in actionmap){if(prop.indexOf(mode)>=0){config[\"action\"]=actionmap[prop];break;}}create();return show(true);},filter:function filter(text){filterNum=0;filterText=text||\"\";return show(true);},update:function update(n){var pos,keys=config.hintKeys;if(null===n&&filterNum){filterNum=Math.floor(filterNum / keys.length);return show(false);}if((pos=keys.indexOf(n))>=0){filterNum=filterNum * keys.length+pos;return show(true);}return\"ERROR:\";},clear:clear,fire:fire,focus:focus,followLink:followLink,incrementUri:incrementUri,};})());"
index ceeb5e0..fcd89fc 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
 #include <fcntl.h>
+#include <glib.h>
 #include <sys/file.h>
-#include "main.h"
+
+#include "ascii.h"
+#include "completion.h"
+#include "config.h"
 #include "history.h"
+#include "main.h"
 #include "util.h"
-#include "completion.h"
-#include "ascii.h"
-
-extern VbCore vb;
 
 #define HIST_FILE(t) (vb.files[file_map[t]])
-/* map history types to files */
-static const VbFile file_map[HISTORY_LAST] = {
-    FILES_COMMAND,
-    FILES_SEARCH,
-    FILES_HISTORY
-};
-
 typedef struct {
     char *first;
     char *second;
 } History;
 
+static gboolean history_item_contains_all_tags(History *item, char **query, guint qlen);
+static void free_history(History *item);
+static History *line_to_history(const char *uri, const char *title);
 static GList *load(const char *file);
 static void write_to_file(GList *list, const char *file);
-static gboolean history_item_contains_all_tags(History *item, char **query,
-    unsigned int qlen);
-static History *line_to_history(const char *uri, const char *title);
-static void free_history(History *item);
 
+/* map history types to files */
+static const int file_map[HISTORY_LAST] = {
+    FILES_COMMAND,
+    FILES_SEARCH,
+    FILES_HISTORY
+};
+extern struct Vimb vb;
 
 /**
- * Makes all history items unique and force them to fit the maximum history
- * size and writes all entries of the different history types to file.
+ * Write a new history entry to the end of history file.
  */
-void history_cleanup(void)
+void history_add(Client *c, HistoryType type, const char *value, const char *additional)
 {
     const char *file;
-    GList *list;
 
-    /* don't cleanup the history file if history max size is 0 */
-    if (!vb.config.history_max) {
+#if 0
+    /* Don't write a history entry if the history max size is set to 0. Else
+     * skip command history in case the command was not typed by the user. */
+    if (!vb.config.history_max || (!vb.state.typed && type == HISTORY_COMMAND)) {
         return;
     }
+#endif
 
-    for (HistoryType i = HISTORY_FIRST; i < HISTORY_LAST; i++) {
-        file = HIST_FILE(i);
-        list = load(file);
-        write_to_file(list, file);
-        g_list_free_full(list, (GDestroyNotify)free_history);
+    file = HIST_FILE(type);
+    if (additional) {
+        util_file_append(file, "%s\t%s\n", value, additional);
+    } else {
+        util_file_append(file, "%s\n", value);
     }
 }
 
 /**
- * Write a new history entry to the end of history file.
+ * Makes all history items unique and force them to fit the maximum history
+ * size and writes all entries of the different history types to file.
  */
-void history_add(HistoryType type, const char *value, const char *additional)
+void history_cleanup(void)
 {
     const char *file;
+    GList *list;
 
-    /* Don't write a history entry if the history max size is set to 0. Else
-     * skip command history in case the command was not typed by the user. */
-    if (!vb.config.history_max || (!vb.state.typed && type == HISTORY_COMMAND)) {
+    /* don't cleanup the history file if history max size is 0 */
+    if (!vb.config.history_max) {
         return;
     }
 
-    file = HIST_FILE(type);
-    if (additional) {
-        util_file_append(file, "%s\t%s\n", value, additional);
-    } else {
-        util_file_append(file, "%s\n", value);
+    for (HistoryType i = HISTORY_FIRST; i < HISTORY_LAST; i++) {
+        file = HIST_FILE(i);
+        list = load(file);
+        write_to_file(list, file);
+        g_list_free_full(list, (GDestroyNotify)free_history);
     }
 }
 
@@ -96,7 +97,7 @@ gboolean history_fill_completion(GtkListStore *store, HistoryType type, const ch
 {
     char **parts;
     unsigned int len;
-    gboolean found = false;
+    gboolean found = FALSE;
     GList *src = NULL;
     GtkTreeIter iter;
     History *item;
@@ -116,7 +117,7 @@ gboolean history_fill_completion(GtkListStore *store, HistoryType type, const ch
 #endif
                 -1
             );
-            found = true;
+            found = TRUE;
         }
     } else if (HISTORY_URL == type) {
         parts = g_strsplit(input, " ", 0);
@@ -134,7 +135,7 @@ gboolean history_fill_completion(GtkListStore *store, HistoryType type, const ch
 #endif
                     -1
                 );
-                found = true;
+                found = TRUE;
             }
         }
         g_strfreev(parts);
@@ -151,7 +152,7 @@ gboolean history_fill_completion(GtkListStore *store, HistoryType type, const ch
 #endif
                     -1
                 );
-                found = true;
+                found = TRUE;
             }
         }
     }
@@ -169,12 +170,12 @@ GList *history_get_list(VbInputType type, const char *query)
     GList *result = NULL, *src = NULL;
 
     switch (type) {
-        case VB_INPUT_COMMAND:
+        case INPUT_COMMAND:
             src = load(HIST_FILE(HISTORY_COMMAND));
             break;
 
-        case VB_INPUT_SEARCH_FORWARD:
-        case VB_INPUT_SEARCH_BACKWARD:
+        case INPUT_SEARCH_FORWARD:
+        case INPUT_SEARCH_BACKWARD:
             src = load(HIST_FILE(HISTORY_SEARCH));
             break;
 
@@ -200,7 +201,46 @@ GList *history_get_list(VbInputType type, const char *query)
 }
 
 /**
- * Loads history items form file but eleminate duplicates in FIFO order.
+ * Checks if the given array of tags are all found in history item.
+ */
+static gboolean history_item_contains_all_tags(History *item, char **query, guint qlen)
+{
+    unsigned int i;
+    if (!qlen) {
+        return TRUE;
+    }
+
+    /* iterate over all query parts */
+    for (i = 0; i < qlen; i++) {
+        if (!(util_strcasestr(item->first, query[i])
+            || (item->second && util_strcasestr(item->second, query[i])))
+        ) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static void free_history(History *item)
+{
+    g_free(item->first);
+    g_free(item->second);
+    g_slice_free(History, item);
+}
+
+static History *line_to_history(const char *uri, const char *title)
+{
+    History *item = g_slice_new0(History);
+
+    item->first  = g_strdup(uri);
+    item->second = g_strdup(title);
+
+    return item;
+}
+
+/**
+ * Loads history items form file but eliminate duplicates in FIFO order.
  *
  * Returned list must be freed with (GDestroyNotify)free_history.
  */
@@ -234,43 +274,3 @@ static void write_to_file(GList *list, const char *file)
         fclose(f);
     }
 }
-
-/**
- * Checks if the given array of tags are all found in history item.
- */
-static gboolean history_item_contains_all_tags(History *item, char **query,
-    unsigned int qlen)
-{
-    unsigned int i;
-    if (!qlen) {
-        return true;
-    }
-
-    /* iterate over all query parts */
-    for (i = 0; i < qlen; i++) {
-        if (!(util_strcasestr(item->first, query[i])
-            || (item->second && util_strcasestr(item->second, query[i])))
-        ) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-static History *line_to_history(const char *uri, const char *title)
-{
-    History *item = g_slice_new0(History);
-
-    item->first  = g_strdup(uri);
-    item->second = g_strdup(title);
-
-    return item;
-}
-
-static void free_history(History *item)
-{
-    g_free(item->first);
-    g_free(item->second);
-    g_slice_free(History, item);
-}
index a686285..8709f5d 100644 (file)
 #ifndef _HISTORY_H
 #define _HISTORY_H
 
+#include <glib.h>
+
+#include "main.h"
+
 typedef enum {
     HISTORY_FIRST   = 0,
     HISTORY_COMMAND = 0,
@@ -28,8 +32,8 @@ typedef enum {
     HISTORY_LAST
 } HistoryType;
 
+void history_add(Client *c, HistoryType type, const char *value, const char *additional);
 void history_cleanup(void);
-void history_add(HistoryType type, const char *value, const char *additional);
 gboolean history_fill_completion(GtkListStore *store, HistoryType type, const char *input);
 GList *history_get_list(VbInputType type, const char *query);
 
diff --git a/src/hsts.c b/src/hsts.c
deleted file mode 100644 (file)
index 00b25a1..0000000
+++ /dev/null
@@ -1,458 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_HSTS
-#include "hsts.h"
-#include "util.h"
-#include "main.h"
-#include <fcntl.h>
-#include <sys/file.h>
-#include <string.h>
-#include <glib-object.h>
-#include <libsoup/soup.h>
-
-#define HSTS_HEADER_NAME "Strict-Transport-Security"
-#define HSTS_FILE_FORMAT "%s\t%s\t%c\n"
-#define HSTS_PROVIDER_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE((o), HSTS_TYPE_PROVIDER, HSTSProviderPrivate))
-
-extern VbCore vb;
-
-/* private interface of the provider */
-typedef struct _HSTSProviderPrivate {
-    GHashTable* whitelist;
-} HSTSProviderPrivate;
-
-typedef struct {
-    SoupDate *expires_at;
-    gboolean include_sub_domains;
-} HSTSEntry;
-
-static void hsts_provider_class_init(HSTSProviderClass *klass);
-static void hsts_provider_init(HSTSProvider *self);
-static void hsts_provider_finalize(GObject* obj);
-static inline gboolean should_secure_host(HSTSProvider *provider,
-    const char *host);
-static void process_hsts_header(SoupMessage *msg, gpointer data);
-static void parse_hsts_header(HSTSProvider *provider,
-    const char *host, const char *header);
-static void free_entry(HSTSEntry *entry);
-static void add_host_entry(HSTSProvider *provider, const char *host,
-    HSTSEntry *entry);
-static void add_host_entry_to_file(HSTSProvider *provider, const char *host,
-    HSTSEntry *entry);
-static void remove_host_entry(HSTSProvider *provider, const char *host);
-/* session feature related functions */
-static void session_feature_init(
-    SoupSessionFeatureInterface *inteface, gpointer data);
-static void request_queued(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg);
-static void request_started(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg, SoupSocket *socket);
-static void request_unqueued(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg);
-/* caching related functions */
-static void load_entries(HSTSProvider *provider, const char *file);
-static void save_entries(HSTSProvider *provider, const char *file);
-
-/**
- * Change scheme and port of soup messages uri if the host is a known and
- * valid hsts host.
- *
- * This logic should be implemented in request_queued function but the changes
- * that are done there to the uri do not appear in webkit_web_view_get_uri().
- * If a valid hsts host is requested via http and the url is changed to https
- * vimb would still show the http uri in url bar. This seems to be a
- * missbehaviour in webkit, but for now we provide this function to put in the
- * logic in the scope of the navigation-policy-decision-requested event of the
- * webview.
- *
- * Returns newly allocated string with new URI if the URI was change to
- * fullfill HSTS, else NULL.
- */
-char *hsts_get_changed_uri(SoupSession* session, SoupMessage *msg)
-{
-    SoupSessionFeature *feature;
-    HSTSProvider *provider;
-    SoupURI *uri;
-
-    if (!msg) {
-        return NULL;
-    }
-
-    feature = soup_session_get_feature_for_message(session, HSTS_TYPE_PROVIDER, msg);
-    uri     = soup_message_get_uri(msg);
-    if (!feature || !uri) {
-        return NULL;
-    }
-
-    provider = HSTS_PROVIDER(feature);
-    /* if URI uses still https we don't nee to rewrite it */
-    if (uri->scheme != SOUP_URI_SCHEME_HTTPS
-        && should_secure_host(provider, uri->host)
-    ) {
-        /* the ports is set by soup uri if scheme is changed */
-        soup_uri_set_scheme(uri, SOUP_URI_SCHEME_HTTPS);
-        return soup_uri_to_string(uri, false);
-    }
-    return NULL;
-}
-
-/**
- * Generates a new hsts provider instance.
- * Unref the instance with g_object_unref if no more used.
- */
-HSTSProvider *hsts_provider_new(void)
-{
-    return g_object_new(HSTS_TYPE_PROVIDER, NULL);
-}
-
-G_DEFINE_TYPE_WITH_CODE(
-    HSTSProvider, hsts_provider, G_TYPE_OBJECT,
-    G_IMPLEMENT_INTERFACE(SOUP_TYPE_SESSION_FEATURE, session_feature_init)
-)
-
-static void hsts_provider_class_init(HSTSProviderClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    hsts_provider_parent_class = g_type_class_peek_parent(klass);
-    g_type_class_add_private(klass, sizeof(HSTSProviderPrivate));
-    object_class->finalize = hsts_provider_finalize;
-}
-
-static void hsts_provider_init(HSTSProvider *self)
-{
-    /* initialize private fields */
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(self);
-    priv->whitelist = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)free_entry);
-
-    /* load entries from hsts file */
-    load_entries(self, vb.files[FILES_HSTS]);
-}
-
-static void hsts_provider_finalize(GObject* obj)
-{
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE (obj);
-
-    /* save all the entries in hsts file */
-    save_entries(HSTS_PROVIDER(obj), vb.files[FILES_HSTS]);
-
-    g_hash_table_destroy(priv->whitelist);
-    G_OBJECT_CLASS(hsts_provider_parent_class)->finalize(obj);
-}
-
-/**
- * Checks if given host is a known https host according to RFC 6797 8.2f
- */
-static inline gboolean should_secure_host(HSTSProvider *provider,
-    const char *host)
-{
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
-    HSTSEntry *entry;
-    char *canonical, *p;
-    gboolean result = false, is_subdomain = false;
-
-    /* ip is not allowed for hsts */
-    if (g_hostname_is_ip_address(host)) {
-        return false;
-    }
-
-    canonical = g_hostname_to_ascii(host);
-    /* don't match empty host */
-    if (*canonical) {
-        p = canonical;
-        /* Try to find the whole congruent matching host in hash table - if
-         * not found strip of the first label and try to find a superdomain
-         * match. Specified is a from right to left comparison 8.3, but in the
-         * end this should be lead to the same result. */
-        while (p != NULL) {
-            entry = g_hash_table_lookup(priv->whitelist, p);
-            if (entry != NULL) {
-                /* remove expired entries RFC 6797 8.1.1 */
-                if (soup_date_is_past(entry->expires_at)) {
-                    remove_host_entry(provider, p);
-                } else if(!is_subdomain || entry->include_sub_domains) {
-                    result = true;
-                    break;
-                }
-            }
-
-            is_subdomain = true;
-            /* test without the first domain part */
-            if ((p = strchr(p, '.'))) {
-                p++;
-            }
-        }
-    }
-    g_free(canonical);
-
-    return result;
-}
-
-static void process_hsts_header(SoupMessage *msg, gpointer data)
-{
-    HSTSProvider *provider = (HSTSProvider*)data;
-    SoupURI *uri           = soup_message_get_uri(msg);
-    SoupMessageHeaders *hdrs;
-    const char *header;
-
-    if (!g_hostname_is_ip_address(uri->host)
-        && (soup_message_get_flags(msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)
-    ){
-        g_object_get(G_OBJECT(msg), SOUP_MESSAGE_RESPONSE_HEADERS, &hdrs, NULL);
-
-        /* TODO according to RFC 6797 8.1 we must only use the first header */
-        header = soup_message_headers_get_one(hdrs, HSTS_HEADER_NAME);
-        if (header) {
-            parse_hsts_header(provider, uri->host, header);
-        }
-    }
-}
-
-/**
- * Parses the hsts directives from given header like specified in RFC 6797 6.1
- */
-static void parse_hsts_header(HSTSProvider *provider,
-    const char *host, const char *header)
-{
-    GHashTable *directives = soup_header_parse_semi_param_list(header);
-    HSTSEntry *entry;
-    int max_age = G_MAXINT;
-    gboolean include_sub_domains = false;
-    GHashTableIter iter;
-    gpointer key, value;
-    gboolean success = true;
-
-    HSTSProviderClass *klass = g_type_class_ref(HSTS_TYPE_PROVIDER);
-
-    g_hash_table_iter_init(&iter, directives);
-    while (g_hash_table_iter_next(&iter, &key, &value)) {
-        /* parse the max-age directive */
-        if (!g_ascii_strncasecmp(key, "max-age", 7)) {
-            /* max age needs a value */
-            if (value) {
-                max_age = g_ascii_strtoll(value, NULL, 10);
-                if (max_age < 0) {
-                    success = false;
-                    break;
-                }
-            } else {
-                success = false;
-                break;
-            }
-        } else if (g_ascii_strncasecmp(key, "includeSubDomains", 17)) {
-            /* includeSubDomains must not have a value */
-            if (!value) {
-                include_sub_domains = true;
-            } else {
-                success = false;
-                break;
-            }
-        }
-    }
-    soup_header_free_param_list(directives);
-    g_type_class_unref(klass);
-
-    if (success) {
-        /* remove host if max-age = 0 RFC 6797 6.1.1 */
-        if (max_age == 0) {
-            remove_host_entry(provider, host);
-        } else {
-            entry = g_slice_new(HSTSEntry);
-            entry->expires_at          = soup_date_new_from_now(max_age);
-            entry->include_sub_domains = include_sub_domains;
-
-            add_host_entry(provider, host, entry);
-            add_host_entry_to_file(provider, host, entry);
-        }
-    }
-}
-
-static void free_entry(HSTSEntry *entry)
-{
-    soup_date_free(entry->expires_at);
-    g_slice_free(HSTSEntry, entry);
-}
-
-/**
- * Adds the host to the known host, if it already exists it replaces it with
- * the information contained in entry according to RFC 6797 8.1.
- */
-static void add_host_entry(HSTSProvider *provider, const char *host,
-    HSTSEntry *entry)
-{
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
-    g_hash_table_replace(priv->whitelist, g_hostname_to_unicode(host), entry);
-}
-
-static void add_host_entry_to_file(HSTSProvider *provider, const char *host,
-    HSTSEntry *entry)
-{
-    char *date = soup_date_to_string(entry->expires_at, SOUP_DATE_ISO8601_FULL);
-
-    util_file_append(
-        vb.files[FILES_HSTS], HSTS_FILE_FORMAT, host, date, entry->include_sub_domains ? 'y' : 'n'
-    );
-    g_free(date);
-}
-
-/**
- * Removes stored entry for given host.
- */
-static void remove_host_entry(HSTSProvider *provider, const char *host)
-{
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
-    char *canonical           = g_hostname_to_unicode(host);
-
-    g_hash_table_remove(priv->whitelist, canonical);
-    g_free(canonical);
-}
-
-/**
- * Initialise the SoupSessionFeature interface.
- */
-static void session_feature_init(
-    SoupSessionFeatureInterface *inteface, gpointer data)
-{
-    inteface->request_queued   = request_queued;
-    inteface->request_started  = request_started;
-    inteface->request_unqueued = request_unqueued;
-}
-
-/**
- * Check if the host is known and switch the URI scheme to https.
- */
-static void request_queued(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg)
-{
-    SoupURI *uri = soup_message_get_uri(msg);
-    HSTSProvider *provider = HSTS_PROVIDER(feature);
-
-    /* only look for HSTS headers sent over https RFC 6797 7.2*/
-    if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
-        soup_message_add_header_handler(
-            msg, "got-headers", HSTS_HEADER_NAME, G_CALLBACK(process_hsts_header), feature
-        );
-    } else if (should_secure_host(provider, uri->host)) {
-        /* the ports is set by soup uri if scheme is changed */
-        soup_uri_set_scheme(uri, SOUP_URI_SCHEME_HTTPS);
-        soup_session_requeue_message(session, msg);
-    }
-}
-
-static void request_started(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg, SoupSocket *socket)
-{
-    HSTSProvider *provider = HSTS_PROVIDER(feature);
-    SoupURI *uri           = soup_message_get_uri(msg);
-    GTlsCertificate *certificate;
-    GTlsCertificateFlags errors;
-
-    if (should_secure_host(provider, uri->host)) {
-        if (uri->scheme != SOUP_URI_SCHEME_HTTPS
-            || (soup_message_get_https_status(msg, &certificate, &errors) && errors)
-        ) {
-            soup_session_cancel_message(session, msg, SOUP_STATUS_SSL_FAILED);
-            g_warning("cancel invalid hsts request to %s://%s", uri->scheme, uri->host);
-        }
-    }
-}
-
-static void request_unqueued(SoupSessionFeature *feature,
-    SoupSession *session, SoupMessage *msg)
-{
-    g_signal_handlers_disconnect_by_func(msg, process_hsts_header, feature);
-}
-
-/**
- * Reads the entries save in given file and store them in the whitelist.
- */
-static void load_entries(HSTSProvider *provider, const char *file)
-{
-    char **lines, **parts, *host, *line;
-    int i, len, partlen;
-    gboolean include_sub_domains;
-    SoupDate *date;
-    HSTSEntry *entry;
-
-    lines = util_get_lines(file);
-    if (!(len = g_strv_length(lines))) {
-        return;
-    }
-
-    for (i = len - 1; i >= 0; i--) {
-        line = lines[i];
-        /* skip empty or commented lines */
-        if (!*line || *line == '#') {
-            continue;
-        }
-
-        parts   = g_strsplit(line, "\t", 3);
-        partlen = g_strv_length(parts);
-        if (partlen == 3) {
-            host = parts[0];
-            if (g_hostname_is_ip_address(host)) {
-                continue;
-            }
-            date = soup_date_new_from_string(parts[1]);
-            if (!date) {
-                continue;
-            }
-            include_sub_domains = (*parts[2] == 'y') ? true : false;
-
-            /* built the new entry to add */
-            entry = g_slice_new(HSTSEntry);
-            entry->expires_at          = soup_date_new_from_string(parts[1]);
-            entry->include_sub_domains = include_sub_domains;
-
-            add_host_entry(provider, host, entry);
-        } else {
-            g_warning("could not parse hsts line '%s'", line);
-        }
-        g_strfreev(parts);
-    }
-    g_strfreev(lines);
-}
-
-/**
- * Saves all entries of given provider in given file.
- */
-static void save_entries(HSTSProvider *provider, const char *file)
-{
-    GHashTableIter iter;
-    char *host, *date;
-    HSTSEntry *entry;
-    FILE *f;
-    HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
-
-    if ((f = fopen(file, "w"))) {
-        flock(fileno(f), LOCK_EX);
-
-        g_hash_table_iter_init(&iter, priv->whitelist);
-        while (g_hash_table_iter_next (&iter, (gpointer)&host, (gpointer)&entry)) {
-            date = soup_date_to_string(entry->expires_at, SOUP_DATE_ISO8601_FULL);
-            fprintf(f, HSTS_FILE_FORMAT, host, date, entry->include_sub_domains ? 'y' : 'n');
-            g_free(date);
-        }
-
-        flock(fileno(f), LOCK_UN);
-        fclose(f);
-    }
-}
-#endif
diff --git a/src/hsts.h b/src/hsts.h
deleted file mode 100644 (file)
index c56b2c7..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_HSTS
-
-#ifndef _HSTS_H
-#define _HSTS_H
-
-#include <glib-object.h>
-#include <libsoup/soup.h>
-
-#define HSTS_TYPE_PROVIDER            (hsts_provider_get_type())
-#define HSTS_PROVIDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), HSTS_TYPE_PROVIDER, HSTSProvider))
-#define HSTS_PROVIDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), HSTS_TYPE_PROVIDER, HSTSProviderClass))
-#define HSTS_IS_PROVIDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), HSTS_TYPE_PROVIDER))
-#define HSTS_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), HSTS_TYPE_PROVIDER))
-#define HSTS_PROVIDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), HSTS_TYPE_PROVIDER, HSTSProviderClass))
-
-/* public interface of the provider */
-typedef struct {
-    GObject parent_instance;
-} HSTSProvider;
-
-/* class members of the provider */
-typedef struct {
-    GObjectClass parent_class;
-} HSTSProviderClass;
-
-
-char *hsts_get_changed_uri(SoupSession* session, SoupMessage *msg);
-GType hsts_provider_get_type(void);
-HSTSProvider *hsts_provider_new(void);
-
-#endif /* end of include guard: _HSTS_H */
-#endif
index 5292b4e..b5381ff 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
+#include <glib.h>
 #include <glib/gstdio.h>
-#include "main.h"
-#include "input.h"
-#include "dom.h"
-#include "util.h"
+
 #include "ascii.h"
+#include "config.h"
+#include "input.h"
+#include "main.h"
 #include "normal.h"
+#include "util.h"
+#include "ext-proxy.h"
 
-typedef struct {
-    char    *file;
-    Element *element;
-} EditorData;
-
-static void resume_editor(GPid pid, int status, EditorData *data);
-
-extern VbCore vb;
 
 /**
  * Function called when vimb enters the input mode.
  */
-void input_enter(void)
+void input_enter(Client *c)
 {
     /* switch focus first to make sure we can write to the inputbox without
      * disturbing the user */
-    gtk_widget_grab_focus(GTK_WIDGET(vb.gui.webview));
-    vb_update_mode_label("-- INPUT --");
+    gtk_widget_grab_focus(GTK_WIDGET(c->webview));
+    vb_modelabel_update(c, "-- INPUT --");
 }
 
 /**
  * Called when the input mode is left.
  */
-void input_leave(void)
+void input_leave(Client *c)
 {
-    vb_update_mode_label("");
+    webkit_web_view_run_javascript(c->webview, "document.activeElement.blur();", NULL, NULL, NULL);
+    vb_modelabel_update(c, "");
 }
 
 /**
  * Handles the keypress events from webview and inputbox.
  */
-VbResult input_keypress(int key)
+VbResult input_keypress(Client *c, int key)
 {
-    static gboolean ctrlo = false;
+    static gboolean ctrlo = FALSE;
 
     if (ctrlo) {
         /* if we are in ctrl-O mode perform the next keys as normal mode
          * commands until the command is complete or error */
-        VbResult res = normal_keypress(key);
+        VbResult res = normal_keypress(c, key);
         if (res != RESULT_MORE) {
-            ctrlo = false;
+            ctrlo = FALSE;
             /* Don't overwrite the mode label in case we landed in another
              * mode. This might occurre by CTRL-0 CTRL-Z or after running ex
              * command, where we mainly end up in normal mode. */
-            if (vb.mode->id == 'i') {
+            if (c->mode->id == 'i') {
                 /* reenter the input mode */
-                input_enter();
+                input_enter(c);
             }
         }
         return res;
@@ -80,106 +75,31 @@ VbResult input_keypress(int key)
 
     switch (key) {
         case CTRL('['): /* esc */
-            vb_enter('n');
+            vb_enter(c, 'n');
             return RESULT_COMPLETE;
 
         case CTRL('O'):
             /* enter CTRL-0 mode to execute next command in normal mode */
-            ctrlo           = true;
-            vb.mode->flags |= FLAG_NOMAP;
-            vb_update_mode_label("-- (input) --");
+            ctrlo           = TRUE;
+            c->mode->flags |= FLAG_NOMAP;
+            vb_modelabel_update(c, "-- (input) --");
             return RESULT_MORE;
 
         case CTRL('T'):
-            return input_open_editor();
+            return input_open_editor(c);
 
         case CTRL('Z'):
-            vb_enter('p');
+            vb_enter(c, 'p');
             return RESULT_COMPLETE;
     }
 
-    vb.state.processed_key = false;
+    c->state.processed_key = FALSE;
     return RESULT_ERROR;
 }
 
-VbResult input_open_editor(void)
+VbResult input_open_editor(Client *c)
 {
-    char **argv, *file_path = NULL;
-    const char *text, *editor_command;
-    int argc;
-    GPid pid;
-    gboolean success;
-
-    editor_command = GET_CHAR("editor-command");
-    if (!editor_command || !*editor_command) {
-        vb_echo(VB_MSG_ERROR, true, "No editor-command configured");
-        return RESULT_ERROR;
-    }
-    Element* active = dom_get_active_element(vb.gui.webview);
-
-    /* check if element is suitable for editing */
-    if (!active || !dom_is_editable(active)) {
-        return RESULT_ERROR;
-    }
-
-    text = dom_editable_element_get_value(active);
-    if (!text) {
-        return RESULT_ERROR;
-    }
-
-    if (!util_create_tmp_file(text, &file_path)) {
-        return RESULT_ERROR;
-    }
-
-    /* spawn editor */
-    char* command = g_strdup_printf(editor_command, file_path);
-    if (!g_shell_parse_argv(command, &argc, &argv, NULL)) {
-        g_critical("Could not parse editor-command '%s'", command);
-        g_free(command);
-        return RESULT_ERROR;
-    }
-    g_free(command);
-
-    success = g_spawn_async(
-        NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-        NULL, NULL, &pid, NULL
-    );
-    g_strfreev(argv);
-
-    if (!success) {
-        unlink(file_path);
-        g_free(file_path);
-        g_warning("Could not spawn editor-command");
-        return RESULT_ERROR;
-    }
-
-    /* disable the active element */
-    dom_editable_element_set_disable(active, true);
-
-    EditorData *data = g_slice_new0(EditorData);
-    data->file    = file_path;
-    data->element = active;
-
-    g_child_watch_add(pid, (GChildWatchFunc)resume_editor, data);
-
+    /* TODO should the editor be opened by the webextension or by the
+     * application? */
     return RESULT_COMPLETE;
 }
-
-static void resume_editor(GPid pid, int status, EditorData *data)
-{
-    char *text;
-    if (status == 0) {
-        text = util_get_file_contents(data->file, NULL);
-        if (text) {
-            dom_editable_element_set_value(data->element, text);
-        }
-        g_free(text);
-    }
-    dom_editable_element_set_disable(data->element, false);
-    dom_give_focus(data->element);
-
-    g_unlink(data->file);
-    g_free(data->file);
-    g_slice_free(EditorData, data);
-    g_spawn_close_pid(pid);
-}
index 8887abc..64d1d5c 100644 (file)
@@ -23,9 +23,9 @@
 #include "config.h"
 #include "main.h"
 
-void input_enter(void);
-void input_leave(void);
-VbResult input_keypress(int key);
-VbResult input_open_editor(void);
+void input_enter(Client *c);
+void input_leave(Client *c);
+VbResult input_keypress(Client *c, int key);
+VbResult input_open_editor(Client *c);
 
 #endif /* end of include guard: _INPUT_H */
diff --git a/src/io.c b/src/io.c
deleted file mode 100644 (file)
index 0abac30..0000000
--- a/src/io.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_SOCKET
-#include "io.h"
-#include "main.h"
-#include "map.h"
-#include "util.h"
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <errno.h>
-
-extern VbCore vb;
-
-static gboolean socket_accept(GIOChannel *chan);
-static gboolean socket_watch(GIOChannel *chan);
-
-
-gboolean io_init_socket(const char *name)
-{
-    char *dir, *path;
-    int sock;
-    struct sockaddr_un local;
-
-    /* create socket in runtime directory */
-    dir = g_build_filename(util_get_runtime_dir(vb.config.profile), PROJECT, "socket", NULL);
-    util_create_dir_if_not_exists(dir);
-    path = g_build_filename(dir, name, NULL);
-    g_free(dir);
-
-    /* if the file already exists - remove this first */
-    if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) {
-        unlink(path);
-    }
-
-    sock = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (sock < 0) {
-        g_warning("Can't create socket %s", path);
-    }
-
-    /* prepare socket address */
-    local.sun_family = AF_UNIX;
-    strcpy(local.sun_path, path);
-
-    if (bind(sock, (struct sockaddr*)&local, sizeof(local)) != -1
-        && listen(sock, 5) >= 0
-    ) {
-        GIOChannel *chan = g_io_channel_unix_new(sock);
-        if (chan) {
-            g_io_channel_set_encoding(chan, NULL, NULL);
-            g_io_channel_set_buffered(chan, false);
-            g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc)socket_accept, chan);
-            /* don't free path - because we want to keep the value in
-             * vb.state.socket_path still accessible */
-            vb.state.socket_path = path;
-            g_setenv("VIMB_SOCKET", path, true);
-
-            return true;
-        }
-    } else {
-        g_warning("no bind");
-    }
-
-    g_warning("Could not listen on %s: %s", path, strerror(errno));
-    g_free(path);
-
-    return false;
-}
-
-void io_cleanup(void)
-{
-    if (vb.state.socket_path) {
-        if (unlink(vb.state.socket_path) == -1) {
-            g_warning("Can't remove socket %s", vb.state.socket_path);
-        }
-        g_free(vb.state.socket_path);
-        vb.state.socket_path = NULL;
-    }
-}
-
-static gboolean socket_accept(GIOChannel *chan)
-{
-    struct sockaddr_un remote;
-    guint size = sizeof(remote);
-    GIOChannel *iochan;
-    int clientsock;
-
-    clientsock = accept(g_io_channel_unix_get_fd(chan), (struct sockaddr *)&remote, &size);
-
-    if ((iochan = g_io_channel_unix_new(clientsock))) {
-        g_io_channel_set_encoding(iochan, NULL, NULL);
-        g_io_add_watch(iochan, G_IO_IN|G_IO_HUP, (GIOFunc)socket_watch, iochan);
-    }
-    return true;
-}
-
-static gboolean socket_watch(GIOChannel *chan)
-{
-    GIOStatus ret;
-    GError *error = NULL;
-    char *line, *inputtext;
-    gsize len;
-
-    ret = g_io_channel_read_line(chan, &line, &len, NULL, &error);
-    if (ret == G_IO_STATUS_ERROR || ret == G_IO_STATUS_EOF) {
-        if (ret == G_IO_STATUS_ERROR) {
-            g_warning("Error reading: %s", error->message);
-            g_error_free(error);
-        }
-
-        /* shutdown and remove the client channel */
-        ret = g_io_channel_shutdown(chan, true, &error);
-        g_io_channel_unref(chan);
-
-        if (ret == G_IO_STATUS_ERROR) {
-            g_warning("Error closing: %s", error->message);
-            g_error_free(error);
-        }
-        return false;
-    }
-
-    /* simulate the typed flag to allow to record the commands in history */
-    vb.state.typed = true;
-
-    /* run the commands */
-    map_handle_string(line, true);
-    g_free(line);
-
-    /* unset typed flag */
-    vb.state.typed = false;
-
-    /* We assume that the commands result is still available in the inputbox,
-     * so the whole inputbox content is written to the socket. */
-    inputtext = vb_get_input_text();
-    ret       = g_io_channel_write_chars(chan, inputtext, -1, &len, &error);
-    if (ret == G_IO_STATUS_ERROR) {
-        g_warning("Error writing: %s", error->message);
-        g_error_free(error);
-    }
-    if (g_io_channel_flush(chan, &error) == G_IO_STATUS_ERROR) {
-        g_warning("Error flushing: %s", error->message);
-        g_error_free(error);
-    }
-
-    g_free(inputtext);
-
-    return true;
-}
-
-#endif
diff --git a/src/io.h b/src/io.h
deleted file mode 100644 (file)
index 7fa894b..0000000
--- a/src/io.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include "config.h"
-#ifdef FEATURE_SOCKET
-
-#ifndef _IO_H
-#define _IO_H
-
-#include <glib.h>
-
-gboolean io_init_socket(const char *name);
-void io_cleanup(void);
-
-#endif /* end of include guard: _IO_H */
-#endif
index fb40236..e6f467e 100644 (file)
--- a/src/js.c
+++ b/src/js.c
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
-#include "js.h"
-
-
-static gboolean evaluate_string(JSContextRef ctx, const char *script,
-    const char *file, JSValueRef *result);
-
-/**
- * Run scripts out of given file in the given frame.
- */
-gboolean js_eval_file(JSContextRef ctx, const char *file)
-{
-    char *js = NULL, *value = NULL;
-
-    if (g_file_test(file, G_FILE_TEST_IS_REGULAR)
-        && g_file_get_contents(file, &js, NULL, NULL)
-    ) {
-        gboolean success = js_eval(ctx, js, file, &value);
-        if (!success) {
-            g_warning("JavaScript error in %s: %s", file, value);
-        }
-        g_free(value);
-        g_free(js);
-
-        return success;
-    }
-
-    return false;
-}
-
-/**
- * Evaluates given string as script and return if this call succeed or not.
- * On success the given **value pointer is filled with the returned string,
- * else with the exception message. In both cases this must be freed by the
- * caller if no longer used.
- */
-gboolean js_eval(JSContextRef ctx, const char *script, const char *file,
-    char **value)
-{
-    gboolean   success;
-    JSValueRef result = NULL;
-
-    success = evaluate_string(ctx, script, file, &result);
-    *value  = js_ref_to_string(ctx, result);
-
-    return success;
-}
-
-/**
- * Creates a JavaScript object in context of given frame.
- */
-JSObjectRef js_create_object(JSContextRef ctx, const char *script)
-{
-    JSValueRef result = NULL, exc = NULL;
-    JSObjectRef object;
-    if (!evaluate_string(ctx, script, NULL, &result)) {
-        return NULL;
-    }
-
-    object = JSValueToObject(ctx, result, &exc);
-    if (exc) {
-        return NULL;
-    }
-    JSValueProtect(ctx, result);
-
-    return object;
-}
-
-/**
- * Calls a function on object and returns the result as newly allocates
- * string.
- * Returned string must be freed after use.
- */
-char* js_object_call_function(JSContextRef ctx, JSObjectRef obj,
-    const char *func, int count, JSValueRef params[])
-{
-    JSValueRef  js_ret, function;
-    JSObjectRef function_object;
-    JSStringRef js_func = NULL;
-    char *value;
-
-    g_return_val_if_fail(obj != NULL, NULL);
-
-    js_func = JSStringCreateWithUTF8CString(func);
-    if (!JSObjectHasProperty(ctx, obj, js_func)) {
-        JSStringRelease(js_func);
-
-        return NULL;
-    }
-
-    function        = JSObjectGetProperty(ctx, obj, js_func, NULL);
-    function_object = JSValueToObject(ctx, function, NULL);
-    js_ret          = JSObjectCallAsFunction(ctx, function_object, NULL, count, params, NULL);
-    JSStringRelease(js_func);
-
-    value = js_ref_to_string(ctx, js_ret);
+#include <JavaScriptCore/JavaScript.h>
+#include <glib.h>
+#include <webkit2/webkit2.h>
 
-    return value;
-}
+#include "js.h"
+#include "config.h"
 
 /**
- * Returns a new allocates string for given value reference.
+ * Returns a new allocates string for given value javascript result.
  * String must be freed if not used anymore.
  */
-char* js_ref_to_string(JSContextRef ctx, JSValueRef ref)
+char *js_result_as_string(WebKitJavascriptResult *res)
 {
-    char *string;
-    size_t len;
-    JSStringRef str_ref;
-
-    g_return_val_if_fail(ref != NULL, NULL);
+    JSGlobalContextRef cr;
+    JSStringRef jsstring;
+    JSValueRef jsvalue;
+    gsize max;
 
-    str_ref = JSValueToStringCopy(ctx, ref, NULL);
-    len     = JSStringGetMaximumUTF8CStringSize(str_ref);
+    g_return_val_if_fail(res != NULL, NULL);
 
-    string = g_new0(char, len);
-    JSStringGetUTF8CString(str_ref, string, len);
-    JSStringRelease(str_ref);
+    jsvalue  = webkit_javascript_result_get_value(res);
+    cr       = webkit_javascript_result_get_global_context(res);
+    jsstring = JSValueToStringCopy(cr, jsvalue, NULL);
+    max      = JSStringGetMaximumUTF8CStringSize(jsstring);
+    if (max > 0) {
+        char *string = g_new(char, max);
+        JSStringGetUTF8CString(jsstring, string, max);
 
-    return string;
-}
-
-/**
- * Retrieves a values reference for given string.
- */
-JSValueRef js_string_to_ref(JSContextRef ctx, const char *string)
-{
-    JSStringRef js = JSStringCreateWithUTF8CString(string);
-    JSValueRef ref = JSValueMakeString(ctx, js);
-    JSStringRelease(js);
-    return ref;
-}
-
-/**
- * Retrieves a values reference for given json or array string string.
- */
-JSValueRef js_object_to_ref(JSContextRef ctx, const char *json)
-{
-    JSValueRef ref = NULL;
-    if (evaluate_string(ctx, json, NULL, &ref)) {
-        return ref;
+        return string;
     }
-    g_warning("Could not parse %s", json);
     return NULL;
 }
 
-/**
- * Runs a string as JavaScript and returns if the call succeed.
- * In case the call succeed, the given *result is filled with the result
- * value, else with the value reference of the exception.
- */
-static gboolean evaluate_string(JSContextRef ctx, const char *script,
-    const char *file, JSValueRef *result)
-{
-    JSStringRef js_str, js_file;
-    JSValueRef exc = NULL, res = NULL;
-
-    js_str  = JSStringCreateWithUTF8CString(script);
-    js_file = JSStringCreateWithUTF8CString(file);
-
-    res = JSEvaluateScript(ctx, js_str, JSContextGetGlobalObject(ctx), js_file, 0, &exc);
-    JSStringRelease(js_file);
-    JSStringRelease(js_str);
-
-    if (exc) {
-        *result = exc;
-        return false;
-    }
-
-    *result = res;
-    return true;
-}
index 5081bf9..1a63d0a 100644 (file)
--- a/src/js.h
+++ b/src/js.h
 #ifndef _JS_H
 #define _JS_H
 
-#include "main.h"
+#include <webkit2/webkit2.h>
 
-gboolean js_eval_file(JSContextRef ctx, const char *file);
-gboolean js_eval(JSContextRef ctx, const char *script, const char *file,
-    char **value);
-JSObjectRef js_create_object(JSContextRef ctx, const char *script);
-char* js_object_call_function(JSContextRef ctx, JSObjectRef obj,
-    const char *func, int count, JSValueRef params[]);
-char *js_ref_to_string(JSContextRef ctx, JSValueRef ref);
-JSValueRef js_string_to_ref(JSContextRef ctx, const char *string);
-JSValueRef js_object_to_ref(JSContextRef ctx, const char *json);
+char *js_result_as_string(WebKitJavascriptResult *res);
 
-#endif /* end of include guard: _JS_H */
+#endif
diff --git a/src/js2h.sh b/src/js2h.sh
deleted file mode 100755 (executable)
index f849e85..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-echo '#define HINTS_JS "' | tr -d '\n'
-cat $1 | \
-    tr '\n\r\t' ' ' | \
-    sed -e 's:/\*[^*]*\*/::g' \
-    -e 's|[ ]\{2,\}| |g' \
-    -e "s|[ ]\{0,\}\([-!?<>:=(){};+\&\"',\|]\)[ ]\{0,\}|\1|g" \
-    -e 's|"+"||g' \
-    -e 's|\\x20| |g' \
-    -e 's|\\|\\\\|g' \
-    -e 's|"|\\"|g' \
-    -e 's|HINT_CSS|" HINT_CSS "|' \
-    -e '$s/$/"/'
-echo ""
index 3b379fc..a8c9f74 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
-#include <stdio.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <gdk/gdkx.h>
-#include "main.h"
-#include "util.h"
-#include "command.h"
-#include "setting.h"
+#include <webkit2/webkit2.h>
+
+#include "ascii.h"
 #include "completion.h"
-#include "dom.h"
-#include "hints.h"
-#include "shortcut.h"
-#include "handlers.h"
-#include "history.h"
-#include "cookiejar.h"
-#include "hsts.h"
-#include "normal.h"
+#include "config.h"
 #include "ex.h"
+#include "ext-proxy.h"
 #include "input.h"
-#include "map.h"
-#include "bookmark.h"
 #include "js.h"
-#include "autocmd.h"
-#include "arh.h"
-#include "io.h"
-#include "ascii.h"
-
-/* variables */
-static char *argv0;
-VbCore      vb;
-
-/* callbacks */
+#include "main.h"
+#include "map.h"
+#include "normal.h"
+#include "scripts/scripts.h"
+#include "setting.h"
+#include "shortcut.h"
+#include "util.h"
 
-static void buffer_changed_cb(GtkTextBuffer* buffer, gpointer data);
-#if WEBKIT_CHECK_VERSION(1, 10, 0)
-static gboolean context_menu_cb(WebKitWebView *view, GtkWidget *menu,
-    WebKitHitTestResult *hitTestResult, gboolean keyboard, gpointer data);
-#else
-static void context_menu_cb(WebKitWebView *view, GtkMenu *menu, gpointer data);
-#endif
-static void context_menu_activate_cb(GtkMenuItem *item, gpointer data);
-static void uri_change_cb(WebKitWebView *view, GParamSpec param_spec);
-static void webview_progress_cb(WebKitWebView *view, GParamSpec *pspec);
-static void webview_download_progress_cb(WebKitWebView *view, GParamSpec *pspec);
-static void webview_load_status_cb(WebKitWebView *view, GParamSpec *pspec);
-static void webview_request_starting_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitWebResource *res, WebKitNetworkRequest *req,
-    WebKitNetworkResponse *resp, gpointer data);
-static gboolean focus_out_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
-static gboolean focus_in_event_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
-static void destroy_window_cb(GtkWidget *widget);
-static void scroll_cb(GtkAdjustment *adjustment);
-static gboolean input_focus_in_cb(GtkWidget *widget, GdkEventFocus *event,
-    gpointer data);
-static WebKitWebView *inspector_new(WebKitWebInspector *inspector, WebKitWebView *webview);
-static gboolean inspector_show(WebKitWebInspector *inspector);
-static gboolean inspector_close(WebKitWebInspector *inspector);
-static void inspector_finished(WebKitWebInspector *inspector);
-static gboolean button_relase_cb(WebKitWebView *webview, GdkEventButton *event);
-static gboolean new_window_policy_cb(
-    WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *navig, WebKitWebPolicyDecision *policy);
-static WebKitWebView *create_web_view_cb(WebKitWebView *view, WebKitWebFrame *frame);
-static gboolean create_web_view_received_uri_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *action, WebKitWebPolicyDecision *policy,
-    gpointer data);
-static gboolean navigation_decision_requested_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *action, WebKitWebPolicyDecision *policy,
-    gpointer data);
-static void onload_event_cb(WebKitWebView *view, WebKitWebFrame *frame,
-    gpointer user_data);
-static void hover_link_cb(WebKitWebView *webview, const char *title, const char *link);
-static void title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, const char *title);
-static gboolean mimetype_decision_cb(WebKitWebView *webview,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request, char*
-    mime_type, WebKitWebPolicyDecision *decision);
-gboolean vb_download(WebKitWebView *view, WebKitDownload *download, const char *path);
-void vb_download_internal(WebKitWebView *view, WebKitDownload *download, const char *file);
-void vb_download_external(WebKitWebView *view, WebKitDownload *download, const char *file);
-static void download_progress_cp(WebKitDownload *download, GParamSpec *pspec);
-static void read_from_stdin(void);
-#ifdef FEATURE_ARH
-static void session_request_queued_cb(SoupSession *session, SoupMessage *msg, gpointer data);
+static void client_destroy(Client *c);
+static Client *client_new(WebKitWebView *webview);
+static gboolean input_clear(Client *c);
+static void input_print(Client *c, gboolean force, MessageType type,
+        gboolean hide, const char *message);
+static void marks_clear(Client *c);
+static void mode_free(Mode *mode);
+static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data);
+static void on_webview_close(WebKitWebView *webview, Client *c);
+static WebKitWebView *on_webview_create(WebKitWebView *webview,
+        WebKitNavigationAction *navact, Client *c);
+static gboolean on_webview_decide_policy(WebKitWebView *webview,
+        WebKitPolicyDecision *dec, WebKitPolicyDecisionType type, Client *c);
+static void on_webview_load_changed(WebKitWebView *webview,
+        WebKitLoadEvent event, Client *c);
+static void on_webview_mouse_target_changed(WebKitWebView *webview,
+        WebKitHitTestResult *result, guint modifiers, Client *c);
+static void on_webview_notify_estimated_load_progress(WebKitWebView *webview,
+        GParamSpec *spec, Client *c);
+static void on_webview_notify_title(WebKitWebView *webview, GParamSpec *pspec,
+        Client *c);
+static void on_webview_notify_uri(WebKitWebView *webview, GParamSpec *pspec,
+        Client *c);
+static void on_webview_ready_to_show(WebKitWebView *webview, Client *c);
+static gboolean on_webview_web_process_crashed(WebKitWebView *webview, Client *c);
+static void on_window_destroy(GtkWidget *window, Client *c);
+static gboolean quit(Client *c);
+static void register_cleanup(Client *c);
+static void update_urlbar(Client *c);
+static void set_statusbar_style(Client *c, StatusType type);
+static void set_title(Client *c, const char *title);
+static void spawn_new_instance(const char *uri, gboolean embed);
+#ifdef FREE_ON_QUIT
+static void vimb_cleanup(void);
 #endif
+static void vimb_setup(void);
+static WebKitWebView *webview_new(Client *c, WebKitWebView *webview);
+
+struct Vimb vb;
 
-/* functions */
-#ifdef FEATURE_WGET_PROGRESS_BAR
-static void wget_bar(int len, int progress, char *string);
-#endif
-static void update_title(void);
-static void set_uri(const char *uri);
-static void set_title(const char *title);
-static void init_core(void);
-static void marks_clear(void);
-static void setup_signals();
-static void init_files(void);
-static void session_init(void);
-static void session_cleanup(void);
-static void register_init(void);
-static void register_cleanup(void);
-static gboolean hide_message();
-static void set_status(const StatusType status);
-static void input_print(gboolean force, const MessageType type, gboolean hide, const char *message);
-static void vb_cleanup(void);
-static void cleanup_modes(void);
-static void free_mode(Mode *mode);
 
 /**
- * Creates a new mode with given callback functions.
+ * Write text to the inpubox if this isn't focused.
  */
-void vb_add_mode(char id, ModeTransitionFunc enter, ModeTransitionFunc leave,
-    ModeKeyFunc keypress, ModeInputChangedFunc input_changed)
-{
-    Mode *new = g_slice_new(Mode);
-    new->id            = id;
-    new->enter         = enter;
-    new->leave         = leave;
-    new->keypress      = keypress;
-    new->input_changed = input_changed;
-    new->flags         = 0;
-
-    /* Initialize the hashmap if this was not done before */
-    if (!vb.modes) {
-        vb.modes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)free_mode);
-    }
-    g_hash_table_insert(vb.modes, GINT_TO_POINTER(id), new);
-}
-
-void vb_echo_force(const MessageType type, gboolean hide, const char *error, ...)
+void vb_echo(Client *c, MessageType type, gboolean hide, const char *error, ...)
 {
     char *buffer;
     va_list args;
@@ -157,11 +95,15 @@ void vb_echo_force(const MessageType type, gboolean hide, const char *error, ...
     buffer = g_strdup_vprintf(error, args);
     va_end(args);
 
-    input_print(true, type, hide, buffer);
+    input_print(c, FALSE, type, hide, buffer);
     g_free(buffer);
 }
 
-void vb_echo(const MessageType type, gboolean hide, const char *error, ...)
+/**
+ * Write text to the inpubox independent if this is focused or not.
+ * Note that this could disturb the user during typing into inputbox.
+ */
+void vb_echo_force(Client *c, MessageType type, gboolean hide, const char *error, ...)
 {
     char *buffer;
     va_list args;
@@ -170,28 +112,28 @@ void vb_echo(const MessageType type, gboolean hide, const char *error, ...)
     buffer = g_strdup_vprintf(error, args);
     va_end(args);
 
-    input_print(false, type, hide, buffer);
+    input_print(c, TRUE, type, hide, buffer);
     g_free(buffer);
 }
 
 /**
  * Enter into the new given mode and leave possible active current mode.
  */
-void vb_enter(char id)
+void vb_enter(Client *c, char id)
 {
     Mode *new = g_hash_table_lookup(vb.modes, GINT_TO_POINTER(id));
 
     g_return_if_fail(new != NULL);
 
-    if (vb.mode) {
+    if (c->mode) {
         /* don't do anything if the mode isn't a new one */
-        if (vb.mode == new) {
+        if (c->mode == new) {
             return;
         }
 
         /* if there is a active mode, leave this first */
-        if (vb.mode->leave) {
-            vb.mode->leave();
+        if (c->mode->leave) {
+            c->mode->leave(c);
         }
     }
 
@@ -199,14 +141,14 @@ void vb_enter(char id)
     new->flags = 0;
 
     /* set the new mode so that it is available also in enter function */
-    vb.mode = new;
+    c->mode = new;
     /* call enter only if the new mode isn't the current mode */
     if (new->enter) {
-        new->enter();
+        new->enter(c);
     }
 
 #ifndef TESTLIB
-    vb_update_statusbar();
+    vb_statusbar_update(c);
 #endif
 }
 
@@ -218,111 +160,69 @@ void vb_enter(char id)
  * @print_prompt: Indicates if the new set prompt should be put into inputbox
  *                after switching the mode.
  */
-void vb_enter_prompt(char id, const char *prompt, gboolean print_prompt)
+void vb_enter_prompt(Client *c, char id, const char *prompt, gboolean print_prompt)
 {
     /* set the prompt to be accessible in vb_enter */
-    strncpy(vb.state.prompt, prompt, PROMPT_SIZE - 1);
-    vb.state.prompt[PROMPT_SIZE - 1] = '\0';
+    strncpy(c->state.prompt, prompt, PROMPT_SIZE - 1);
+    c->state.prompt[PROMPT_SIZE - 1] = '\0';
 
-    vb_enter(id);
+    vb_enter(c, id);
 
     if (print_prompt) {
         /* set it after the mode was entered so that the modes input change
          * event listener could grep the new prompt */
-        vb_echo_force(VB_MSG_NORMAL, false, vb.state.prompt);
-    }
-}
-
-VbResult vb_handle_key(int key)
-{
-    VbResult res;
-    static gboolean ctrl_v = false;
-
-    if (ctrl_v) {
-        vb.state.processed_key = false;
-        ctrl_v = false;
-
-        return RESULT_COMPLETE;
-    }
-    if (vb.mode->id != 'p' && key == CTRL('V')) {
-        vb.mode->flags |= FLAG_NOMAP;
-        ctrl_v = true;
-
-        return RESULT_MORE;
-    }
-
-    if (vb.mode && vb.mode->keypress) {
-#ifdef DEBUG
-        int flags = vb.mode->flags;
-        int id    = vb.mode->id;
-        res = vb.mode->keypress(key);
-        if (vb.mode) {
-            PRINT_DEBUG(
-                "%c[%d]: %#.2x '%c' -> %c[%d]",
-                id - ' ', flags, key, (key >= 0x20 && key <= 0x7e) ? key : ' ',
-                vb.mode->id - ' ', vb.mode->flags
-            );
-        }
-#else
-        res = vb.mode->keypress(key);
-#endif
-        return res;
+        vb_echo_force(c, MSG_NORMAL, FALSE, c->state.prompt);
     }
-    return RESULT_ERROR;
 }
 
-static void input_print(gboolean force, const MessageType type, gboolean hide,
-    const char *message)
+/**
+ * Retrieves the content of the command line.
+ * Returned string must be freed with g_free.
+ */
+char *vb_input_get_text(Client *c)
 {
-    static guint timer = 0;
-
-    /* don't print message if the input is focussed */
-    if (!force && gtk_widget_is_focus(GTK_WIDGET(vb.gui.input))) {
-        return;
-    }
+    GtkTextIter start, end;
 
-    /* apply input style only if the message type was changed */
-    if (type != vb.state.input_type) {
-        vb.state.input_type = type;
-        vb_update_input_style();
-    }
-    vb_set_input_text(message);
-    if (hide) {
-        /* add timeout function */
-        timer = g_timeout_add_seconds(MESSAGE_TIMEOUT, (GSourceFunc)hide_message, NULL);
-    } else if (timer > 0) {
-        /* If there is already a timeout function but the input box content is
-         * changed - remove the timeout. Seems the user started another
-         * command or typed into inputbox. */
-        g_source_remove(timer);
-        timer = 0;
-    }
+    gtk_text_buffer_get_bounds(c->buffer, &start, &end);
+    return gtk_text_buffer_get_text(c->buffer, &start, &end, FALSE);
 }
 
 /**
  * Writes given text into the command line.
  */
-void vb_set_input_text(const char *text)
+void vb_input_set_text(Client *c, const char *text)
 {
-    gtk_text_buffer_set_text(vb.gui.buffer, text, -1);
-    if (vb.config.input_autohide) {
-        gtk_widget_set_visible(GTK_WIDGET(vb.gui.input), *text != '\0');
+    gtk_text_buffer_set_text(c->buffer, text, -1);
+    if (c->config.input_autohide) {
+        gtk_widget_set_visible(GTK_WIDGET(c->input), *text != '\0');
     }
 }
 
 /**
- * Retrieves the content of the command line.
- * Returned string must be freed with g_free.
+ * Set the style of the inputbox according to current input type (normal or
+ * error).
  */
-char *vb_get_input_text(void)
+void vb_input_update_style(Client *c)
 {
-    GtkTextIter start, end;
+    MessageType type = c->state.input_type;
 
-    gtk_text_buffer_get_bounds(vb.gui.buffer, &start, &end);
-    return gtk_text_buffer_get_text(vb.gui.buffer, &start, &end, false);
+    if (type == MSG_ERROR) {
+        gtk_style_context_add_class(gtk_widget_get_style_context(c->input), "error");
+    } else {
+        gtk_style_context_remove_class(gtk_widget_get_style_context(c->input), "error");
+    }
 }
 
-gboolean vb_load_uri(const Arg *arg)
+/**
+ * Load the a uri given in Arg. This function handles also shortcuts and local
+ * file paths.
+ *
+ * If arg.i = TARGET_CURRENT, the url is opened into the current webview.
+ * TARGET_RELATED causes the generation of a new window within the current
+ * instance of vimb with a own, but related webview. And TARGET_NEW spawns a
+ * new instance of vimb with the given uri.
+ */
+gboolean vb_load_uri(Client *c, const Arg *arg)
 {
     char *uri = NULL, *rp, *path = NULL;
     struct stat st;
@@ -331,7 +231,7 @@ gboolean vb_load_uri(const Arg *arg)
         path = g_strstrip(arg->s);
     }
     if (!path || !*path) {
-        path = GET_CHAR("home-page");
+        path = GET_CHAR(c, "home-page");
     }
 
     /* If path contains :// but no space we open it direct. This is required
@@ -345,1548 +245,964 @@ gboolean vb_load_uri(const Arg *arg)
         free(rp);
     } else if (strchr(path, ' ') || !strchr(path, '.')) {
         /* use a shortcut if path contains spaces or no dot */
-        uri = shortcut_get_uri(path);
+        uri = shortcut_get_uri(c, path);
     }
 
     if (!uri) {
         uri = g_strconcat("http://", path, NULL);
     }
 
-    if (arg->i == VB_TARGET_NEW) {
-        guint i = 0;
-
-        /* memory allocation */
-        char **cmd = g_malloc_n(
-            3                       /* basename + uri + ending NULL */
-            + (vb.embed ? 2 : 0)
-            + (vb.config.file ? 2 : 0)
-            + (vb.config.profile ? 2 : 0)
-            + (vb.config.kioskmode ? 1 : 0)
-#ifdef FEATURE_SOCKET
-            + (vb.config.socket ? 1 : 0)
-#endif
-            + g_slist_length(vb.config.cmdargs) * 2,
-            sizeof(char *)
-        );
-
-        /* build commandline */
-        cmd[i++] = argv0;
-        if (vb.embed) {
-            char xid[64];
-            snprintf(xid, LENGTH(xid), "%u", (int)vb.embed);
-            cmd[i++] = "-e";
-            cmd[i++] = xid;
-        }
-        if (vb.config.file) {
-            cmd[i++] = "-c";
-            cmd[i++] = vb.config.file;
-        }
-        if (vb.config.profile) {
-            cmd[i++] = "-p";
-            cmd[i++] = vb.config.profile;
-        }
-        for (GSList *l = vb.config.cmdargs; l; l = l->next) {
-            cmd[i++] = "-C";
-            cmd[i++] = l->data;
-        }
-        if (vb.config.kioskmode) {
-            cmd[i++] = "-k";
-        }
-#ifdef FEATURE_SOCKET
-        if (vb.config.socket) {
-            cmd[i++] = "-s";
-        }
-#endif
-        cmd[i++] = uri;
-        cmd[i++] = NULL;
-
-        /* spawn a new browser instance */
-        g_spawn_async(NULL, cmd, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
-
-        /* free commandline */
-        g_free(cmd);
-    } else {
-        /* Load a web page into the browser instance */
-        webkit_web_view_load_uri(vb.gui.webview, uri);
-        /* show the url to be opened in the window title until we receive the
-         * page title */
-        set_title(uri);
+    if (arg->i == TARGET_CURRENT) {
+        /* Load the uri into the browser instance. */
+        webkit_web_view_load_uri(c->webview, uri);
+        set_title(c, uri);
+    } else if (arg->i == TARGET_NEW) {
+        spawn_new_instance(uri, TRUE);
+    } else { /* TARGET_RELATET */
+        Client *newclient = client_new(c->webview);
+        /* Load the uri into the new client. */
+        webkit_web_view_load_uri(newclient->webview, uri);
+        set_title(c, uri);
     }
     g_free(uri);
 
-    return true;
+    return TRUE;
+}
+
+/**
+ * Creates and add a new mode with given callback functions.
+ */
+void vb_mode_add(char id, ModeTransitionFunc enter, ModeTransitionFunc leave,
+    ModeKeyFunc keypress, ModeInputChangedFunc input_changed)
+{
+    Mode *new = g_slice_new(Mode);
+    new->id            = id;
+    new->enter         = enter;
+    new->leave         = leave;
+    new->keypress      = keypress;
+    new->input_changed = input_changed;
+    new->flags         = 0;
+
+    /* Initialize the hashmap if this was not done before */
+    if (!vb.modes) {
+        vb.modes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)mode_free);
+    }
+    g_hash_table_insert(vb.modes, GINT_TO_POINTER(id), new);
 }
 
-gboolean vb_set_clipboard(const Arg *arg)
+VbResult vb_mode_handle_key(Client *c, int key)
 {
-    gboolean result = false;
-    if (!arg->s) {
-        return result;
+    VbResult res;
+
+    if (c->state.ctrlv) {
+        c->state.processed_key = FALSE;
+        c->state.ctrlv         = FALSE;
+
+        return RESULT_COMPLETE;
     }
+    if (c->mode->id != 'p' && key == CTRL('V')) {
+        c->mode->flags |= FLAG_NOMAP;
+        c->state.ctrlv  = TRUE;
 
-    if (arg->i & VB_CLIPBOARD_PRIMARY) {
-        gtk_clipboard_set_text(PRIMARY_CLIPBOARD(), arg->s, -1);
-        result = true;
+        return RESULT_MORE;
     }
-    if (arg->i & VB_CLIPBOARD_SECONDARY) {
-        gtk_clipboard_set_text(SECONDARY_CLIPBOARD(), arg->s, -1);
-        result = true;
+
+    if (c->mode && c->mode->keypress) {
+#ifdef DEBUGDISABLED
+        int flags = c->mode->flags;
+        int id    = c->mode->id;
+        res = c->mode->keypress(c, key);
+        if (c->mode) {
+            PRINT_DEBUG(
+                "%c[%d]: %#.2x '%c' -> %c[%d]",
+                id - ' ', flags, key, (key >= 0x20 && key <= 0x7e) ? key : ' ',
+                c->mode->id - ' ', c->mode->flags
+            );
+        }
+#else
+        res = c->mode->keypress(c, key);
+#endif
+        return res;
     }
+    return RESULT_ERROR;
+}
 
-    return result;
+/**
+ * Change the label for the current mode in inputbox or on the left of
+ * statusbar if inputbox is in autohide mode.
+ */
+void vb_modelabel_update(Client *c, const char *label)
+{
+    if (c->config.input_autohide) {
+        /* if the inputbox is potentially not shown write mode into statusbar */
+        gtk_label_set_text(GTK_LABEL(c->statusbar.mode), label);
+    } else {
+        vb_echo(c, MSG_NORMAL, FALSE, "%s", label);
+    }
+    gtk_label_set_text(GTK_LABEL(c->statusbar.mode), label);
 }
 
-void vb_set_widget_font(GtkWidget *widget, const VbColor *fg, const VbColor *bg, PangoFontDescription *font)
+/**
+ * Close the given client instances window.
+ */
+void vb_quit(Client *c, gboolean force)
 {
-    VB_WIDGET_OVERRIDE_FONT(widget, font);
-    VB_WIDGET_OVERRIDE_TEXT(widget, VB_GTK_STATE_NORMAL, fg);
-    VB_WIDGET_OVERRIDE_COLOR(widget, VB_GTK_STATE_NORMAL, fg);
-    VB_WIDGET_OVERRIDE_BASE(widget, VB_GTK_STATE_NORMAL, bg);
-    VB_WIDGET_OVERRIDE_BACKGROUND(widget, VB_GTK_STATE_NORMAL, bg);
+#if 0 /* TODO don't quit on running downloads */
+    /* if not forced quit - don't quit if there are still running downloads */
+    if (!force && c->state.downloads) {
+        vb_echo_force(c, VB_MSG_ERROR, TRUE, "Can't quit: there are running downloads");
+        return;
+    }
+#endif
+    /* Don't run the quit synchronously, because this could lead to access of
+     * no more existing widget where some command response is written. */
+    g_idle_add((GSourceFunc)quit, c);
 }
 
-#ifdef FEATURE_WGET_PROGRESS_BAR
-static void wget_bar(int len, int progress, char *string)
+/**
+ * Adds content to a named register.
+ */
+void vb_register_add(Client *c, char buf, const char *value)
 {
-    int i, state;
+    char *mark;
+    int idx;
 
-    state = progress * len / 100;
-    for (i = 0; i < state; i++) {
-        string[i] = PROGRESS_BAR[0];
+    if (!c->state.enable_register || !buf) {
+        return;
     }
-    string[i++] = PROGRESS_BAR[1];
-    for (; i < len; i++) {
-        string[i] = PROGRESS_BAR[2];
+
+    /* make sure the mark is a valid mark char */
+    if ((mark = strchr(REG_CHARS, buf))) {
+        /* get the index of the mark char */
+        idx = mark - REG_CHARS;
+
+        OVERWRITE_STRING(c->state.reg[idx], value);
     }
-    string[i] = '\0';
 }
-#endif
 
-void vb_update_statusbar()
+/**
+ * Lookup register entry by it's name.
+ */
+const char *vb_register_get(Client *c, char buf)
+{
+    char *mark;
+    int idx;
+
+    /* make sure the mark is a valid mark char */
+    if ((mark = strchr(REG_CHARS, buf))) {
+        /* get the index of the mark char */
+        idx = mark - REG_CHARS;
+
+        return c->state.reg[idx];
+    }
+
+    return NULL;
+}
+
+void vb_statusbar_update(Client *c)
 {
-    int max, val, num;
+    int num;
     GString *status;
 
-    if (!gtk_widget_get_visible(GTK_WIDGET(vb.gui.statusbar.box))) {
+    if (!gtk_widget_get_visible(GTK_WIDGET(c->statusbar.box))) {
         return;
     }
 
     status = g_string_new("");
     /* show the active downloads */
-    if (vb.state.downloads) {
-        num = g_list_length(vb.state.downloads);
+    if (c->state.downloads) {
+        num = g_list_length(c->state.downloads);
         g_string_append_printf(status, " %d %s", num, num == 1 ? "download" : "downloads");
     }
 
-#ifdef FEATURE_SEARCH_HIGHLIGHT
     /* show the number of matches search results */
-    if (vb.state.search_matches) {
-        g_string_append_printf(status, " (%d)", vb.state.search_matches);
+    if (c->state.search.matches) {
+        g_string_append_printf(status, " (%d)", c->state.search.matches);
     }
-#endif
 
     /* show load status of page or the downloads */
-    if (vb.state.progress != 100) {
+    if (c->state.progress != 100) {
 #ifdef FEATURE_WGET_PROGRESS_BAR
         char bar[PROGRESS_BAR_LEN + 1];
-        wget_bar(PROGRESS_BAR_LEN, vb.state.progress, bar);
+        int i, state;
+
+        state = c->state.progress * PROGRESS_BAR_LEN / 100;
+        for (i = 0; i < state; i++) {
+            bar[i] = PROGRESS_BAR[0];
+        }
+        bar[i++] = PROGRESS_BAR[1];
+        for (; i < PROGRESS_BAR_LEN; i++) {
+            bar[i] = PROGRESS_BAR[2];
+        }
+        bar[i] = '\0';
         g_string_append_printf(status, " [%s]", bar);
 #else
-        g_string_append_printf(status, " [%i%%]", vb.state.progress);
+        g_string_append_printf(status, " [%i%%]", c->state.progress);
 #endif
     }
 
     /* show the scroll status */
-    max = gtk_adjustment_get_upper(vb.gui.adjust_v) - gtk_adjustment_get_page_size(vb.gui.adjust_v);
-    val = (int)(0.5 + (gtk_adjustment_get_value(vb.gui.adjust_v) / max * 100));
-
-    if (max == 0) {
+    if (c->state.scroll_max == 0) {
         g_string_append(status, " All");
-    } else if (val == 0) {
+    } else if (c->state.scroll_percent == 0) {
         g_string_append(status, " Top");
-    } else if (val >= 100) {
+    } else if (c->state.scroll_percent == 100) {
         g_string_append(status, " Bot");
     } else {
-        g_string_append_printf(status, " %d%%", val);
+        g_string_append_printf(status, " %d%%", c->state.scroll_percent);
     }
 
-    gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.right), status->str);
-    g_string_free(status, true);
+    gtk_label_set_text(GTK_LABEL(c->statusbar.right), status->str);
+    g_string_free(status, TRUE);
 }
 
-void vb_update_status_style(void)
+/**
+ * Destroys given client and removed it from client queue. If no client is
+ * there in queue, quit the gtk main loop.
+ */
+static void client_destroy(Client *c)
 {
-    StatusType type = vb.state.status_type;
-    vb_set_widget_font(
-        vb.gui.eventbox, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
-    );
-#ifndef HAS_GTK3
-    vb_set_widget_font(
-        vb.gui.statusbar.mode, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
-    );
-    vb_set_widget_font(
-        vb.gui.statusbar.left, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
-    );
-    vb_set_widget_font(
-        vb.gui.statusbar.right, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
-    );
-    vb_set_widget_font(
-        vb.gui.statusbar.cmd, &vb.style.status_fg[type], &vb.style.status_bg[type], vb.style.status_font[type]
-    );
-#endif
-}
+    Client *p;
+    webkit_web_view_stop_loading(c->webview);
+    gtk_widget_destroy(c->window);
 
-void vb_update_input_style(void)
-{
-    MessageType type = vb.state.input_type;
-    vb_set_widget_font(
-        vb.gui.input, &vb.style.input_fg[type], &vb.style.input_bg[type], vb.style.input_font[type]
-    );
+    /* Look for the client in the list, if we searched through the list and
+     * didn't find it the client must be the first item. */
+    for (p = vb.clients; p && p->next != c; p = p->next);
+    if (p) {
+        p->next = c->next;
+    } else {
+        vb.clients = c->next;
+    }
+
+    completion_cleanup(c);
+    map_cleanup(c);
+    register_cleanup(c);
+
+    g_slice_free(Client, c);
+
+    /* if there are no clients - quit the main loop */
+    if (!vb.clients) {
+        gtk_main_quit();
+    }
 }
 
-void vb_update_urlbar(const char *uri)
+/**
+ * Creates a new client instance with it's own window.
+ *
+ * @webview:    Related webview or NULL if a client with an independent
+ *              webview shoudl be created.
+ */
+static Client *client_new(WebKitWebView *webview)
 {
-    Gui *gui = &vb.gui;
-#if !defined(FEATURE_HISTORY_INDICATOR) && !defined(FEATURE_PROFILE_INDICATOR)
-    /* if only the uri is shown - write it like it is on the label */
-    gtk_label_set_text(GTK_LABEL(gui->statusbar.left), uri);
-#else
-    GString *str = g_string_new("");
-#ifdef FEATURE_PROFILE_INDICATOR
-    if (vb.config.profile) {
-        g_string_append_printf(str, "[%s] ", vb.config.profile);
+    Client *c;
+    char *xid;
+    GtkWidget *box;
+
+    /* create the client */
+    c = g_slice_new0(Client);
+    c->state.progress = 100;
+
+    if (vb.embed) {
+        c->window = gtk_plug_new(vb.embed);
+        xid       = g_strdup_printf("%d", (int)vb.embed);
+    } else {
+        c->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+        gtk_window_set_role(GTK_WINDOW(c->window), PROJECT_UCFIRST);
+        gtk_widget_realize(GTK_WIDGET(c->window));
+
+        xid = g_strdup_printf("%d",
+                (int)GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(c->window))));
     }
-#endif /* FEATURE_PROFILE_INDICATOR */
 
-    g_string_append_printf(str, "%s", uri);
+    completion_init(c);
+    map_init(c);
 
-#ifdef FEATURE_HISTORY_INDICATOR
-    gboolean back, fwd;
+    g_object_connect(
+            G_OBJECT(c->window),
+            "signal::destroy", G_CALLBACK(on_window_destroy), c,
+            "signal::key-press-event", G_CALLBACK(on_map_keypress), c,
+            NULL);
+
+    /* statusbar */
+    c->statusbar.box   = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
+    c->statusbar.mode  = gtk_label_new(NULL);
+    c->statusbar.left  = gtk_label_new(NULL);
+    c->statusbar.right = gtk_label_new(NULL);
+    c->statusbar.cmd   = gtk_label_new(NULL);
+    gtk_widget_set_name(GTK_WIDGET(c->statusbar.box), "statusbar");
+    gtk_label_set_ellipsize(GTK_LABEL(c->statusbar.left), PANGO_ELLIPSIZE_MIDDLE);
+    gtk_widget_set_halign(c->statusbar.left, GTK_ALIGN_START);
+    gtk_widget_set_halign(c->statusbar.mode, GTK_ALIGN_START);
+
+    gtk_box_pack_start(c->statusbar.box, c->statusbar.mode, FALSE, TRUE, 0);
+    gtk_box_pack_start(c->statusbar.box, c->statusbar.left, TRUE, TRUE, 2);
+    gtk_box_pack_start(c->statusbar.box, c->statusbar.cmd, FALSE, FALSE, 0);
+    gtk_box_pack_start(c->statusbar.box, c->statusbar.right, FALSE, FALSE, 2);
+
+    /* webview */
+    c->webview = webview_new(c, webview);
+    c->page_id = webkit_web_view_get_page_id(c->webview);
+
+    /* inputbox */
+    c->input  = gtk_text_view_new();
+    gtk_widget_set_name(c->input, "input");
+    c->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(c->input));
+    /* Make sure the user can see the typed text. */
+    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(c->input), GTK_WRAP_WORD_CHAR);
+
+    /* pack the parts together */
+    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    gtk_container_add(GTK_CONTAINER(c->window), box);
+    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(c->webview), TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(c->statusbar.box), FALSE, FALSE, 0);
+    gtk_box_pack_end(GTK_BOX(box), GTK_WIDGET(c->input), FALSE, FALSE, 0);
+
+    /* Set the default style for statusbar and inputbox. */
+    GtkCssProvider* provider = gtk_css_provider_get_default();
+    gtk_css_provider_load_from_data(provider, GUI_STYLE, -1, NULL);
+    gtk_style_context_add_provider(gtk_widget_get_style_context(GTK_WIDGET(c->statusbar.box)),
+            GTK_STYLE_PROVIDER(provider),
+            GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+    gtk_style_context_add_provider(gtk_widget_get_style_context(c->input),
+            GTK_STYLE_PROVIDER(provider),
+            GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+    /* set the x window id to env */
+    g_setenv("VIMB_XID", xid, TRUE);
+    g_free(xid);
 
-    back = webkit_web_view_can_go_back(gui->webview);
-    fwd  = webkit_web_view_can_go_forward(gui->webview);
+    /* initialize the settings */
+    setting_init(c);
 
-    /* show history indicator only if there is something to show */
-    if (back || fwd) {
-        g_string_append_printf(str, " [%s]", back ? (fwd ? "-+" : "-") : "+");
-    }
-#endif /* FEATURE_HISTORY_INDICATOR */
+    /* start client in normal mode */
+    vb_enter(c, 'n');
 
-    gtk_label_set_text(GTK_LABEL(gui->statusbar.left), str->str);
-    g_string_free(str, true);
-#endif /* !defined(FEATURE_HISTORY_INDICATOR) && !defined(FEATURE_PROFILE_INDICATOR) */
+    gtk_widget_show_all(c->window);
+
+    /* Prepend the new client to the queue of clients. */
+    c->next    = vb.clients;
+    vb.clients = c;
+
+    return c;
 }
 
-void vb_update_mode_label(const char *label)
+/**
+ * Callback that clear the input box after a timeout if this was set on
+ * input_print.
+ */
+static gboolean input_clear(Client *c)
 {
-    if (GET_BOOL("input-autohide")) {
-        /* if the inputbox is potentially not shown write mode into statusbar */
-        gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.mode), label);
-    } else {
-        vb_echo(VB_MSG_NORMAL, false, "%s", label);
-    }
+    input_print(c, FALSE, MSG_NORMAL, FALSE, "");
+
+    return FALSE;
 }
 
-void vb_quit(gboolean force)
+/**
+ * Print a message to the input box.
+ *
+ * @force: If TRUE the message is also written when the inputbox is already
+ *         focused, might be the case when the user types text into it.
+ * @type: Type of message normal or error
+ * @hide: If TRUE the inputbox is cleared after a short timeout.
+ * @message: The message to print.
+ */
+static void input_print(Client *c, gboolean force, MessageType type,
+        gboolean hide, const char *message)
 {
-    /* if not forced quit - don't quit if there are still running downloads */
-    if (!force && vb.state.downloads) {
-        vb_echo_force(VB_MSG_ERROR, true, "Can't quit: there are running downloads");
+    /* don't print message if the input is focussed */
+    if (!force && gtk_widget_is_focus(GTK_WIDGET(c->input))) {
         return;
     }
 
-    webkit_web_view_stop_loading(vb.gui.webview);
-
-    /* write last URL into file for recreation */
-    if (vb.state.uri && vb.config.closed_max) {
-        char **lines = util_get_lines(vb.files[FILES_CLOSED]);
-        GString *new = g_string_new(vb.state.uri);
-        g_string_append(new, "\n");
-        if (lines) {
-            int len = g_strv_length(lines);
-            int i;
-            for (i = 0; i < len - 1 && i < vb.config.closed_max - 1; i++) {
-                g_string_append_printf(new, "%s\n", lines[i]);
-            }
-            g_strfreev(lines);
-        }
-        g_file_set_contents(vb.files[FILES_CLOSED], new->str, -1, NULL);
-        g_string_free(new, true);
+    /* apply input style only if the message type was changed */
+    if (type != c->state.input_type) {
+        c->state.input_type = type;
+    }
+    vb_input_set_text(c, message);
+    if (hide) {
+        /* add timeout function */
+        c->state.input_timer = g_timeout_add_seconds(MESSAGE_TIMEOUT, (GSourceFunc)input_clear, c);
+    } else if (c->state.input_timer > 0) {
+        /* If there is already a timeout function but the input box content is
+         * changed - remove the timeout. Seems the user started another
+         * command or typed into inputbox. */
+        g_source_remove(c->state.input_timer);
+        c->state.input_timer = 0;
     }
-
-    gtk_main_quit();
 }
 
-static gboolean hide_message()
+/**
+ * Reinitializes or clears the set page marks.
+ */
+static void marks_clear(Client *c)
 {
-    input_print(false, VB_MSG_NORMAL, false, "");
+    int i;
 
-    return false;
+    /* init empty marks array */
+    for (i = 0; i < MARK_SIZE; i++) {
+        c->state.marks[i] = -1;
+    }
 }
 
 /**
- * Process input changed event on current active mode.
+ * Free the memory of given mode. This is used as destroy function of the
+ * modes hashmap.
  */
-static void buffer_changed_cb(GtkTextBuffer* buffer, gpointer data)
+static void mode_free(Mode *mode)
 {
-    char *text;
-    GtkTextIter start, end;
-    /* don't observe changes in completion mode */
-    if (vb.mode->flags & FLAG_COMPLETION) {
+    g_slice_free(Mode, mode);
+}
+
+/**
+ * Set the style of the statusbar.
+ */
+static void set_statusbar_style(Client *c, StatusType type)
+{
+    GtkStyleContext *ctx;
+    /* Do nothing if the new to set style is the same as the current. */
+    if (type == c->state.status_type) {
         return;
     }
-    /* don't process changes not typed by the user */
-    if (gtk_widget_is_focus(vb.gui.input) && vb.mode && vb.mode->input_changed) {
-        gtk_text_buffer_get_bounds(buffer, &start, &end);
-        text = gtk_text_buffer_get_text(buffer, &start, &end, false);
 
-        vb.mode->input_changed(text);
+    ctx = gtk_widget_get_style_context(GTK_WIDGET(c->statusbar.box));
 
-        g_free(text);
+    if (type == STATUS_SSL_VALID) {
+        gtk_style_context_remove_class(ctx, "unsecure");
+        gtk_style_context_add_class(ctx, "secure");
+    } else if (type == STATUS_SSL_INVALID) {
+        gtk_style_context_remove_class(ctx, "secure");
+        gtk_style_context_add_class(ctx, "unsecure");
+    } else {
+        gtk_style_context_remove_class(ctx, "secure");
+        gtk_style_context_remove_class(ctx, "unsecure");
     }
+    c->state.status_type = type;
 }
 
-#if WEBKIT_CHECK_VERSION(1, 10, 0)
-static gboolean context_menu_cb(WebKitWebView *view, GtkWidget *menu,
-    WebKitHitTestResult *hitTestResult, gboolean keyboard, gpointer data)
+/**
+ * Update the window title of the main window.
+ */
+static void set_title(Client *c, const char *title)
 {
-    GList *items = gtk_container_get_children(GTK_CONTAINER(GTK_MENU(menu)));
-    for (GList *l = items; l; l = l->next) {
-        g_signal_connect(l->data, "activate", G_CALLBACK(context_menu_activate_cb), NULL);
-    }
-    g_list_free(items);
-
-    return false;
+    gtk_window_set_title(GTK_WINDOW(c->window), title);
 }
-#else
-static void context_menu_cb(WebKitWebView *view, GtkMenu *menu, gpointer data)
-{
-    GList *items = gtk_container_get_children(GTK_CONTAINER(GTK_MENU(menu)));
-    for (GList *l = items; l; l = l->next) {
-        g_signal_connect(l->data, "activate", G_CALLBACK(context_menu_activate_cb), NULL);
-    }
-    g_list_free(items);
-}
-#endif
 
-static void context_menu_activate_cb(GtkMenuItem *item, gpointer data)
-{
-#if WEBKIT_CHECK_VERSION(1, 10, 0)
-    WebKitContextMenuAction action = webkit_context_menu_item_get_action(item);
-    if (action == WEBKIT_CONTEXT_MENU_ACTION_COPY_LINK_TO_CLIPBOARD) {
-        vb_set_clipboard(
-            &((Arg){VB_CLIPBOARD_PRIMARY|VB_CLIPBOARD_SECONDARY, vb.state.linkhover})
-        );
-    }
-#else
-    const char *name;
-    GtkAction *action = gtk_activatable_get_related_action(GTK_ACTIVATABLE(item));
-
-    if (!action) {
-        return;
-    }
-
-    name = gtk_action_get_name(action);
-    /* context-menu-action-3 copy link location */
-    if (!g_strcmp0(name, "context-menu-action-3")) {
-        vb_set_clipboard(
-            &((Arg){VB_CLIPBOARD_PRIMARY|VB_CLIPBOARD_SECONDARY,vb.state.linkhover})
-        );
-    }
-#endif
-}
-
-static void uri_change_cb(WebKitWebView *view, GParamSpec param_spec)
+/**
+ * Spawns a new browser instance for given uri.
+ *
+ * @uri:    URI used for the new instance.
+ * @embed:  If FALSE, the new instance is not embedded, independent from
+ *          current set -e option.
+ */
+static void spawn_new_instance(const char *uri, gboolean embed)
 {
-    set_uri(webkit_web_view_get_uri(view));
-}
+    guint i = 0;
+    char xid[64];
+    char *cmd[5];
 
-static void webview_progress_cb(WebKitWebView *view, GParamSpec *pspec)
-{
-    vb.state.progress = webkit_web_view_get_progress(view) * 100;
-    vb_update_statusbar();
-    update_title();
-}
+    cmd[i++] = vb.argv0;
 
-static void webview_download_progress_cb(WebKitWebView *view, GParamSpec *pspec)
-{
-    if (vb.state.downloads) {
-        vb.state.progress = 0;
-        GList *ptr;
-        for (ptr = vb.state.downloads; ptr; ptr = g_list_next(ptr)) {
-            vb.state.progress += 100 * webkit_download_get_progress(ptr->data);
-        }
-        vb.state.progress /= g_list_length(vb.state.downloads);
+    if (vb.embed && embed) {
+        cmd[i++] = "-e";
+        snprintf(xid, LENGTH(xid), "%d", (int)vb.embed);
+        cmd[i++] = xid;
     }
-    vb_update_statusbar();
-    update_title();
-}
-
-static void webview_load_status_cb(WebKitWebView *view, GParamSpec *pspec)
-{
-    const char *uri;
-    WebKitWebFrame *frame = webkit_web_view_get_main_frame(view);
-
-    switch (webkit_web_view_get_load_status(view)) {
-        case WEBKIT_LOAD_PROVISIONAL:
-#ifdef FEATURE_AUTOCMD
-            {
-                WebKitWebDataSource *src  = webkit_web_frame_get_provisional_data_source(frame);
-                WebKitNetworkRequest *req = webkit_web_data_source_get_initial_request(src);
-                uri = webkit_network_request_get_uri(req);
-                autocmd_run(AU_LOAD_PROVISIONAL, uri, NULL);
-            }
-#endif
-            /* update load progress in statusbar */
-            vb.state.progress = 0;
-            vb_update_statusbar();
-            update_title();
-            break;
-
-        case WEBKIT_LOAD_COMMITTED:
-            uri = webkit_web_view_get_uri(view);
-#ifdef FEATURE_AUTOCMD
-            autocmd_run(AU_LOAD_COMMITED, uri, NULL);
-#endif
-            {
-                JSContextRef ctx;
-                /* set the status */
-                if (g_str_has_prefix(uri, "https://")) {
-                    WebKitWebDataSource *src      = webkit_web_frame_get_data_source(frame);
-                    WebKitNetworkRequest *request = webkit_web_data_source_get_request(src);
-                    SoupMessage *msg              = webkit_network_request_get_message(request);
-                    SoupMessageFlags flags        = soup_message_get_flags(msg);
-                    set_status(
-                        (flags & SOUP_MESSAGE_CERTIFICATE_TRUSTED) ? VB_STATUS_SSL_VALID : VB_STATUS_SSL_INVALID
-                    );
-                } else {
-                    set_status(VB_STATUS_NORMAL);
-                }
-
-                /* inject the hinting javascript */
-                hints_init(frame);
-
-                /* run user script file */
-                ctx = webkit_web_frame_get_global_context(frame);
-                js_eval_file(ctx, vb.files[FILES_SCRIPT]);
-            }
-
-            vb_update_statusbar();
-            set_uri(uri);
-            set_title(uri);
-            /* save the current URI in register % */
-            vb_register_add('%', uri);
-
-            /* clear possible set marks */
-            marks_clear();
-
-            break;
-
-        case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
-#ifdef FEATURE_AUTOCMD
-            uri = webkit_web_view_get_uri(view);
-            autocmd_run(AU_LOAD_FIRST_LAYOUT, uri, NULL);
-#endif
-            /* if we load a page from a submitted form, leave the insert mode */
-            if (vb.mode->id == 'i') {
-                vb_enter('n');
-            }
+    cmd[i++] = (char*)uri;
+    cmd[i++] = NULL;
 
-            dom_install_focus_blur_callbacks(webkit_web_frame_get_dom_document(frame));
-            vb.state.done_loading_page = false;
-
-            break;
-
-        case WEBKIT_LOAD_FINISHED:
-            dom_install_focus_blur_callbacks(webkit_web_frame_get_dom_document(frame));
-            uri = webkit_web_view_get_uri(view);
-#ifdef FEATURE_AUTOCMD
-            autocmd_run(AU_LOAD_FINISHED, uri, NULL);
-#endif
-            /* update load progress in statusbar */
-            vb.state.progress = 100;
-            vb_update_statusbar();
-            update_title();
-
-            if (strncmp(uri, "about:", 6)) {
-                history_add(HISTORY_URL, uri, webkit_web_view_get_title(view));
-            }
-            break;
-
-        case WEBKIT_LOAD_FAILED:
-            {
-                /* In case the requested uri could not be loaded the Current
-                 * uri of the Webview would still be the PRevious one. So We
-                 * use the provisional uri here. */
-                WebKitWebDataSource *src  = webkit_web_frame_get_provisional_data_source(frame);
-                if (src) {
-                    WebKitNetworkRequest *req = webkit_web_data_source_get_initial_request(src);
-                    uri = webkit_network_request_get_uri(req);
-                } else {
-                    uri = webkit_web_view_get_uri(view);
-                }
-                set_uri(uri);
-                /* Show the failed uri as title. */
-                set_title(uri);
-#ifdef FEATURE_AUTOCMD
-                autocmd_run(AU_LOAD_FAILED, uri, NULL);
-#endif
-            }
-            break;
-    }
+    /* spawn a new browser instance */
+    g_spawn_async(NULL, cmd, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
 }
 
-static void webview_request_starting_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitWebResource *res, WebKitNetworkRequest *req,
-    WebKitNetworkResponse *resp, gpointer data)
+/**
+ * Callback for the web contexts initialize-web-extensions signal.
+ */
+static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data)
 {
-    char *name, *value;
-    GHashTableIter iter;
-    SoupMessage *msg;
-
-    /* don't try to load favicon */
-    const char *uri = webkit_network_request_get_uri(req);
-    if (g_str_has_suffix(uri, "/favicon.ico")) {
-        webkit_network_request_set_uri(req, "about:blank");
-        return;
-    }
+    char *name;
+    static guint ext_count = 0;
+    GVariant *vdata;
 
-    msg = webkit_network_request_get_message(req);
-    if (!msg) {
-        return;
-    }
+    /* Setup the extension directory. */
+    webkit_web_context_set_web_extensions_directory(webctx, EXTPREFIX);
 
-    if (!vb.config.headers) {
-        return;
-    }
+    name = g_strdup_printf("%u-%u", getpid(), ++ext_count);
+    ext_proxy_init(name);
 
-    /* set/remove/change user defined headers */
-    g_hash_table_iter_init(&iter, vb.config.headers);
-    while (g_hash_table_iter_next(&iter, (gpointer*)&name, (gpointer*)&value)) {
-        /* allow to remove header with null value */
-        if (value == NULL) {
-            soup_message_headers_remove(msg->request_headers, name);
-        } else {
-            soup_message_headers_replace(msg->request_headers, name, value);
-        }
-    }
-}
-
-static gboolean focus_out_event_cb(GtkWidget *widget, GdkEvent *event,
-    gpointer user_data)
-{
-    vb.state.window_has_focus = false;
-    return false;
-}
-
-static gboolean focus_in_event_cb(GtkWidget *widget, GdkEvent *event,
-    gpointer user_data)
-{
-    vb.state.window_has_focus = true;
-    return false;
-}
+    vdata = g_variant_new("(s)", name);
+    webkit_web_context_set_web_extensions_initialization_user_data(webctx, vdata);
 
-static void destroy_window_cb(GtkWidget *widget)
-{
-    vb_quit(true);
+    g_free(name);
 }
 
-static void scroll_cb(GtkAdjustment *adjustment)
+/**
+ * Callback for the webview close signal.
+ */
+static void on_webview_close(WebKitWebView *webview, Client *c)
 {
-    vb_update_statusbar();
+    client_destroy(c);
 }
 
-static gboolean input_focus_in_cb(GtkWidget *widget, GdkEventFocus *event,
-    gpointer data)
+/**
+ * Callback for the webview create signal.
+ * This creates a new client - with it's own window with a related webview.
+ */
+static WebKitWebView *on_webview_create(WebKitWebView *webview,
+        WebKitNavigationAction *navact, Client *c)
 {
-    /* enter the command mode if the focus is on inputbox */
-    vb_enter('c');
-
-    return false;
-}
+    Client *new = client_new(webview);
 
-static WebKitWebView *inspector_new(WebKitWebInspector *inspector, WebKitWebView *webview)
-{
-    return WEBKIT_WEB_VIEW(webkit_web_view_new());
+    return new->webview;
 }
 
-static gboolean inspector_show(WebKitWebInspector *inspector)
-{
-    WebKitWebView *webview;
-    int height;
-
-    if (vb.state.is_inspecting) {
-        return false;
-    }
-
-    webview = webkit_web_inspector_get_web_view(inspector);
-
-    /* use about 1/3 of window height for the inspector */
-    gtk_window_get_size(GTK_WINDOW(vb.gui.window), NULL, &height);
-    gtk_paned_set_position(GTK_PANED(vb.gui.pane), 2 * height / 3);
-
-    gtk_paned_pack2(GTK_PANED(vb.gui.pane), GTK_WIDGET(webview), true, true);
-    gtk_widget_show(GTK_WIDGET(webview));
+/**
+ * Callback for the webview decide-policy signal.
+ * Checks the reasons for some navigation actions and decides if the action is
+ * allowed, or should go into a new instance of vimb.
+ */
+static gboolean on_webview_decide_policy(WebKitWebView *webview,
+        WebKitPolicyDecision *dec, WebKitPolicyDecisionType type, Client *c)
+{
+    guint status;
+    WebKitNavigationAction *a;
+    WebKitURIRequest *req;
+    WebKitURIResponse *res;
+
+    switch (type) {
+        case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
+            a   = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(dec));
+            req = webkit_navigation_action_get_request(a);
+
+            if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED) {
+                if (webkit_navigation_action_get_mouse_button(a) == 2
+                        || (webkit_navigation_action_get_mouse_button(a) == 1
+                        && webkit_navigation_action_get_modifiers(a) & GDK_CONTROL_MASK)) {
+                    webkit_policy_decision_ignore(dec);
+                    spawn_new_instance(webkit_uri_request_get_uri(req), TRUE);
+                    return TRUE;
+                }
+            }
+            return FALSE;
 
-    vb.state.is_inspecting = true;
+        case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
+            a   = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(dec));
+            req = webkit_navigation_action_get_request(a);
 
-    return true;
-}
+            /* Ignore opening new window if this was started without user gesture. */
+            if (!webkit_navigation_action_is_user_gesture(a)) {
+                webkit_policy_decision_ignore(dec);
+                return TRUE;
+            }
 
-static gboolean inspector_close(WebKitWebInspector *inspector)
-{
-    WebKitWebView *webview;
+            if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED) {
+                webkit_policy_decision_ignore(dec);
+                /* This is triggered on link click for links with *
+                 * target="_blank". Maybe it should be configurable if the
+                 * page is opened as tabe or a new instance. */
+                spawn_new_instance(webkit_uri_request_get_uri(req), TRUE);
+                return TRUE;
+            }
+            return FALSE;
 
-    if (!vb.state.is_inspecting) {
-        return false;
-    }
-    webview = webkit_web_inspector_get_web_view(inspector);
-    gtk_widget_hide(GTK_WIDGET(webview));
-    gtk_widget_destroy(GTK_WIDGET(webview));
+        case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
+            req    = webkit_response_policy_decision_get_request(WEBKIT_RESPONSE_POLICY_DECISION(dec));
+            res    = webkit_response_policy_decision_get_response(WEBKIT_RESPONSE_POLICY_DECISION(dec));
+            status = webkit_uri_response_get_status_code(res);
 
-    vb.state.is_inspecting = false;
+            if (!webkit_response_policy_decision_is_mime_type_supported(WEBKIT_RESPONSE_POLICY_DECISION(dec))
+                    && (SOUP_STATUS_IS_SUCCESSFUL(status) || status == SOUP_STATUS_NONE)) {
 
-    return true;
-}
+                webkit_policy_decision_download(dec);
 
-static void inspector_finished(WebKitWebInspector *inspector)
-{
-    g_free(vb.gui.inspector);
-}
+                return TRUE;
+            }
+            return FALSE;
 
-static void set_status(const StatusType status)
-{
-    if (vb.state.status_type != status) {
-        vb.state.status_type = status;
-        /* update the statusbar style only if the status changed */
-        vb_update_status_style();
+        default:
+            return FALSE;
     }
 }
 
-static void init_core(void)
+static void on_webview_load_changed(WebKitWebView *webview,
+        WebKitLoadEvent event, Client *c)
 {
-    Gui *gui = &vb.gui;
-    char *xid;
-
-    if (vb.embed) {
-        gui->window = gtk_plug_new(vb.embed);
-        xid = g_strdup_printf("%u", (int)vb.embed);
-    } else {
-        gui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-        gtk_window_set_role(GTK_WINDOW(gui->window), PROJECT_UCFIRST);
-
-        gtk_widget_realize(GTK_WIDGET(gui->window));
-
-        /* set the x window id to env */
-        xid = g_strdup_printf("%d", (int)GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(gui->window))));
-    }
-
-    g_setenv("VIMB_XID", xid, true);
-    g_free(xid);
-
-    GdkGeometry hints = {10, 10};
-    gtk_window_set_default_size(GTK_WINDOW(gui->window), WIN_WIDTH, WIN_HEIGHT);
-    gtk_window_set_title(GTK_WINDOW(gui->window), PROJECT "/" VERSION);
-    gtk_window_set_geometry_hints(GTK_WINDOW(gui->window), NULL, &hints, GDK_HINT_MIN_SIZE);
-    gtk_window_set_icon(GTK_WINDOW(gui->window), NULL);
-    gtk_widget_set_name(GTK_WIDGET(gui->window), PROJECT);
-
-    /* Create a browser instance */
-    gui->webview = WEBKIT_WEB_VIEW(webkit_web_view_new());
-    gui->inspector = webkit_web_view_get_inspector(gui->webview);
-
-    /* Create a scrollable area */
-    GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
-    gui->adjust_h = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll));
-    gui->adjust_v = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll));
-
-#ifdef FEATURE_NO_SCROLLBARS
-#ifdef HAS_GTK3
-    /* set the default style for the application - this can be overwritten by
-     * the users style in gtk-3.0/gtk.css */
-    const char *style = "GtkScrollbar{-GtkRange-slider-width:0;-GtkRange-trough-border:0;}\
-                        GtkScrolledWindow{-GtkScrolledWindow-scrollbar-spacing:0;}";
-    GtkCssProvider *provider = gtk_css_provider_get_default();
-    gtk_css_provider_load_from_data(provider, style, -1, NULL);
-    gtk_style_context_add_provider_for_screen(
-        gdk_screen_get_default(),
-        GTK_STYLE_PROVIDER(provider),
-        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
-    );
-#else /* no GTK3 */
-    /* GTK_POLICY_NEVER with gtk3 disallows window resizing and scrolling */
-    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
-#endif
-#endif
-
-    /* Prepare the command line */
-    gui->input  = gtk_text_view_new();
-    gui->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui->input));
-
-#ifdef HAS_GTK3
-    gui->pane            = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
-    gui->box             = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
-    gui->statusbar.box   = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
-#else
-    gui->pane            = gtk_vpaned_new();
-    gui->box             = GTK_BOX(gtk_vbox_new(false, 0));
-    gui->statusbar.box   = GTK_BOX(gtk_hbox_new(false, 0));
-#endif
-    gui->statusbar.mode  = gtk_label_new(NULL);
-    gui->statusbar.left  = gtk_label_new(NULL);
-    gui->statusbar.right = gtk_label_new(NULL);
-    gui->statusbar.cmd   = gtk_label_new(NULL);
-
-    /* Prepare the event box */
-    gui->eventbox = gtk_event_box_new();
-
-    gtk_paned_pack1(GTK_PANED(gui->pane), GTK_WIDGET(gui->box), true, true);
-
-    /* Put all part together */
-    gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(gui->webview));
-    gtk_container_add(GTK_CONTAINER(gui->eventbox), GTK_WIDGET(gui->statusbar.box));
-    gtk_container_add(GTK_CONTAINER(gui->window), GTK_WIDGET(gui->pane));
-#ifdef HAS_GTK3
-    gtk_widget_set_halign(gui->statusbar.mode, GTK_ALIGN_START);
-    gtk_widget_set_halign(gui->statusbar.left, GTK_ALIGN_START);
-#else
-    gtk_misc_set_alignment(GTK_MISC(gui->statusbar.mode), 0.0, 0.0);
-    gtk_misc_set_alignment(GTK_MISC(gui->statusbar.left), 0.0, 0.0);
-#endif
-    gtk_label_set_ellipsize(GTK_LABEL(gui->statusbar.left), PANGO_ELLIPSIZE_MIDDLE);
-    gtk_box_pack_start(gui->statusbar.box, gui->statusbar.mode, false, true, 0);
-    gtk_box_pack_start(gui->statusbar.box, gui->statusbar.left, true, true, 2);
-    gtk_box_pack_start(gui->statusbar.box, gui->statusbar.cmd, false, false, 0);
-    gtk_box_pack_start(gui->statusbar.box, gui->statusbar.right, false, false, 2);
-
-    gtk_box_pack_start(gui->box, scroll, true, true, 0);
-    gtk_box_pack_start(gui->box, gui->eventbox, false, false, 0);
-
-#ifdef HAS_GTK3
-    /* use a scrolled window to hide overflowing text in inputbox like GTK2 */
-    GtkWidget *inputscroll = gtk_scrolled_window_new(NULL, NULL);
-    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(inputscroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
-    gtk_container_add(GTK_CONTAINER(inputscroll), gui->input);
-
-    gtk_box_pack_end(gui->box, inputscroll, false, false, 0);
-#else
-    gtk_box_pack_end(gui->box, gui->input, false, false, 0);
-#endif
-
-    /* initialize the modes */
-    vb_add_mode('n', normal_enter, normal_leave, normal_keypress, NULL);
-    vb_add_mode('c', ex_enter, ex_leave, ex_keypress, ex_input_changed);
-    vb_add_mode('i', input_enter, input_leave, input_keypress, NULL);
-    vb_add_mode('p', pass_enter, pass_leave, pass_keypress, NULL);
-
-    /* initialize the marks with empty values */
-    marks_clear();
-
-    init_files();
-    session_init();
-    setting_init();
-    register_init();
-#ifdef FEATURE_AUTOCMD
-    autocmd_init();
-#endif
-    map_init();
-
-    setup_signals();
-
-    /* enter normal mode */
-    vb_enter('n');
-
-    /* make sure the main window and all its contents are visible */
-    gtk_widget_show_all(gui->window);
-
-    /* read the config file */
-    ex_run_file(vb.files[FILES_CONFIG]);
-
-    /* initially apply input style */
-    vb_update_input_style();
-
-    if (vb.config.kioskmode) {
-        WebKitWebSettings *setting = webkit_web_view_get_settings(gui->webview);
-
-        /* hide input box - to not create it would be better, but this needs a
-         * lot of changes in the code where the input is used */
-        gtk_widget_hide(vb.gui.input);
-
-        /* disable context menu */
-        g_object_set(G_OBJECT(setting), "enable-default-context-menu", false, NULL);
-    }
-
-    vb.config.default_zoom = 1.0;
-
-#ifdef FEATURE_HIGH_DPI
-    /* fix for high dpi displays */
-    GdkScreen *screen = gdk_window_get_screen(gtk_widget_get_window(vb.gui.window));
-    gdouble dpi = gdk_screen_get_resolution(screen);
-    if (dpi != -1) {
-        WebKitWebSettings *setting = webkit_web_view_get_settings(gui->webview);
-        webkit_web_view_set_full_content_zoom(gui->webview, true);
-        g_object_set(G_OBJECT(setting), "enforce-96-dpi", true, NULL);
-
-        /* calculate the zoom level based on 96 dpi */
-        vb.config.default_zoom = dpi/96;
+    GTlsCertificateFlags tlsflags;
+    const char *uri;
 
-        webkit_web_view_set_zoom_level(gui->webview, vb.config.default_zoom);
-    }
-#endif
-}
+    switch (event) {
+        case WEBKIT_LOAD_STARTED:
+            /* update load progress in statusbar */
+            c->state.progress = 0;
+            vb_statusbar_update(c);
+            set_title(c, webkit_web_view_get_uri(webview));
+            break;
 
-static void marks_clear(void)
-{
-    int i;
+        case WEBKIT_LOAD_REDIRECTED:
+            break;
 
-    /* init empty marks array */
-    for (i = 0; i < VB_MARK_SIZE; i++) {
-        vb.state.marks[i] = -1;
-    }
-}
+        case WEBKIT_LOAD_COMMITTED:
+            uri = webkit_web_view_get_uri(webview);
+            /* save the current URI in register % */
+            vb_register_add(c, '%', uri);
+            /* check if tls is on and the page is trusted */
+            if (g_str_has_prefix(uri, "https://")) {
+                if (webkit_web_view_get_tls_info(webview, NULL, &tlsflags) && tlsflags) {
+                    set_statusbar_style(c, STATUS_SSL_INVALID);
+                } else {
+                    set_statusbar_style(c, STATUS_SSL_VALID);
+                }
+            } else {
+                set_statusbar_style(c, STATUS_NORMAL);
+            }
 
-static void setup_signals()
-{
-    /* Set up callbacks so that if either the main window or the browser
-     * instance is closed, the program will exit */
-    g_signal_connect(vb.gui.window, "destroy", G_CALLBACK(destroy_window_cb), NULL);
-    g_object_connect(
-        G_OBJECT(vb.gui.webview),
-#if WEBKIT_CHECK_VERSION(1, 10, 0)
-        "signal::context-menu", G_CALLBACK(context_menu_cb), NULL,
-#else
-        "signal::populate-popup", G_CALLBACK(context_menu_cb), NULL,
-#endif
-        "signal::notify::uri", G_CALLBACK(uri_change_cb), NULL,
-        "signal::notify::progress", G_CALLBACK(webview_progress_cb), NULL,
-        "signal::notify::load-status", G_CALLBACK(webview_load_status_cb), NULL,
-        "signal::button-release-event", G_CALLBACK(button_relase_cb), NULL,
-        "signal::new-window-policy-decision-requested", G_CALLBACK(new_window_policy_cb), NULL,
-        "signal::create-web-view", G_CALLBACK(create_web_view_cb), NULL,
-        "signal::hovering-over-link", G_CALLBACK(hover_link_cb), NULL,
-        "signal::title-changed", G_CALLBACK(title_changed_cb), NULL,
-        "signal::mime-type-policy-decision-requested", G_CALLBACK(mimetype_decision_cb), NULL,
-        "signal::download-requested", G_CALLBACK(vb_download), NULL,
-        "signal::should-show-delete-interface-for-element", G_CALLBACK(gtk_false), NULL,
-        "signal::resource-request-starting", G_CALLBACK(webview_request_starting_cb), NULL,
-        "signal::navigation-policy-decision-requested", G_CALLBACK(navigation_decision_requested_cb), NULL,
-        "signal::onload-event", G_CALLBACK(onload_event_cb), NULL,
-        NULL
-    );
-    g_signal_connect(vb.gui.window, "focus-in-event", G_CALLBACK(focus_in_event_cb), NULL);
-    g_signal_connect(vb.gui.window, "focus-out-event", G_CALLBACK(focus_out_event_cb), NULL);
-#ifdef FEATURE_ARH
-    g_signal_connect(vb.session, "request-queued", G_CALLBACK(session_request_queued_cb), NULL);
-#endif
+            /* clear possible set marks */
+            marks_clear(c);
 
-#ifdef FEATURE_NO_SCROLLBARS
-    WebKitWebFrame *frame = webkit_web_view_get_main_frame(vb.gui.webview);
-    g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed", G_CALLBACK(gtk_true), NULL);
-#endif
+            break;
 
-    if (!vb.config.kioskmode) {
-        g_signal_connect(
-            G_OBJECT(vb.gui.window), "key-press-event", G_CALLBACK(map_keypress), NULL
-        );
-        g_signal_connect(
-            G_OBJECT(vb.gui.input), "focus-in-event", G_CALLBACK(input_focus_in_cb), NULL
-        );
-
-        /* inspector */
-        g_object_connect(
-            G_OBJECT(vb.gui.inspector),
-            "signal::inspect-web-view", G_CALLBACK(inspector_new), NULL,
-            "signal::show-window", G_CALLBACK(inspector_show), NULL,
-            "signal::close-window", G_CALLBACK(inspector_close), NULL,
-            "signal::finished", G_CALLBACK(inspector_finished), NULL,
-            NULL
-        );
+        case WEBKIT_LOAD_FINISHED:
+            c->state.progress = 100;
+            break;
     }
-    /* There is no inputbox in kioskmode - but the contents may be changed in
-     * case vimb is controlled via socket. To track inputbox changes is
-     * required for the hinting to work. */
-    g_signal_connect(G_OBJECT(vb.gui.buffer), "changed", G_CALLBACK(buffer_changed_cb), NULL);
-
-    /* webview adjustment */
-    g_signal_connect(G_OBJECT(vb.gui.adjust_v), "value-changed", G_CALLBACK(scroll_cb), NULL);
 }
 
-static void init_files(void)
+/**
+ * Callback for the webview mouse-target-changed signal.
+ * This is used to print the uri too statusbar if the user hovers over links
+ * or images.
+ */
+static void on_webview_mouse_target_changed(WebKitWebView *webview,
+        WebKitHitTestResult *result, guint modifiers, Client *c)
 {
-    char *path = util_get_config_dir(vb.config.profile);
+    char *msg;
+    const char *uri;
 
-    if (vb.config.file) {
-        char *rp = realpath(vb.config.file, NULL);
-        vb.files[FILES_CONFIG] = g_strdup(rp);
-        free(rp);
+    /* Save the hitTestResult to have this later available for events that
+     * don't support this. */
+    if (c->state.hit_test_result) {
+        g_object_unref(c->state.hit_test_result);
+    }
+    c->state.hit_test_result = g_object_ref(result);
+
+    if (webkit_hit_test_result_context_is_link(result)) {
+        uri = webkit_hit_test_result_get_link_uri(result);
+        msg = g_strconcat("Link: ", uri, NULL);
+        gtk_label_set_text(GTK_LABEL(c->statusbar.left), msg);
+        g_free(msg);
+    } else if (webkit_hit_test_result_context_is_image(result)) {
+        uri = webkit_hit_test_result_get_image_uri(result);
+        msg = g_strconcat("Image: ", uri, NULL);
+        gtk_label_set_text(GTK_LABEL(c->statusbar.left), msg);
+        g_free(msg);
     } else {
-        vb.files[FILES_CONFIG] = g_build_filename(path, "config", NULL);
-        util_create_file_if_not_exists(vb.files[FILES_CONFIG]);
+        /* No link under cursor - show the current URI. */
+        update_urlbar(c);
     }
-
-#ifdef FEATURE_COOKIE
-    vb.files[FILES_COOKIE] = g_build_filename(path, "cookies", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_COOKIE]);
-#endif
-
-    vb.files[FILES_CLOSED] = g_build_filename(path, "closed", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_CLOSED]);
-
-    vb.files[FILES_HISTORY] = g_build_filename(path, "history", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_HISTORY]);
-
-    vb.files[FILES_COMMAND] = g_build_filename(path, "command", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_COMMAND]);
-
-    vb.files[FILES_SEARCH] = g_build_filename(path, "search", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_SEARCH]);
-
-    vb.files[FILES_BOOKMARK] = g_build_filename(path, "bookmark", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_BOOKMARK]);
-
-#ifdef FEATURE_QUEUE
-    vb.files[FILES_QUEUE] = g_build_filename(path, "queue", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_QUEUE]);
-#endif
-#ifdef FEATURE_HSTS
-    vb.files[FILES_HSTS] = g_build_filename(path, "hsts", NULL);
-    util_create_file_if_not_exists(vb.files[FILES_HSTS]);
-#endif
-
-    vb.files[FILES_SCRIPT] = g_build_filename(path, "scripts.js", NULL);
-
-    vb.files[FILES_USER_STYLE] = g_build_filename(path, "style.css", NULL);
-
-    g_free(path);
 }
 
-static void session_init(void)
-{
-    /* init soup session */
-    vb.session = webkit_get_default_session();
-    g_object_set(vb.session, "max-conns", SETTING_MAX_CONNS , NULL);
-    g_object_set(vb.session, "max-conns-per-host", SETTING_MAX_CONNS_PER_HOST, NULL);
-    g_object_set(vb.session, "accept-language-auto", true, NULL);
-
-#ifdef FEATURE_COOKIE
-    SoupCookieJar *cookie = cookiejar_new(vb.files[FILES_COOKIE], false);
-    soup_session_add_feature(vb.session, SOUP_SESSION_FEATURE(cookie));
-    g_object_unref(cookie);
-#endif
-#ifdef FEATURE_HSTS
-    /* create only the session feature - the feature is added in setting.c
-     * when the setting hsts=on */
-    vb.config.hsts_provider = hsts_provider_new();
-#endif
-#ifdef FEATURE_SOUP_CACHE
-    /* setup the soup cache but without setting the cache size - this is done in setting.c */
-    char *cache_dir      = util_get_cache_dir(vb.config.profile);
-    vb.config.soup_cache = soup_cache_new(cache_dir, SOUP_CACHE_SINGLE_USER);
-    soup_session_add_feature(vb.session, SOUP_SESSION_FEATURE(vb.config.soup_cache));
-    soup_cache_load(vb.config.soup_cache);
-    g_free(cache_dir);
-#endif
-}
-
-static void session_cleanup(void)
-{
-#ifdef FEATURE_HSTS
-    /* remove feature from session and unref the feature to make sure the
-     * feature is finalized */
-    g_object_unref(vb.config.hsts_provider);
-    soup_session_remove_feature_by_type(vb.session, HSTS_TYPE_PROVIDER);
-#endif
-#ifdef FEATURE_SOUP_CACHE
-    /* commit all cache writes */
-    soup_cache_flush(vb.config.soup_cache);
-    /* make sure that the cache will be kept on next browser start */
-    soup_cache_dump(vb.config.soup_cache);
-#endif
-}
-
-static void register_init(void)
+/**
+ * Called on webviews notify::estimated-load-progress event. This writes the
+ * esitamted load progress in percent in a variable and updates the statusbar
+ * to make the changes visible.
+ */
+static void on_webview_notify_estimated_load_progress(WebKitWebView *webview,
+        GParamSpec *spec, Client *c)
 {
-    memset(vb.state.reg, 0, sizeof(char*));
+    c->state.progress = webkit_web_view_get_estimated_load_progress(webview) * 100;
+    vb_statusbar_update(c);
 }
 
-void vb_register_add(char buf, const char *value)
-{
-    char *mark;
-    int idx;
-
-    if (!vb.state.enable_register || !buf) {
-        return;
-    }
-
-    /* make sure the mark is a valid mark char */
-    if ((mark = strchr(VB_REG_CHARS, buf))) {
-        /* get the index of the mark char */
-        idx = mark - VB_REG_CHARS;
-
-        OVERWRITE_STRING(vb.state.reg[idx], value);
-    }
-}
-
-const char *vb_register_get(char buf)
+/**
+ * Callback for the webview notify::title signal.
+ * Changes the window title according to the title of the current page.
+ */
+static void on_webview_notify_title(WebKitWebView *webview, GParamSpec *pspec, Client *c)
 {
-    char *mark;
-    int idx;
-
-    /* make sure the mark is a valid mark char */
-    if ((mark = strchr(VB_REG_CHARS, buf))) {
-        /* get the index of the mark char */
-        idx = mark - VB_REG_CHARS;
-
-        return vb.state.reg[idx];
-    }
-
-    return NULL;
+    set_title(c, webkit_web_view_get_title(webview));
 }
 
-static void register_cleanup(void)
+/**
+ * Callback for the webview notify::uri signal.
+ * Changes the current uri shown on left of statusbar.
+ */
+static void on_webview_notify_uri(WebKitWebView *webview, GParamSpec *pspec, Client *c)
 {
-    int i;
-    for (i = 0; i < VB_REG_SIZE; i++) {
-        if (vb.state.reg[i]) {
-            g_free(vb.state.reg[i]);
-        }
-    }
+    OVERWRITE_STRING(c->state.uri, webkit_web_view_get_uri(c->webview));
+    update_urlbar(c);
+    g_setenv("VIMB_URI", c->state.uri, TRUE);
 }
 
-static gboolean button_relase_cb(WebKitWebView *webview, GdkEventButton *event)
+/**
+ * Callback for the webview ready-to-show signal.
+ * Show the webview only if it's ready to be shown.
+ */
+static void on_webview_ready_to_show(WebKitWebView *webview, Client *c)
 {
-    /* let webkit handle the click - for example on a link */
-    gboolean nopropagate = false;
-    WebKitHitTestResultContext context;
-
-    WebKitHitTestResult *result = webkit_web_view_get_hit_test_result(webview, event);
-
-    g_object_get(result, "context", &context, NULL);
-    /* ctrl click or middle mouse click onto link */
-    if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK
-        && (event->button == 2 || (event->button == 1 && event->state & GDK_CONTROL_MASK))
-    ) {
-        Arg a = {VB_TARGET_NEW};
-        g_object_get(result, "link-uri", &a.s, NULL);
-        vb_load_uri(&a);
-
-        nopropagate = true;
-    }
-    g_object_unref(result);
-
-    return nopropagate;
+    gtk_widget_show(GTK_WIDGET(webview));
 }
 
-static gboolean new_window_policy_cb(
-    WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *navig, WebKitWebPolicyDecision *policy)
+/**
+ * Callback for the webview web-process-crashed signal.
+ */
+static gboolean on_webview_web_process_crashed(WebKitWebView *webview, Client *c)
 {
-    if (webkit_web_navigation_action_get_reason(navig) == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
-        webkit_web_policy_decision_ignore(policy);
-        /* open in a new window */
-        Arg a = {VB_TARGET_NEW, (char*)webkit_network_request_get_uri(request)};
-        vb_load_uri(&a);
-        return true;
-    }
-    return false;
+    g_warning("Webview Crashed on %s", webkit_web_view_get_uri(webview));
+    return TRUE;
 }
 
-static WebKitWebView *create_web_view_cb(WebKitWebView *view, WebKitWebFrame *frame)
+/**
+ * Callback for the window destroy signal.
+ * Destroys the client that is associated to the window.
+ */
+static void on_window_destroy(GtkWidget *window, Client *c)
 {
-    WebKitWebView *new = WEBKIT_WEB_VIEW(webkit_web_view_new());
-
-    /* wait until the new webview receives its new URI */
-    g_signal_connect(new, "navigation-policy-decision-requested", G_CALLBACK(create_web_view_received_uri_cb), NULL);
-
-    return new;
+    client_destroy(c);
 }
 
-static gboolean create_web_view_received_uri_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *action, WebKitWebPolicyDecision *policy,
-    gpointer data)
+/**
+ * Callback for to quit given client as idle event source.
+ */
+static gboolean quit(Client *c)
 {
-    Arg a = {VB_TARGET_NEW, (char*)webkit_network_request_get_uri(request)};
-    vb_load_uri(&a);
-
-    /* destroy temporary webview */
-    gtk_widget_destroy(GTK_WIDGET(view));
+    /* Destroy the main window to tirgger the destruction of the client. */
+    gtk_widget_destroy(c->window);
 
-    /* mark that we handled the signal */
-    return true;
+    /* Remove this from the list of event sources. */
+    return FALSE;
 }
 
-static gboolean navigation_decision_requested_cb(WebKitWebView *view,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request,
-    WebKitWebNavigationAction *action, WebKitWebPolicyDecision *policy,
-    gpointer data)
+/**
+ * Free the register contents memory.
+ */
+static void register_cleanup(Client *c)
 {
-#ifdef FEATURE_HSTS
-    char *uri;
-    SoupMessage *msg = webkit_network_request_get_message(request);
-
-    /* manually reload the page for HSTS only when it occurs in
-     * the main-frame. the others cases are covered by requeueing. */
-    if (webkit_web_view_get_main_frame(view) == frame) {
-        uri = hsts_get_changed_uri(vb.session, msg);
-        if (uri) {
-            webkit_web_frame_load_uri(frame, uri);
-            webkit_web_policy_decision_ignore(policy);
-
-            g_free(uri);
-            /* mark the request as handled */
-            return true;
+    int i;
+    for (i = 0; i < REG_SIZE; i++) {
+        if (c->state.reg[i]) {
+            g_free(c->state.reg[i]);
         }
     }
-#endif
-
-    /* try to find a protocol handler to open the uri */
-    if (handle_uri(webkit_network_request_get_uri(request))) {
-        webkit_web_policy_decision_ignore(policy);
-
-        return true;
-    }
-    return false;
 }
 
-static void onload_event_cb(WebKitWebView *view, WebKitWebFrame *frame,
-    gpointer user_data)
+/**
+ * Update the contents of the url bar on the left of the statu bar according
+ * to current opened url and position in back forward history.
+ */
+static void update_urlbar(Client *c)
 {
-    Document *doc = webkit_web_frame_get_dom_document(frame);
-    dom_check_auto_insert(doc);
-    vb.state.done_loading_page = true;
-}
+#if !defined(FEATURE_HISTORY_INDICATOR)
+    /* if only the uri is shown - write it like it is on the label */
+    gtk_label_set_text(GTK_LABEL(c->statusbar.left), c->state.uri);
+#else
+    GString *str = g_string_new(c->state.uri);
 
-static void hover_link_cb(WebKitWebView *webview, const char *title, const char *link)
-{
-    char *message;
-    if (link) {
-        /* save the uri to have this if the user want's to copy the link
-         * location via context menu */
-        OVERWRITE_STRING(vb.state.linkhover, link);
-
-        message = g_strconcat("Link: ", link, NULL);
-        gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.left), message);
-        g_free(message);
-    } else if (vb.state.uri) {
-        /* Use previous url in case of hover out of a link. */
-        vb_update_urlbar(vb.state.uri);
-    } else {
-        /* If there is no previous uri use the current uri from webview. */
-        set_uri(webkit_web_view_get_uri(webview));
-    }
-}
+#ifdef FEATURE_HISTORY_INDICATOR
+    gboolean back, fwd;
 
-static void title_changed_cb(WebKitWebView *webview, WebKitWebFrame *frame, const char *title)
-{
-    set_title(title);
-}
+    back = webkit_web_view_can_go_back(c->webview);
+    fwd  = webkit_web_view_can_go_forward(c->webview);
 
-static void update_title(void)
-{
-#ifdef FEATURE_TITLE_PROGRESS
-    /* show load status of page or the downloads */
-    if (vb.state.progress != 100) {
-        char *title = g_strdup_printf(
-            "[%i%%] %s",
-            vb.state.progress,
-            vb.state.title ? vb.state.title : ""
-        );
-        gtk_window_set_title(GTK_WINDOW(vb.gui.window), title);
-        g_free(title);
-        return;
-    }
-#endif
-    if (vb.state.title) {
-        gtk_window_set_title(GTK_WINDOW(vb.gui.window), vb.state.title);
+    /* show history indicator only if there is something to show */
+    if (back || fwd) {
+        g_string_append_printf(str, " [%s]", back ? (fwd ? "-+" : "-") : "+");
     }
-}
-
-static void set_uri(const char *uri)
-{
-    OVERWRITE_STRING(vb.state.uri, uri);
-    g_setenv("VIMB_URI", uri, true);
-    vb_update_urlbar(uri);
-}
+#endif /* FEATURE_HISTORY_INDICATOR */
 
-static void set_title(const char *title)
-{
-    OVERWRITE_STRING(vb.state.title, title);
-    update_title();
-    g_setenv("VIMB_TITLE", title ? title : "", true);
+    gtk_label_set_text(GTK_LABEL(c->statusbar.left), str->str);
+    g_string_free(str, TRUE);
+#endif /* !defined(FEATURE_HISTORY_INDICATOR) */
 }
 
-static gboolean mimetype_decision_cb(WebKitWebView *webview,
-    WebKitWebFrame *frame, WebKitNetworkRequest *request, char *mime_type,
-    WebKitWebPolicyDecision *decision)
+#ifdef FREE_ON_QUIT
+/**
+ * Free memory of the whole application.
+ */
+static void vimb_cleanup(void)
 {
-    SoupMessage *msg;
-    /* don't start download if request failed or stopped by proxy or can be
-     * displayed in the webview */
-    if (!mime_type || *mime_type == '\0'
-        || webkit_web_view_can_show_mime_type(webview, mime_type)) {
+    int i;
 
-        return false;
+    while (vb.clients) {
+        client_destroy(vb.clients);
     }
 
-    /* Don't start a download when the response has no 2xx status code. Or the
-     * message was not sent before - this seems to be the case when the server
-     * responds with a Accept-Ranges header. */
-    msg = webkit_network_request_get_message(request);
-    if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)
-        || msg->status_code == SOUP_STATUS_NONE
-    ) {
-        webkit_web_policy_decision_download(decision);
-        return true;
-    }
-    return false;
-}
+    /* free memory of other components */
+    util_cleanup();
 
-gboolean vb_download(WebKitWebView *view, WebKitDownload *download, const char *path)
-{
-    char *file, *dir;
-    const char *download_cmd = GET_CHAR("download-command");
-    gboolean use_external    = GET_BOOL("download-use-external");
-
-    /* prepare the path to save the download */
-    if (path) {
-        file = util_build_path(path, vb.config.download_dir);
-
-        /* if file is an directory append a file name */
-        if (g_file_test(file, (G_FILE_TEST_IS_DIR))) {
-            dir  = file;
-            file = g_build_filename(dir, PROJECT "-download", NULL);
-            g_free(dir);
-        }
-    } else {
-        /* if there was no path given where to download the file, used
-         * suggested file name or a static one */
-        path = webkit_download_get_suggested_filename(download);
-        if (!path || *path == '\0') {
-            path = PROJECT "-download";
+    for (i = 0; i < FILES_LAST; i++) {
+        if (vb.files[i]) {
+            g_free(vb.files[i]);
         }
-        file = util_build_path(path, vb.config.download_dir);
-    }
-
-#ifdef FEATURE_AUTOCMD
-    autocmd_run(AU_DOWNLOAD_START, webkit_download_get_uri(download), NULL);
-#endif
-    if (use_external && *download_cmd) {
-        /* run download with external program */
-        vb_download_external(view, download, file);
-        g_free(file);
-
-        /* signalize that we handle the download ourself */
-        return false;
-    } else {
-        /* use webkit download helpr to download the uri */
-        vb_download_internal(view, download, file);
-        g_free(file);
-
-        return true;
     }
 }
-
-void vb_download_internal(WebKitWebView *view, WebKitDownload *download, const char *file)
-{
-    char *uri;
-    guint64 size;
-    WebKitDownloadStatus status;
-
-    /* build the file uri from file path */
-    uri = g_filename_to_uri(file, NULL, NULL);
-    webkit_download_set_destination_uri(download, uri);
-    g_free(uri);
-
-    size = webkit_download_get_total_size(download);
-    if (size > 0) {
-        vb_echo(VB_MSG_NORMAL, false, "Download %s [%uB] started ...", file, size);
-    } else {
-        vb_echo(VB_MSG_NORMAL, false, "Download %s started ...", file);
-    }
-
-    status = webkit_download_get_status(download);
-    if (status == WEBKIT_DOWNLOAD_STATUS_CREATED) {
-        webkit_download_start(download);
-    }
-
-    /* prepend the download to the download list */
-    vb.state.downloads = g_list_prepend(vb.state.downloads, download);
-
-    /* connect signal handler to check if the download is done */
-    g_signal_connect(download, "notify::status", G_CALLBACK(download_progress_cp), NULL);
-    g_signal_connect(download, "notify::progress", G_CALLBACK(webview_download_progress_cb), NULL);
-
-    vb_update_statusbar();
-}
-
-void vb_download_external(WebKitWebView *view, WebKitDownload *download, const char *file)
-{
-    const char *user_agent = NULL, *mimetype = NULL, *download_cmd;
-    char **argv, **envp;
-    char *cmd;
-    int argc;
-    guint64 size;
-    SoupMessage *msg;
-    WebKitNetworkRequest *request;
-    GError *error = NULL;
-
-    request = webkit_download_get_network_request(download);
-    msg     = webkit_network_request_get_message(request);
-    /* if the download is started by the :save command or hinting we get no
-     * message here */
-    if (msg) {
-        user_agent = soup_message_headers_get_one(msg->request_headers, "User-Agent");
-        mimetype   = soup_message_headers_get_one(msg->request_headers, "Content-Type");
-    }
-
-    /* set the required download information as environment */
-    envp = g_get_environ();
-    envp = g_environ_setenv(envp, "VIMB_FILE", file, true);
-    envp = g_environ_setenv(envp, "VIMB_USE_PROXY", GET_BOOL("proxy") ? "1" : "0", true);
-#ifdef FEATURE_COOKIE
-    envp = g_environ_setenv(envp, "VIMB_COOKIES", vb.files[FILES_COOKIE], true);
 #endif
-    if (mimetype) {
-        envp = g_environ_setenv(envp, "VIMB_MIME_TYPE", mimetype, true);
-    }
-
-    if (!user_agent) {
-        WebKitWebSettings *setting = webkit_web_view_get_settings(view);
-        g_object_get(G_OBJECT(setting), "user-agent", &user_agent, NULL);
-    }
-    envp         = g_environ_setenv(envp, "VIMB_USER_AGENT", user_agent, true);
-    download_cmd = GET_CHAR("download-command");
-    cmd          = g_strdup_printf(download_cmd, webkit_download_get_uri(download));
-
-    if (!g_shell_parse_argv(cmd, &argc, &argv, &error)) {
-        g_warning("Could not parse download-command '%s': %s", download_cmd, error->message);
-        g_error_free(error);
-        g_free(cmd);
-
-        return;
-    }
-    g_free(cmd);
-
-    if (g_spawn_async(NULL, argv, envp, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) {
-        size = webkit_download_get_total_size(download);
-        if (size > 0) {
-            vb_echo(VB_MSG_NORMAL, true, "Download of %uB started", size);
-        } else {
-            vb_echo(VB_MSG_NORMAL, true, "Download started");
-        }
-    } else {
-        g_warning("%s", error->message);
-        g_clear_error(&error);
-        vb_echo(VB_MSG_ERROR, true, "Could not start download");
-    }
-    g_strfreev(argv);
-    g_strfreev(envp);
-}
 
-static void download_progress_cp(WebKitDownload *download, GParamSpec *pspec)
+/**
+ * Setup resources used on application scope.
+ */
+static void vimb_setup(void)
 {
-    WebKitDownloadStatus status = webkit_download_get_status(download);
+    WebKitWebContext *ctx;
+    WebKitCookieManager *cm;
+    char *path;
 
-    if (status == WEBKIT_DOWNLOAD_STATUS_STARTED || status == WEBKIT_DOWNLOAD_STATUS_CREATED) {
-        return;
-    }
+    /* prepare the file pathes */
+    path = util_get_config_dir();
 
-    const char *file = webkit_download_get_destination_uri(download);
-    /* skip the file protocol for the display */
-    if (!strncmp(file, "file://", 7)) {
-        file += 7;
-    }
-    if (status != WEBKIT_DOWNLOAD_STATUS_FINISHED) {
-#ifdef FEATURE_AUTOCMD
-        autocmd_run(AU_DOWNLOAD_FAILED, webkit_download_get_uri(download), NULL);
-#endif
-        vb_echo(VB_MSG_ERROR, false, "Error downloading %s", file);
+    if (vb.configfile) {
+        char *rp = realpath(vb.configfile, NULL);
+        vb.files[FILES_CONFIG] = g_strdup(rp);
+        free(rp);
     } else {
-#ifdef FEATURE_AUTOCMD
-        autocmd_run(AU_DOWNLOAD_FINISHED, webkit_download_get_uri(download), NULL);
-#endif
-        vb_echo(VB_MSG_NORMAL, false, "Download %s finished", file);
-    }
+        vb.files[FILES_CONFIG] = util_get_filepath(path, "config", FALSE);
+    }
+
+    /* Setup those files that are use multiple time during runtime */
+    vb.files[FILES_CLOSED]     = util_get_filepath(path, "closed", FALSE);
+    vb.files[FILES_COOKIE]     = util_get_filepath(path, "cookies", FALSE);
+    vb.files[FILES_USER_STYLE] = util_get_filepath(path, "style.css", FALSE);
+    vb.files[FILES_SCRIPT]     = util_get_filepath(path, "scripts.js", FALSE);
+    vb.files[FILES_HISTORY]    = util_get_filepath(path, "history", FALSE);
+    vb.files[FILES_COMMAND]    = util_get_filepath(path, "command", FALSE);
+    vb.files[FILES_HSTS]       = util_get_filepath(path, "hsts", FALSE);
+    vb.files[FILES_BOOKMARK]   = util_get_filepath(path, "bookmark", FALSE);
+    vb.files[FILES_QUEUE]      = util_get_filepath(path, "queue", FALSE);
+    vb.files[FILES_SEARCH]     = util_get_filepath(path, "search", FALSE);
+    g_free(path);
 
-    /* remove the download from the list */
-    vb.state.downloads = g_list_remove(vb.state.downloads, download);
+    /* Use seperate rendering processed for the webview of the clients in the
+     * current instance. This must be called as soon as possible according to
+     * the documentation. */
+    ctx = webkit_web_context_get_default();
+    webkit_web_context_set_process_model(ctx, WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
+    webkit_web_context_set_cache_model(ctx, WEBKIT_CACHE_MODEL_WEB_BROWSER);
 
-    vb_update_statusbar();
-}
+    g_signal_connect(ctx, "initialize-web-extensions", G_CALLBACK(on_webctx_init_web_extension), NULL);
 
-static void read_from_stdin(void)
-{
-    /* read content from stdin */
-    GIOChannel *ch = g_io_channel_unix_new(fileno(stdin));
-    gchar *buf     = NULL;
-    GError *err    = NULL;
-    gsize len;
-
-    g_io_channel_read_to_end(ch, &buf, &len, &err);
-    g_io_channel_unref(ch);
-    if (err) {
-        g_warning("Error loading from stdin: %s", err->message);
-        g_error_free(err);
-    } else {
-        webkit_web_view_load_string(vb.gui.webview, buf, "text/html", NULL, "(stdin)");
+    /* Add cookie support only if the cookie file exists. */
+    if (vb.files[FILES_COOKIE]) {
+        cm = webkit_web_context_get_cookie_manager(ctx);
+        webkit_cookie_manager_set_persistent_storage(
+                cm,
+                vb.files[FILES_COOKIE],
+                WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT);
     }
-    g_free(buf);
-}
-
-#ifdef FEATURE_ARH
-static void session_request_queued_cb(SoupSession *session, SoupMessage *msg, gpointer data)
-{
-    SoupURI *suri = soup_message_get_uri(msg);
-    char     *uri = soup_uri_to_string(suri, false);
 
-    arh_run(vb.config.autoresponseheader, uri, msg);
+    /* TODO move to settings.c */
+    webkit_web_context_set_tls_errors_policy(ctx, TRUE ? WEBKIT_TLS_ERRORS_POLICY_FAIL : WEBKIT_TLS_ERRORS_POLICY_IGNORE);
 
-    g_free(uri);
+    /* initialize the modes */
+    vb_mode_add('n', normal_enter, normal_leave, normal_keypress, NULL);
+    vb_mode_add('c', ex_enter, ex_leave, ex_keypress, ex_input_changed);
+    vb_mode_add('i', input_enter, input_leave, input_keypress, NULL);
+    vb_mode_add('p', pass_enter, pass_leave, pass_keypress, NULL);
 }
-#endif
 
 /**
- * Free some memory when vimb is quit.
+ * Factory to create a new webview.
+ *
+ * @webview:    Relates webview or NULL. If given a related webview is
+ *              generated.
  */
-static void vb_cleanup(void)
+static WebKitWebView *webview_new(Client *c, WebKitWebView *webview)
 {
+    WebKitWebView *new;
+    WebKitUserContentManager *ucm;
+    WebKitUserScript *script;
+    char *js = NULL;
 
-    completion_clean();
-    map_cleanup();
-    cleanup_modes();
-    setting_cleanup();
-    history_cleanup();
-    session_cleanup();
-    register_cleanup();
-#ifdef FEATURE_AUTOCMD
-    autocmd_cleanup();
-#endif
-#ifdef FEATURE_ARH
-    arh_free(vb.config.autoresponseheader);
-#endif
-#ifdef FEATURE_SOCKET
-    io_cleanup();
-#endif
-    g_free(vb.state.pid_str);
-    g_free(vb.state.uri);
-
-    g_slist_free_full(vb.config.cmdargs, g_free);
-
-    for (int i = 0; i < FILES_LAST; i++) {
-        g_free(vb.files[i]);
-        vb.files[i] = NULL;
+    /* create a new webview */
+    if (webview) {
+        new = WEBKIT_WEB_VIEW(webkit_web_view_new_with_related_view(webview));
+        ucm = webkit_web_view_get_user_content_manager(webview);
+    } else {
+        ucm = webkit_user_content_manager_new();
+        new = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(ucm));
     }
-}
 
-static void cleanup_modes(void)
-{
-    if (vb.modes) {
-        g_hash_table_destroy(vb.modes);
-        vb.modes = NULL;
-        vb.mode  = NULL;
+    g_object_connect(
+        G_OBJECT(new),
+        "signal::close", G_CALLBACK(on_webview_close), c,
+        "signal::create", G_CALLBACK(on_webview_create), c,
+        "signal::decide-policy", G_CALLBACK(on_webview_decide_policy), c,
+        "signal::load-changed", G_CALLBACK(on_webview_load_changed), c,
+        "signal::mouse-target-changed", G_CALLBACK(on_webview_mouse_target_changed), c,
+        "signal::notify::estimated-load-progress", G_CALLBACK(on_webview_notify_estimated_load_progress), c,
+        "signal::notify::title", G_CALLBACK(on_webview_notify_title), c,
+        "signal::notify::uri", G_CALLBACK(on_webview_notify_uri), c,
+        "signal::ready-to-show", G_CALLBACK(on_webview_ready_to_show), c,
+        "signal::web-process-crashed", G_CALLBACK(on_webview_web_process_crashed), c,
+        NULL
+    );
+
+    /* Inject the user script file. */
+    if (g_file_get_contents(vb.files[FILES_SCRIPT], &js, NULL, NULL)) {
+        script = webkit_user_script_new(js,
+                WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+                WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL);
+        webkit_user_content_manager_add_script(ucm, script);
+        webkit_user_script_unref(script);
     }
-}
 
-static void free_mode(Mode *mode)
-{
-    g_slice_free(Mode, mode);
+    return new;
 }
 
-static gboolean autocmdOptionArgFunc(const gchar *option_name, const gchar *value, gpointer data, GError **error)
+int main(int argc, char* argv[])
 {
-    vb.config.cmdargs = g_slist_append(vb.config.cmdargs, g_strdup(value));
-    return TRUE;
-}
+    Client *c;
+    GError *err = NULL;
+    char *pidstr, *winid = NULL;
+    gboolean ver = FALSE;
 
-int main(int argc, char *argv[])
-{
-    static char *winid   = NULL;
-    static gboolean ver  = false;
-#ifdef FEATURE_SOCKET
-    static gboolean dump = false;
-#endif
-    static GError *err;
-
-    static GOptionEntry opts[] = {
-        {"cmd", 'C', 0, G_OPTION_ARG_CALLBACK, autocmdOptionArgFunc, "Ex command run before first page is loaded", NULL},
-        {"config", 'c', 0, G_OPTION_ARG_FILENAME, &vb.config.file, "Custom configuration file", NULL},
-        {"profile", 'p', 0, G_OPTION_ARG_STRING, &vb.config.profile, "Profile name", NULL},
-        {"embed", 'e', 0, G_OPTION_ARG_STRING, &winid, "Reparents to window specified by xid", NULL},
-#ifdef FEATURE_SOCKET
-        {"dump", 'd', 0, G_OPTION_ARG_NONE, &dump, "Dump the socket path to stdout", NULL},
-        {"socket", 's', 0, G_OPTION_ARG_NONE, &vb.config.socket, "Create control socket", NULL},
-#endif
-        {"kiosk", 'k', 0, G_OPTION_ARG_NONE, &vb.config.kioskmode, "Run in kiosk mode", NULL},
+    GOptionEntry opts[] = {
+        {"embed", 'e', 0, G_OPTION_ARG_STRING, &winid,  "Reparents to window specified by xid", NULL},
+        {"config", 'c', 0, G_OPTION_ARG_FILENAME, &vb.configfile, "Custom configuration file", NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Print version", NULL},
         {NULL}
     };
-    /* Initialize GTK+ */
+
+    /* initialize GTK+ */
     if (!gtk_init_with_args(&argc, &argv, "[URI]", opts, NULL, &err)) {
-        g_printerr("can't init gtk: %s\n", err->message);
+        fprintf(stderr, "can't init gtk: %s\n", err->message);
         g_error_free(err);
 
         return EXIT_FAILURE;
     }
 
     if (ver) {
-        fprintf(stdout, "%s/%s\n", PROJECT, VERSION);
-        return EXIT_SUCCESS;
-    }
-
-    /* save vimb basename */
-    argv0 = argv[0];
+        fprintf(stdout, "%s, version %s\n\n", PROJECT, VERSION);
+        fprintf(stdout, "Copyright © 2012 - 2016 Daniel Carl <danielcarl@gmx.de>\n");
+        fprintf(stdout, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
+        fprintf(stdout, "This is free software; you are free to change and redistribute it.\n");
+        fprintf(stdout, "There is NO WARRANTY, to the extent permitted by law.\n");
 
-    if (winid) {
-        vb.embed = strtol(winid, NULL, 0);
+        return EXIT_SUCCESS;
     }
 
-    vb.state.pid_str = g_strdup_printf("%d", (int)getpid());
-    g_setenv("VIMB_PID", vb.state.pid_str, true);
+    /* Save the base name for spawning new instances. */
+    vb.argv0 = argv[0];
 
-    /* init some state variable */
-    vb.state.enable_register = false;
-    vb.state.uri             = g_strdup("");
+    /* set the current pid in env */
+    pidstr = g_strdup_printf("%d", (int)getpid());
+    g_setenv("VIMB_PID", pidstr, TRUE);
 
-    init_core();
+    vimb_setup();
 
-    /* process the --cmd if this was given */
-    for (GSList *l = vb.config.cmdargs; l; l = l->next) {
-        ex_run_string(l->data, false);
+    if (winid) {
+        vb.embed = strtol(winid, NULL, 0);
     }
 
-    /* active the registers and writing of command history */
-    vb.state.enable_register = true;
-
-    /* open uri given as last argument */
+    c = client_new(NULL);
     if (argc <= 1) {
-        /* open configured home page if no uri was given */
-        vb_load_uri(&(Arg){VB_TARGET_CURRENT, NULL});
-    } else if (!strcmp(argv[argc - 1], "-")) {
-        /* read from stdin if uri is - */
-        read_from_stdin();
+        vb_load_uri(c, &(Arg){TARGET_CURRENT, NULL});
     } else {
-        vb_load_uri(&(Arg){VB_TARGET_CURRENT, argv[argc - 1]});
-    }
-
-#ifdef FEATURE_SOCKET
-    /* setup the control socket - quit vimb if this failed */
-    if (vb.config.socket && !io_init_socket(vb.state.pid_str)) {
-        /* cleanup memory */
-        vb_cleanup();
-        return EXIT_FAILURE;
-    }
-    if (dump && vb.state.socket_path) {
-        printf("%s\n", vb.state.socket_path);
-        fflush(NULL);
+        vb_load_uri(c, &(Arg){TARGET_CURRENT, argv[argc - 1]});
     }
-#endif
 
-    /* Run the main GTK+ event loop */
     gtk_main();
-
-    /* cleanup memory */
-    vb_cleanup();
+#ifdef FREE_ON_QUIT
+    vimb_cleanup();
+#endif
 
     return EXIT_SUCCESS;
 }
index 1bde3cd..f0816d0 100644 (file)
 #ifndef _MAIN_H
 #define _MAIN_H
 
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <webkit/webkit.h>
-#include <JavaScriptCore/JavaScript.h>
 #include <fcntl.h>
-#include <stdio.h>
-#ifdef HAS_GTK3
-#include <gdk/gdkx.h>
 #include <gtk/gtkx.h>
-#endif
-#include "config.h"
-#ifdef FEATURE_HSTS
-#include "hsts.h"
-#endif
+#include <stdio.h>
+#include <webkit2/webkit2.h>
 
-/* size of some I/O buffer */
-#define BUF_SIZE  512
+#include "config.h"
 
 #define LENGTH(x) (sizeof x / sizeof x[0])
+#define OVERWRITE_STRING(t, s) {if (t) g_free(t); t = g_strdup(s);}
+#define OVERWRITE_NSTRING(t, s, l) {if (t) {g_free(t); t = NULL;} t = g_strndup(s, l);}
+#define GET_CHAR(c, n)  (((Setting*)g_hash_table_lookup(c->config.settings, n))->value.s)
+#define GET_INT(c, n)   (((Setting*)g_hash_table_lookup(c->config.settings, n))->value.i)
+#define GET_BOOL(c, n)  (((Setting*)g_hash_table_lookup(c->config.settings, n))->value.b)
 
 
 #ifdef DEBUG
 #define PRINT_DEBUG(...) { \
-    fprintf(stderr, "\n\033[31;1mDEBUG:\033[0m %s:%d:%s()\t", __FILE__, __LINE__, __func__); \
+    fprintf(stderr, "\n\033[31;1mDEBUG: \033[32;1m%s +%d %s()\033[0m\t", __FILE__, __LINE__, __func__); \
     fprintf(stderr, __VA_ARGS__);\
 }
 #define TIMER_START GTimer *__timer; {__timer = g_timer_new(); g_timer_start(__timer);}
 #define TIMER_END
 #endif
 
-#define PRIMARY_CLIPBOARD() gtk_clipboard_get(GDK_SELECTION_PRIMARY)
-#define SECONDARY_CLIPBOARD() gtk_clipboard_get(GDK_NONE)
-
-#define OVERWRITE_STRING(t, s) {if (t) {g_free(t); t = NULL;} t = g_strdup(s);}
-#define OVERWRITE_NSTRING(t, s, l) {if (t) {g_free(t); t = NULL;} t = g_strndup(s, l);}
-
-#define GET_CHAR(n)  (((Setting*)g_hash_table_lookup(vb.config.settings, n))->value.s)
-#define GET_INT(n)   (((Setting*)g_hash_table_lookup(vb.config.settings, n))->value.i)
-#define GET_BOOL(n)  (((Setting*)g_hash_table_lookup(vb.config.settings, n))->value.b)
-
-#ifdef HAS_GTK3
-#define VbColor GdkRGBA
-#define VB_COLOR_PARSE(color, string)           (gdk_rgba_parse(color, string))
-#define VB_COLOR_TO_STRING(color)               (gdk_rgba_to_string(color))
-#define VB_WIDGET_OVERRIDE_BACKGROUND(w, s, c)
-#define VB_WIDGET_OVERRIDE_BASE(w, s, c)        (gtk_widget_override_background_color(w, s, c))
-#define VB_WIDGET_OVERRIDE_COLOR(w, s, c)
-#define VB_WIDGET_OVERRIDE_TEXT(w, s, c)        (gtk_widget_override_color(w, s, c))
-#define VB_WIDGET_OVERRIDE_FONT(w, f)           (gtk_widget_override_font(w, f))
-
-#define VB_GTK_STATE_NORMAL                     GTK_STATE_FLAG_NORMAL
-#define VB_GTK_STATE_ACTIVE                     GTK_STATE_FLAG_ACTIVE
-#define VB_GTK_STATE_SELECTED                   GTK_STATE_FLAG_SELECTED
-#define VB_WIDGET_SET_STATE(w, s)               (gtk_widget_set_state_flags(w, s, true))
-
-#else
-
-#define VbColor GdkColor
-#define VB_COLOR_PARSE(color, string)           (gdk_color_parse(string, color))
-#define VB_COLOR_TO_STRING(color)               (gdk_color_to_string(color))
-#define VB_WIDGET_OVERRIDE_BACKGROUND(w, s, c)  (gtk_widget_modify_bg(w, s, c))
-#define VB_WIDGET_OVERRIDE_BASE(w, s, c)        (gtk_widget_modify_base(w, s, c))
-#define VB_WIDGET_OVERRIDE_COLOR(w, s, c)       (gtk_widget_modify_fg(w, s, c))
-#define VB_WIDGET_OVERRIDE_TEXT(w, s, c)        (gtk_widget_modify_text(w, s, c))
-#define VB_WIDGET_OVERRIDE_FONT(w, f)           (gtk_widget_modify_font(w, f))
-
-#define VB_GTK_STATE_NORMAL                     GTK_STATE_NORMAL
-#define VB_GTK_STATE_ACTIVE                     GTK_STATE_ACTIVE
-#define VB_GTK_STATE_SELECTED                   GTK_STATE_SELECTED
-#define VB_WIDGET_SET_STATE(w, s)               (gtk_widget_set_state(w, s))
-#endif
-
-#ifndef SOUP_CHECK_VERSION
-#define SOUP_CHECK_VERSION(major, minor, micro) (0)
-#endif
-
 /* the special mark ' must be the first in the list for easiest lookup */
-#define VB_MARK_CHARS   "'abcdefghijklmnopqrstuvwxyz"
-#define VB_MARK_TICK    0
-#define VB_MARK_SIZE    (sizeof(VB_MARK_CHARS) - 1)
+#define MARK_CHARS   "'abcdefghijklmnopqrstuvwxyz"
+#define MARK_TICK    0
+#define MARK_SIZE    (sizeof(MARK_CHARS) - 1)
 
-#define VB_USER_REG     "abcdefghijklmnopqrstuvwxyz"
+#define USER_REG     "abcdefghijklmnopqrstuvwxyz"
 /* registers in order displayed for :register command */
-#define VB_REG_CHARS    "\"" VB_USER_REG ":%/;"
-#define VB_REG_SIZE     (sizeof(VB_REG_CHARS) - 1)
+#define REG_CHARS    "\"" USER_REG ":%/;"
+#define REG_SIZE     (sizeof(REG_CHARS) - 1)
+
+#define FILE_CLOSED  "closed"
+#define FILE_COOKIES "cookies"
+
+enum { TARGET_CURRENT, TARGET_RELATED, TARGET_NEW };
 
-/* enums */
 typedef enum {
-    RESULT_COMPLETE,
-    RESULT_MORE,
-    RESULT_ERROR
+    RESULT_COMPLETE, RESULT_MORE, RESULT_ERROR
 } VbResult;
 
 typedef enum {
-    VB_INPUT_UNKNOWN,
-    VB_INPUT_SET              = 0x01,
-    VB_INPUT_OPEN             = 0x02,
-    VB_INPUT_TABOPEN          = 0x04,
-    VB_INPUT_COMMAND          = 0x08,
-    VB_INPUT_SEARCH_FORWARD   = 0x10,
-    VB_INPUT_SEARCH_BACKWARD  = 0x20,
-    VB_INPUT_BOOKMARK_ADD     = 0x40,
-    VB_INPUT_ALL              = 0xff, /* map to match all input types */
-} VbInputType;
-
-enum {
-    VB_NAVIG_BACK,
-    VB_NAVIG_FORWARD,
-    VB_NAVIG_RELOAD,
-    VB_NAVIG_RELOAD_FORCE,
-    VB_NAVIG_STOP_LOADING
-};
-
-enum {
-    VB_TARGET_CURRENT,
-    VB_TARGET_NEW
-};
-
-enum {
-    VB_INPUT_CURRENT_URI = 1
-};
+    CMD_ERROR,               /* command could not be parses or executed */
+    CMD_SUCCESS   = 0x01,    /* command runned successfully */
+    CMD_KEEPINPUT = 0x02,    /* don't clear inputbox after command run */
+} VbCmdResult;
 
-/*
-1 << 0:  0 = jump,              1 = scroll
-1 << 1:  0 = vertical,          1 = horizontal
-1 << 2:  0 = top/left,          1 = down/right
-1 << 3:  0 = paging/halfpage,   1 = line
-1 << 4:  0 = paging,            1 = halfpage
-*/
-enum {VB_SCROLL_TYPE_JUMP, VB_SCROLL_TYPE_SCROLL};
-enum {
-    VB_SCROLL_AXIS_V,
-    VB_SCROLL_AXIS_H = (1 << 1)
-};
-enum {
-    VB_SCROLL_DIRECTION_TOP,
-    VB_SCROLL_DIRECTION_DOWN  = (1 << 2),
-    VB_SCROLL_DIRECTION_LEFT  = VB_SCROLL_AXIS_H,
-    VB_SCROLL_DIRECTION_RIGHT = VB_SCROLL_AXIS_H | (1 << 2)
-};
-enum {
-    VB_SCROLL_UNIT_PAGE,
-    VB_SCROLL_UNIT_LINE     = (1 << 3),
-    VB_SCROLL_UNIT_HALFPAGE = (1 << 4)
-};
+typedef enum {
+    TYPE_BOOLEAN, TYPE_INTEGER, TYPE_CHAR, TYPE_COLOR, TYPE_FONT
+} DataType;
 
 typedef enum {
-    VB_MSG_NORMAL,
-    VB_MSG_ERROR,
-    VB_MSG_LAST
+    MSG_NORMAL, MSG_ERROR
 } MessageType;
 
 typedef enum {
-    VB_STATUS_NORMAL,
-    VB_STATUS_SSL_VALID,
-    VB_STATUS_SSL_INVALID,
-    VB_STATUS_LAST
+    STATUS_NORMAL, STATUS_SSL_VALID, STATUS_SSL_INVALID
 } StatusType;
 
 typedef enum {
-    VB_CMD_ERROR,               /* command could not be parses or executed */
-    VB_CMD_SUCCESS   = 0x01,    /* command runned successfully */
-    VB_CMD_KEEPINPUT = 0x02,    /* don't clear inputbox after command run */
-} VbCmdResult;
+    INPUT_UNKNOWN,
+    INPUT_SET              = 0x01,
+    INPUT_OPEN             = 0x02,
+    INPUT_TABOPEN          = 0x04,
+    INPUT_COMMAND          = 0x08,
+    INPUT_SEARCH_FORWARD   = 0x10,
+    INPUT_SEARCH_BACKWARD  = 0x20,
+    INPUT_BOOKMARK_ADD     = 0x40,
+    INPUT_ALL              = 0xff, /* map to match all input types */
+} VbInputType;
 
-typedef enum {
-    VB_COMP_NORMAL,
-    VB_COMP_ACTIVE,
-    VB_COMP_LAST
-} CompletionStyle;
+enum {
+    COMP_NORMAL, COMP_ACTIVE, COMP_LAST
+};
 
-typedef enum {
+enum {
+    FILES_BOOKMARK,
+    FILES_CLOSED,
+    FILES_COMMAND,
     FILES_CONFIG,
-#ifdef FEATURE_COOKIE
     FILES_COOKIE,
-#endif
-    FILES_CLOSED,
-    FILES_SCRIPT,
     FILES_HISTORY,
-    FILES_COMMAND,
-    FILES_SEARCH,
-    FILES_BOOKMARK,
-#ifdef FEATURE_QUEUE
+    FILES_HSTS,
     FILES_QUEUE,
-#endif
+    FILES_SCRIPT,
+    FILES_SEARCH,
     FILES_USER_STYLE,
-#ifdef FEATURE_HSTS
-    FILES_HSTS,
-#endif
     FILES_LAST
-} VbFile;
+};
 
-enum {
-    VB_CLIPBOARD_PRIMARY   = (1<<1),
-    VB_CLIPBOARD_SECONDARY = (1<<2)
+typedef struct Client Client;
+typedef struct Map Map;
+typedef struct Mode Mode;
+typedef struct Arg Arg;
+typedef void (*ModeTransitionFunc)(Client*);
+typedef VbResult (*ModeKeyFunc)(Client*, int);
+typedef void (*ModeInputChangedFunc)(Client*, const char*);
+
+struct Arg {
+    int  i;
+    char *s;
 };
 
-typedef int (*SettingFunction)(const char *name, int type, void *value, void *data);
+typedef int (*SettingFunction)(Client *c, const char *name, DataType type, void *value, void *data);
 typedef union {
     gboolean b;
     int      i;
@@ -241,16 +148,48 @@ typedef struct {
     void            *data;  /* data given to the setter */
 } Setting;
 
-/* structs */
-typedef struct {
-    int  i;
-    char *s;
-} Arg;
+struct State {
+    char                *uri;
+    gboolean            typed;              /* indicates if the user typed the keys */
+    gboolean            processed_key;      /* indicates if a key press was handled and should not bubbled up */
+    gboolean            ctrlv;              /* indicates if the CTRL-V temorary submode is on */
 
-typedef void (*ModeTransitionFunc) (void);
-typedef VbResult (*ModeKeyFunc) (int);
-typedef void (*ModeInputChangedFunc) (const char*);
-typedef struct {
+#define PROMPT_SIZE 4
+    char                prompt[PROMPT_SIZE];/* current prompt ':', 'g;t', '/' including nul */
+    gdouble             marks[MARK_SIZE];   /* holds marks set to page with 'm{markchar}' */
+    guint               input_timer;
+    MessageType         input_type;
+    StatusType          status_type;
+    glong               scroll_max;         /* Maxmimum scrollable height of the document. */
+    guint               scroll_percent;     /* Current position of the viewport in document. */
+
+    char                *reg[REG_SIZE];     /* holds the yank buffers */
+    /* TODO rename to reg_{enabled,current} */
+    gboolean            enable_register;    /* indicates if registers are filled */
+    char                current_register;   /* holds char for current register to be used */
+
+    GList               *downloads;
+    guint               progress;
+    WebKitHitTestResult *hit_test_result;
+
+    struct {
+        gboolean    active;         /* indicate if there is a acitve search */
+        short       direction;      /* last direction 1 forward, -1 backward */
+        int         matches;        /* number of matches search results */
+    } search;
+};
+
+struct Map {
+    char        *in;                /* input keys */
+    int         inlen;              /* length of the input keys */
+    char        *mapped;            /* mapped keys */
+    int         mappedlen;          /* length of the mapped keys string */
+    char        mode;               /* mode for which the map is available */
+    gboolean    remap;              /* if FALSE do not remap the {rhs} of this map */
+    gboolean    enable_register;    /* indicates if registers are filled */
+};
+
+struct Mode {
     char                 id;
     ModeTransitionFunc   enter;         /* is called if the mode is entered */
     ModeTransitionFunc   leave;         /* is called if the mode is left */
@@ -261,153 +200,79 @@ typedef struct {
 #define FLAG_COMPLETION  0x0004  /* marks active completion submode */
 #define FLAG_PASSTHROUGH 0x0008  /* don't handle any other keybind than <esc> */
     unsigned int         flags;
-} Mode;
+};
 
-/* statusbar */
-typedef struct {
+struct Statusbar {
     GtkBox    *box;
-    GtkWidget *mode;
-    GtkWidget *left;
-    GtkWidget *right;
-    GtkWidget *cmd;
-} StatusBar;
-
-/* gui */
-typedef struct {
-    GtkWidget          *window;
-    WebKitWebView      *webview;
-    WebKitWebInspector *inspector;
-    GtkBox             *box;
-    GtkWidget          *eventbox;
-    GtkWidget          *input;
-    GtkTextBuffer      *buffer; /* text buffer associated with the input for fast access */
-    GtkWidget          *pane;
-    StatusBar          statusbar;
-    GtkAdjustment      *adjust_h;
-    GtkAdjustment      *adjust_v;
-} Gui;
-
-/* state */
-typedef struct {
-    char            *uri;                   /* holds current  uri or the new to open uri */
-    guint           progress;
-    StatusType      status_type;
-    MessageType     input_type;
-    gboolean        is_inspecting;
-    GList           *downloads;
-    gboolean        processed_key;
-    char            *title;                 /* holds the window title */
-#define PROMPT_SIZE 4
-    char            prompt[PROMPT_SIZE];    /* current prompt ':', 'g;t', '/' including nul */
-    gdouble         marks[VB_MARK_SIZE];    /* holds marks set to page with 'm{markchar}' */
-    char            *linkhover;             /* the uri of the curret hovered link */
-    char            *reg[VB_REG_SIZE];      /* holds the yank buffer */
-    gboolean        enable_register;        /* indicates if registers are filled */
-    char            current_register;       /* holds char for current register to be used */
-    gboolean        typed;                  /* indicates if th euser type the keys processed as command */
-#ifdef FEATURE_SEARCH_HIGHLIGHT
-    int             search_matches;         /* number of matches search results */
-#endif
-    char            *fifo_path;             /* holds the path to the control fifo */
-    char            *socket_path;           /* holds the path to the control socket */
-    char            *pid_str;               /* holds the pid as string */
-    gboolean        done_loading_page;
-    gboolean        window_has_focus;
-} State;
-
-typedef struct {
-#ifdef FEATURE_COOKIE
-    time_t       cookie_timeout;
-    int          cookie_expire_time;
-#endif
-    int          scrollstep;
-    char         *download_dir;
-    guint        history_max;
-    guint        closed_max;
-    guint        timeoutlen;       /* timeout for ambiguous mappings */
-    gboolean     strict_focus;
-    GHashTable   *headers;         /* holds user defined header appended to requests */
-#ifdef FEATURE_ARH
-    GSList       *autoresponseheader; /* holds user defined list of auto-response-header */
-#endif
-    char         *nextpattern;     /* regex patter nfor prev link matching */
-    char         *prevpattern;     /* regex patter nfor next link matching */
-    char         *file;            /* path to the custome config file */
-    char         *profile;         /* profile name */
-    GSList       *cmdargs;         /* list of commands given by --cmd option */
-    char         *cafile;          /* path to the ca file */
-    GTlsDatabase *tls_db;          /* tls database */
-    float        default_zoom;     /* default zoomlevel that is applied on zz zoom reset */
-    gboolean     kioskmode;
-    gboolean     input_autohide;   /* indicates if the inputbox should be hidden if it's empty */
-#ifdef FEATURE_SOCKET
-    gboolean     socket;           /* indicates if the socket is used */
-#endif
-#ifdef FEATURE_HSTS
-    HSTSProvider *hsts_provider;   /* the hsts session feature that is added to soup session */
-#endif
-#ifdef FEATURE_SOUP_CACHE
-    SoupCache    *soup_cache;      /* soup caching feature model */
-#endif
-    GHashTable   *settings;
-} Config;
+    GtkWidget *mode, *left, *right, *cmd;
+};
 
-typedef struct {
-    VbColor              input_fg[VB_MSG_LAST];
-    VbColor              input_bg[VB_MSG_LAST];
-    PangoFontDescription *input_font[VB_MSG_LAST];
-    /* completion */
-    VbColor              comp_fg[VB_COMP_LAST];
-    VbColor              comp_bg[VB_COMP_LAST];
-    PangoFontDescription *comp_font;
-    /* status bar */
-    VbColor              status_bg[VB_STATUS_LAST];
-    VbColor              status_fg[VB_STATUS_LAST];
-    PangoFontDescription *status_font[VB_STATUS_LAST];
-} VbStyle;
+struct Client {
+    struct Client       *next;
+    struct State        state;
+    struct Statusbar    statusbar;
+    void                *comp;                  /* pointer to data used in completion.c */
+    Mode                *mode;                  /* current active browser mode */
+    WebKitWebContext    *webctx;
+    GtkWidget           *window, *input;
+    WebKitWebView       *webview;
+    guint64             page_id;                /* page id of the webview */
+    GtkTextBuffer       *buffer;
+    GDBusProxy          *dbusproxy;
+    struct {
+        /* TODO split in global setting definitions and set values on a per
+         * client base. */
+        GHashTable              *settings;
+        guint                   scrollstep;
+        gboolean                input_autohide;
+        /* completion */
+        GdkRGBA                 comp_fg[COMP_LAST];
+        GdkRGBA                 comp_bg[COMP_LAST];
+        PangoFontDescription    *comp_font;
+    } config;
+    struct {
+        GSList      *list;
+        GString     *queue;                     /* queue holding typed keys */
+        int         qlen;                       /* pointer to last char in queue */
+        int         resolved;                   /* number of resolved keys (no mapping required) */
+        guint       timout_id;                  /* source id of the timeout function */
+        char        showcmd[SHOWCMD_LEN + 1];   /* buffer to show ambiguous key sequence */
+        guint       timeoutlen;                 /* timeout for ambiguous mappings */
+    } map;
+    struct {
+        GHashTable  *table;
+        char        *fallback;                  /* default shortcut to use if none given in request */
+    } shortcut;
+};
 
-typedef struct {
-    Gui             gui;
-    State           state;
-
-    char            *files[FILES_LAST];
-    Mode            *mode;
-    Config          config;
-    VbStyle           style;
-    SoupSession     *session;
-#ifdef HAS_GTK3
-    Window          embed;
-#else
-    GdkNativeWindow embed;
-#endif
-    GHashTable      *modes; /* all available browser main modes */
-} VbCore;
+struct Vimb {
+    char        *argv0;
+    Client      *clients;
+    Window      embed;
+    GHashTable  *modes;             /* all available browser main modes */
+    char        *configfile;        /* config file given as option on startup */
+    char        *files[FILES_LAST];
+    struct {
+        guint   history_max;
+    } config;
+};
 
-/* main object */
-extern VbCore core;
 
-/* functions */
-void vb_add_mode(char id, ModeTransitionFunc enter, ModeTransitionFunc leave,
+void vb_echo(Client *c, MessageType type, gboolean hide, const char *error, ...);
+void vb_echo_force(Client *c, MessageType type, gboolean hide, const char *error, ...);
+void vb_enter(Client *c, char id);
+void vb_enter_prompt(Client *c, char id, const char *prompt, gboolean print_prompt);
+char *vb_input_get_text(Client *c);
+void vb_input_set_text(Client *c, const char *text);
+void vb_input_update_style(Client *c);
+gboolean vb_load_uri(Client *c, const Arg *arg);
+void vb_mode_add(char id, ModeTransitionFunc enter, ModeTransitionFunc leave,
     ModeKeyFunc keypress, ModeInputChangedFunc input_changed);
-void vb_echo_force(const MessageType type,gboolean hide, const char *error, ...);
-void vb_echo(const MessageType type, gboolean hide, const char *error, ...);
-void vb_enter(char id);
-void vb_enter_prompt(char id, const char *prompt, gboolean print_prompt);
-VbResult vb_handle_key(int key);
-void vb_set_input_text(const char *text);
-char *vb_get_input_text(void);
-void vb_input_activate(void);
-gboolean vb_load_uri(const Arg *arg);
-gboolean vb_set_clipboard(const Arg *arg);
-void vb_set_widget_font(GtkWidget *widget, const VbColor *fg, const VbColor *bg, PangoFontDescription *font);
-void vb_update_statusbar(void);
-void vb_update_status_style(void);
-void vb_update_input_style(void);
-void vb_update_urlbar(const char *uri);
-void vb_update_mode_label(const char *label);
-void vb_register_add(char buf, const char *value);
-const char *vb_register_get(char buf);
-gboolean vb_download(WebKitWebView *view, WebKitDownload *download, const char *path);
-void vb_quit(gboolean force);
+VbResult vb_mode_handle_key(Client *c, int key);
+void vb_modelabel_update(Client *c, const char *label);
+void vb_quit(Client *c, gboolean force);
+void vb_register_add(Client *c, char buf, const char *value);
+const char *vb_register_get(Client *c, char buf);
+void vb_statusbar_update(Client *c);
 
 #endif /* end of include guard: _MAIN_H */
index a9fad55..5b4017a 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -1,4 +1,6 @@
 /**
+ * vimb - a webkit based vim like browser.
+ *
  * Copyright (C) 2012-2015 Daniel Carl
  *
  * This program is free software: you can redistribute it and/or modify
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include <gdk/gdkkeysyms.h>
 #include <gdk/gdkkeysyms-compat.h>
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include "ascii.h"
 #include "config.h"
 #include "main.h"
 #include "map.h"
-#include "normal.h"
-#include "ascii.h"
-
-/* convert the lower 4 bits of byte n to its hex character */
-#define NR2HEX(n)   (n & 0xf) <= 9 ? (n & 0xf) + '0' : (c & 0xf) - 10 + 'a'
-
-typedef struct {
-    char     *in;       /* input keys */
-    int      inlen;     /* length of the input keys */
-    char     *mapped;   /* mapped keys */
-    int      mappedlen; /* length of the mapped keys string */
-    char     mode;      /* mode for which the map is available */
-    gboolean remap;     /* if false do not remap the {rhs} of this map */
-} Map;
-
-/* this is only to keep the variables together */
-static struct {
-    GSList  *list;
-    GString *queue;                     /* queue holding typed keys */
-    int     qlen;                       /* pointer to last char in queue */
-    int     resolved;                   /* number of resolved keys (no mapping required) */
-    guint   timout_id;                  /* source id of the timeout function */
-    char    showcmd[SHOWCMD_LEN + 1];   /* buffer to show ambiguous key sequence */
-} map;
-
-extern VbCore vb;
+#include "util.h"
+
+struct MapInfo {
+    GSList      *list;
+    GString     *queue;                     /* queue holding typed keys */
+    int         qlen;                       /* pointer to last char in queue */
+    int         resolved;                   /* number of resolved keys (no mapping required) */
+    guint       timout_id;                  /* source id of the timeout function */
+    char        showcmd[SHOWCMD_LEN + 1];   /* buffer to show ambiguous key sequence */
+    guint       timeoutlen;                 /* timeout for ambiguous mappings */
+};
 
-static void showcmd(int c);
-static char *transchar(int c);
-static gboolean map_delete_by_lhs(const char *lhs, int len, char mode);
-static int keyval_to_string(guint keyval, guint state, guchar *string);
-static int utf_char2bytes(guint c, guchar *buf);
-static char *convert_keys(const char *in, int inlen, int *len);
 static char *convert_keylabel(const char *in, int inlen, int *len);
-static gboolean do_timeout(gpointer data);
+static char *convert_keys(const char *in, int inlen, int *len);
+static gboolean do_timeout(Client *c);
 static void free_map(Map *map);
+static int keyval_to_string(guint keyval, guint state, guchar *string);
+static gboolean map_delete_by_lhs(Client *c, const char *lhs, int len, char mode);
+static void showcmd(Client *c, int ch);
+static char *transchar(int c);
+static int utf_char2bytes(guint c, guchar *buf);
+
+extern struct Vimb vb;
 
 static struct {
     guint state;
@@ -112,69 +104,21 @@ static struct {
     {"<F12>",   5, CSI_STR "F2", 3},
 };
 
-void map_init(void)
+void map_init(Client *c)
 {
-    map.queue = g_string_sized_new(50);
+    c->map.queue = g_string_sized_new(50);
+    /* TODO move this to settings */
+    c->map.timeoutlen = 1000;
 }
 
-void map_cleanup(void)
+void map_cleanup(Client *c)
 {
-    if (map.list) {
-        g_slist_free_full(map.list, (GDestroyNotify)free_map);
-    }
-    g_string_free(map.queue, true);
-}
-
-/**
- * Handle all key events, convert the key event into the internal used ASCII
- * representation and put this into the key queue to be mapped.
- */
-gboolean map_keypress(GtkWidget *widget, GdkEventKey* event, gpointer data)
-{
-    guint state  = event->state;
-    guint keyval = event->keyval;
-    guchar string[32];
-    int len;
-
-    len = keyval_to_string(keyval, state, string);
-
-    /* translate iso left tab to shift tab */
-    if (keyval == GDK_ISO_Left_Tab) {
-        keyval = GDK_Tab;
-        state |= GDK_SHIFT_MASK;
-    }
-
-    if (len == 0 || len == 1) {
-        for (int i = 0; i < LENGTH(special_keys); i++) {
-            if (special_keys[i].keyval == keyval
-                && (special_keys[i].state == 0 || state & special_keys[i].state)
-            ) {
-                state &= ~special_keys[i].state;
-                string[0] = CSI;
-                string[1] = special_keys[i].one;
-                string[2] = special_keys[i].two;
-                len = 3;
-                break;
-            }
-        }
+    if (c->map.list) {
+        g_slist_free_full(c->map.list, (GDestroyNotify)free_map);
     }
-
-    if (len == 0) {
-        /* mark all unknown key events as unhandled to not break some gtk features
-         * like <S-Einf> to copy clipboard content into inputbox */
-        return false;
+    if (c->map.queue) {
+        g_string_free(c->map.queue, TRUE);
     }
-
-    /* set flag to notify that the key was typed by the user */
-    vb.state.typed         = true;
-    vb.state.processed_key = true;
-
-    map_handle_keys(string, len, true);
-
-    /* reset the typed flag */
-    vb.state.typed = false;
-
-    return vb.state.processed_key;
 }
 
 /**
@@ -182,7 +126,7 @@ gboolean map_keypress(GtkWidget *widget, GdkEventKey* event, gpointer data)
  * chars. The key sequence do not need to be NUL terminated.
  * Keylen of 0 signalized a key timeout.
  */
-MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
+MapState map_handle_keys(Client *c, const guchar *keys, int keylen, gboolean use_map)
 {
     int ambiguous;
     Map *match = NULL;
@@ -190,99 +134,99 @@ MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
     static int showlen = 0;           /* track the number of keys in showcmd of status bar */
 
     /* if a previous timeout function was set remove this */
-    if (map.timout_id) {
-        g_source_remove(map.timout_id);
-        map.timout_id = 0;
+    if (c->map.timout_id) {
+        g_source_remove(c->map.timout_id);
+        c->map.timout_id = 0;
     }
 
     /* don't set the timeout function if the timeout is processed now */
     if (!timeout) {
-        map.timout_id = g_timeout_add(vb.config.timeoutlen, (GSourceFunc)do_timeout, NULL);
+        c->map.timout_id = g_timeout_add(c->map.timeoutlen, (GSourceFunc)do_timeout, c);
     }
 
     /* copy the keys onto the end of queue */
     if (keylen > 0) {
-        g_string_overwrite_len(map.queue, map.qlen, (char*)keys, keylen);
-        map.qlen += keylen;
+        g_string_overwrite_len(c->map.queue, c->map.qlen, (char*)keys, keylen);
+        c->map.qlen += keylen;
     }
 
     /* try to resolve keys against the map */
-    while (true) {
+    while (TRUE) {
         /* send any resolved key to the parser */
-        while (map.resolved > 0) {
+        while (c->map.resolved > 0) {
             int qk;
 
             /* skip csi indicator and the next 2 chars - if the csi sequence
              * isn't part of a mapped command we let gtk handle the key - this
              * is required allow to move cursor in inputbox with <Left> and
              * <Right> keys */
-            if ((map.queue->str[0] & 0xff) == CSI && map.qlen >= 3) {
+            if ((c->map.queue->str[0] & 0xff) == CSI && c->map.qlen >= 3) {
                 /* get next 2 chars to build the termcap key */
-                qk = TERMCAP2KEY(map.queue->str[1], map.queue->str[2]);
+                qk = TERMCAP2KEY(c->map.queue->str[1], c->map.queue->str[2]);
 
-                map.resolved -= 3;
-                map.qlen     -= 3;
+                c->map.resolved -= 3;
+                c->map.qlen     -= 3;
                 /* move all other queue entries three steps to the left */
-                memmove(map.queue->str, map.queue->str + 3, map.qlen);
+                memmove(c->map.queue->str, c->map.queue->str + 3, c->map.qlen);
             } else {
                 /* get first char of queue */
-                qk = map.queue->str[0];
+                qk = c->map.queue->str[0];
 
-                map.resolved--;
-                map.qlen--;
+                c->map.resolved--;
+                c->map.qlen--;
 
                 /* move all other queue entries one step to the left */
-                memmove(map.queue->str, map.queue->str + 1, map.qlen);
+                memmove(c->map.queue->str, c->map.queue->str + 1, c->map.qlen);
             }
 
             /* remove the no-map flag */
-            vb.mode->flags &= ~FLAG_NOMAP;
+            c->mode->flags &= ~FLAG_NOMAP;
 
             /* send the key to the parser */
-            if (RESULT_MORE != vb_handle_key((int)qk)) {
-                showcmd(0);
+            if (RESULT_MORE != vb_mode_handle_key(c, (int)qk)) {
+                showcmd(c, 0);
                 showlen = 0;
             } else if (showlen > 0) {
                 showlen--;
             } else {
-                showcmd(qk);
+                showcmd(c, qk);
             }
         }
 
         /* if all keys where processed return MAP_DONE */
-        if (map.qlen == 0) {
-            map.resolved = 0;
+        if (c->map.qlen == 0) {
+            c->map.resolved = 0;
             return match ? MAP_DONE : MAP_NOMATCH;
         }
 
         /* try to find matching maps */
         match     = NULL;
         ambiguous = 0;
-        if (use_map && !(vb.mode->flags & FLAG_NOMAP)) {
-            for (GSList *l = map.list; l != NULL; l = l->next) {
+        if (use_map && !(c->mode->flags & FLAG_NOMAP)) {
+            for (GSList *l = c->map.list; l != NULL; l = l->next) {
                 Map *m = (Map*)l->data;
                 /* ignore maps for other modes */
-                if (m->mode != vb.mode->id) {
+                if (m->mode != c->mode->id) {
                     continue;
                 }
 
                 /* find ambiguous matches */
-                if (!timeout && m->inlen > map.qlen && !strncmp(m->in, map.queue->str, map.qlen)) {
+                if (!timeout && m->inlen > c->map.qlen && !strncmp(m->in, c->map.queue->str, c->map.qlen)) {
                     if (ambiguous == 0) {
                         /* show command chars for the ambiguous commands */
-                        int i = map.qlen > SHOWCMD_LEN ? map.qlen - SHOWCMD_LEN : 0;
+                        int i = c->map.qlen > SHOWCMD_LEN ? c->map.qlen - SHOWCMD_LEN : 0;
                         /* appen only those chars that are not already in showcmd */
                         i += showlen;
-                        while (i < map.qlen) {
-                            showcmd(map.queue->str[i++]);
+                        while (i < c->map.qlen) {
+                            showcmd(c, c->map.queue->str[i++]);
                             showlen++;
                         }
                     }
                     ambiguous++;
                 }
                 /* complete match or better/longer match than previous found */
-                if (m->inlen <= map.qlen
-                    && !strncmp(m->in, map.queue->str, m->inlen)
+                if (m->inlen <= c->map.qlen
+                    && !strncmp(m->in, c->map.queue->str, m->inlen)
                     && (!match || match->inlen < m->inlen)
                 ) {
                     /* backup this found possible match */
@@ -290,7 +234,7 @@ MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
                 }
             }
 
-            /* if there are ambiguous matches return MAP_KEY and flush queue
+            /* if there are ambiguous matches return MAP_AMBIGUOUS and flush queue
              * after a timeout if the user do not type more keys */
             if (ambiguous) {
                 return MAP_AMBIGUOUS;
@@ -303,7 +247,7 @@ MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
             /* Flush the show command to make room for possible mapped command
              * chars to show. For example if :nmap foo 12g is use we want to
              * display the incomplete 12g command. */
-            showcmd(0);
+            showcmd(c, 0);
             showlen = 0;
 
             /* Replace the matching input chars by the mapped chars. */
@@ -312,30 +256,30 @@ MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
                  * chars with the mapped chars. This case could also be
                  * handled by the later string erase and prepend, but handling
                  * it special avoids unneded function call. */
-                g_string_overwrite_len(map.queue, 0, match->mapped, match->mappedlen);
+                g_string_overwrite_len(c->map.queue, 0, match->mapped, match->mappedlen);
             } else {
                 /* Remove all the chars that where matched and prepend the
                  * mapped chars to the queue. */
-                g_string_erase(map.queue, 0, match->inlen);
-                g_string_prepend_len(map.queue, match->mapped, match->mappedlen);
+                g_string_erase(c->map.queue, 0, match->inlen);
+                g_string_prepend_len(c->map.queue, match->mapped, match->mappedlen);
             }
-            map.qlen += match->mappedlen - match->inlen;
+            c->map.qlen += match->mappedlen - match->inlen;
 
             /* without remap the mapped chars are resolved now */
             if (!match->remap) {
-                map.resolved = match->mappedlen;
+                c->map.resolved = match->mappedlen;
             } else if (match->inlen <= match->mappedlen
                 && !strncmp(match->in, match->mapped, match->inlen)
             ) {
-                map.resolved = match->inlen;
+                c->map.resolved = match->inlen;
             }
             /* Unset the typed flag - if there where keys replaced by a
              * mapping the resulting key string is considered as not typed by
              * the user. */
-            vb.state.typed = false;
+            c->state.typed = FALSE;
         } else {
             /* first char is not mapped but resolved */
-            map.resolved = 1;
+            c->map.resolved = 1;
         }
     }
 
@@ -346,21 +290,21 @@ MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map)
  * Like map_handle_keys but use a null terminates string with untranslated
  * keys like <C-T> that are converted here before calling map_handle_keys.
  */
-void map_handle_string(const char *str, gboolean use_map)
+void map_handle_string(Client *c, const char *str, gboolean use_map)
 {
     int len;
     char *keys = convert_keys(str, strlen(str), &len);
-    map_handle_keys((guchar*)keys, len, use_map);
+    map_handle_keys(c, (guchar*)keys, len, use_map);
 }
 
-void map_insert(const char *in, const char *mapped, char mode, gboolean remap)
+void map_insert(Client *c, const char *in, const char *mapped, char mode, gboolean remap)
 {
     int inlen, mappedlen;
     char *lhs = convert_keys(in, strlen(in), &inlen);
     char *rhs = convert_keys(mapped, strlen(mapped), &mappedlen);
 
     /* if lhs was already mapped, remove this first */
-    map_delete_by_lhs(lhs, inlen, mode);
+    map_delete_by_lhs(c, lhs, inlen, mode);
 
     Map *new = g_slice_new(Map);
     new->in        = lhs;
@@ -370,81 +314,186 @@ void map_insert(const char *in, const char *mapped, char mode, gboolean remap)
     new->mode      = mode;
     new->remap     = remap;
 
-    map.list = g_slist_prepend(map.list, new);
+    c->map.list = g_slist_prepend(c->map.list, new);
 }
 
-gboolean map_delete(const char *in, char mode)
+gboolean map_delete(Client *c, const char *in, char mode)
 {
     int len;
     char *lhs = convert_keys(in, strlen(in), &len);
 
-    return map_delete_by_lhs(lhs, len, mode);
+    return map_delete_by_lhs(c, lhs, len, mode);
 }
 
 /**
- * Put the given char onto the show command buffer.
+ * Handle all key events, convert the key event into the internal used ASCII
+ * representation and put this into the key queue to be mapped.
  */
-static void showcmd(int c)
+gboolean on_map_keypress(GtkWidget *widget, GdkEventKey* event, Client *c)
 {
-    char *translated;
-    int old, extra, overflow;
+    guint state  = event->state;
+    guint keyval = event->keyval;
+    guchar string[32];
+    int len;
 
-    if (c) {
-        translated = transchar(c);
-        old        = strlen(map.showcmd);
-        extra      = strlen(translated);
-        overflow   = old + extra - SHOWCMD_LEN;
-        if (overflow > 0) {
-            memmove(map.showcmd, map.showcmd + overflow, old - overflow + 1);
+    len = keyval_to_string(keyval, state, string);
+
+    /* translate iso left tab to shift tab */
+    if (keyval == GDK_ISO_Left_Tab) {
+        keyval = GDK_Tab;
+        state |= GDK_SHIFT_MASK;
+    }
+
+    if (len == 0 || len == 1) {
+        for (int i = 0; i < LENGTH(special_keys); i++) {
+            if (special_keys[i].keyval == keyval
+                && (special_keys[i].state == 0 || state & special_keys[i].state)
+            ) {
+                state &= ~special_keys[i].state;
+                string[0] = CSI;
+                string[1] = special_keys[i].one;
+                string[2] = special_keys[i].two;
+                len = 3;
+                break;
+            }
         }
-        strcat(map.showcmd, translated);
-    } else {
-        map.showcmd[0] = '\0';
     }
-#ifndef TESTLIB
-    /* show the typed keys */
-    gtk_label_set_text(GTK_LABEL(vb.gui.statusbar.cmd), map.showcmd);
-#endif
+
+    if (len == 0) {
+        /* mark all unknown key events as unhandled to not break some gtk features
+         * like <S-Einf> to copy clipboard content into inputbox */
+        return FALSE;
+    }
+
+    /* set flag to notify that the key was typed by the user */
+    c->state.typed         = TRUE;
+    c->state.processed_key = TRUE;
+
+    map_handle_keys(c, string, len, TRUE);
+
+    /* reset the typed flag */
+    c->state.typed = FALSE;
+
+    return c->state.processed_key;
 }
 
 /**
- * Translate a singe char into a readable representation to be show to the
- * user in status bar.
+ * Translate given key string into a internal representation <cr> -> \n.
+ * The len of the translated key sequence is put into given *len pointer.
  */
-static char *transchar(int c)
+static char *convert_keylabel(const char *in, int inlen, int *len)
 {
-    static char trans[5];
-    int i = 0;
-    if (VB_IS_CTRL(c)) {
-        trans[i++] = '^';
-        trans[i++] = CTRL(c);
-    } else if ((unsigned)c >= 0x80) {
-        trans[i++] = '<';
-        trans[i++] = NR2HEX((unsigned)c >> 4);
-        trans[i++] = NR2HEX((unsigned)c);
-        trans[i++] = '>';
-    } else {
-        trans[i++] = c;
+    for (int i = 0; i < LENGTH(key_labels); i++) {
+        if (inlen == key_labels[i].len
+            && !strncmp(key_labels[i].label, in, inlen)
+        ) {
+            *len = key_labels[i].chlen;
+            return key_labels[i].ch;
+        }
     }
-    trans[i++] = '\0';
+    *len = 0;
 
-    return trans;
+    return NULL;
 }
 
-static gboolean map_delete_by_lhs(const char *lhs, int len, char mode)
+/**
+ * Converts a keysequence into a internal raw keysequence.
+ * Returned keyseqence must be freed if not used anymore.
+ */
+static char *convert_keys(const char *in, int inlen, int *len)
 {
-    for (GSList *l = map.list; l != NULL; l = l->next) {
-        Map *m = (Map*)l->data;
+    int symlen, rawlen;
+    char *dest;
+    char ch[1];
+    const char *p, *raw;
+    GString *str = g_string_new("");
 
-        /* remove only if the map's lhs matches the given key sequence */
-        if (m->mode == mode && m->inlen == len && !strcmp(m->in, lhs)) {
-            /* remove the found list item */
-            map.list = g_slist_delete_link(map.list, l);
-            free_map(m);
-            return true;
+    *len = 0;
+    for (p = in; p < &in[inlen]; p++) {
+        /* if it starts not with < we can add it literally */
+        if (*p != '<') {
+            g_string_append_len(str, p, 1);
+            *len += 1;
+            continue;
         }
+
+        /* search matching > of symbolic name */
+        symlen = 1;
+        do {
+            if (&p[symlen] == &in[inlen]
+                || p[symlen] == '<'
+                || p[symlen] == ' '
+            ) {
+                break;
+            }
+        } while (p[symlen++] != '>');
+
+        raw    = NULL;
+        rawlen = 0;
+        /* check if we found a real keylabel */
+        if (p[symlen - 1] == '>') {
+            if (symlen == 5 && p[2] == '-') {
+                /* is it a <C-X> */
+                if (p[1] == 'C') {
+                    if (VB_IS_UPPER(p[3])) {
+                        ch[0]  = p[3] - 0x40;
+                        raw    = ch;
+                        rawlen = 1;
+                    } else if (VB_IS_LOWER(p[3])) {
+                        ch[0]  = p[3] - 0x60;
+                        raw    = ch;
+                        rawlen = 1;
+                    }
+                }
+            }
+
+            /* if we could not convert it jet - try to translate the label */
+            if (!rawlen) {
+                raw = convert_keylabel(p, symlen, &rawlen);
+            }
+        }
+
+        /* we found no known keylabel - so use the chars literally */
+        if (!rawlen) {
+            rawlen = symlen;
+            raw    = p;
+        }
+
+        /* write the converted keylabel into the buffer */
+        g_string_append_len(str, raw, rawlen);
+
+        /* move p after the keylabel */
+        p += symlen - 1;
+
+        *len += rawlen;
     }
-    return false;
+    dest = str->str;
+
+    /* don't free the character data of the GString */
+    g_string_free(str, FALSE);
+
+    return dest;
+}
+
+/**
+ * Timeout function to signalize a key timeout to the map.
+ */
+static gboolean do_timeout(Client *c)
+{
+    /* signalize the timeout to the key handler */
+    map_handle_keys(c, (guchar*)"", 0, TRUE);
+
+    /* we return TRUE to not automatically remove the resource - this is
+     * required to prevent critical error when we remove the source in
+     * map_handle_keys where we don't know if the timeout was called or not */
+    return TRUE;
+}
+
+static void free_map(Map *map)
+{
+    g_free(map->in);
+    g_free(map->mapped);
+    g_slice_free(Map, map);
 }
 
 /**
@@ -505,6 +554,76 @@ static int keyval_to_string(guint keyval, guint state, guchar *string)
     return len;
 }
 
+static gboolean map_delete_by_lhs(Client *c, const char *lhs, int len, char mode)
+{
+    for (GSList *l = c->map.list; l != NULL; l = l->next) {
+        Map *m = (Map*)l->data;
+
+        /* remove only if the map's lhs matches the given key sequence */
+        if (m->mode == mode && m->inlen == len && !strcmp(m->in, lhs)) {
+            /* remove the found list item */
+            c->map.list = g_slist_delete_link(c->map.list, l);
+            free_map(m);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/**
+ * Put the given char onto the show command buffer.
+ */
+static void showcmd(Client *c, int ch)
+{
+    char *translated;
+    int old, extra, overflow;
+
+    if (ch) {
+        translated = transchar(ch);
+        old        = strlen(c->map.showcmd);
+        extra      = strlen(translated);
+        overflow   = old + extra - SHOWCMD_LEN;
+        if (overflow > 0) {
+            memmove(c->map.showcmd, c->map.showcmd + overflow, old - overflow + 1);
+        }
+        strcat(c->map.showcmd, translated);
+    } else {
+        c->map.showcmd[0] = '\0';
+    }
+#ifndef TESTLIB
+    /* show the typed keys */
+    gtk_label_set_text(GTK_LABEL(c->statusbar.cmd), c->map.showcmd);
+#endif
+}
+
+/**
+ * Translate a singe char into a readable representation to be show to the
+ * user in status bar.
+ */
+static char *transchar(int c)
+{
+    static char trans[5];
+    int i = 0;
+
+    if (VB_IS_CTRL(c)) {
+        trans[i++] = '^';
+        trans[i++] = CTRL(c);
+    } else if ((unsigned)c >= 0x80) {
+/* convert the lower 4 bits of byte n to its hex character */
+#define NR2HEX(n)   (n & 0xf) <= 9 ? (n & 0xf) + '0' : (c & 0xf) - 10 + 'a'
+        trans[i++] = '<';
+        trans[i++] = NR2HEX((unsigned)c >> 4);
+        trans[i++] = NR2HEX((unsigned)c);
+        trans[i++] = '>';
+#undef NR2HEX
+    } else {
+        trans[i++] = c;
+    }
+    trans[i++] = '\0';
+
+    return trans;
+}
+
 static int utf_char2bytes(guint c, guchar *buf)
 {
     if (c < 0x80) {
@@ -545,122 +664,3 @@ static int utf_char2bytes(guint c, guchar *buf)
     buf[5] = 0x80 + (c & 0x3f);
     return 6;
 }
-
-/**
- * Converts a keysequence into a internal raw keysequence.
- * Returned keyseqence must be freed if not used anymore.
- */
-static char *convert_keys(const char *in, int inlen, int *len)
-{
-    int symlen, rawlen;
-    char *dest;
-    char ch[1];
-    const char *p, *raw;
-    GString *str = g_string_new("");
-
-    *len = 0;
-    for (p = in; p < &in[inlen]; p++) {
-        /* if it starts not with < we can add it literally */
-        if (*p != '<') {
-            g_string_append_len(str, p, 1);
-            *len += 1;
-            continue;
-        }
-
-        /* search matching > of symbolic name */
-        symlen = 1;
-        do {
-            if (&p[symlen] == &in[inlen]
-                || p[symlen] == '<'
-                || p[symlen] == ' '
-            ) {
-                break;
-            }
-        } while (p[symlen++] != '>');
-
-        raw    = NULL;
-        rawlen = 0;
-        /* check if we found a real keylabel */
-        if (p[symlen - 1] == '>') {
-            if (symlen == 5 && p[2] == '-') {
-                /* is it a <C-X> */
-                if (p[1] == 'C') {
-                    if (VB_IS_UPPER(p[3])) {
-                        ch[0]  = p[3] - 0x40;
-                        raw    = ch;
-                        rawlen = 1;
-                    } else if (VB_IS_LOWER(p[3])) {
-                        ch[0]  = p[3] - 0x60;
-                        raw    = ch;
-                        rawlen = 1;
-                    }
-                }
-            }
-
-            /* if we could not convert it jet - try to translate the label */
-            if (!rawlen) {
-                raw = convert_keylabel(p, symlen, &rawlen);
-            }
-        }
-
-        /* we found no known keylabel - so use the chars literally */
-        if (!rawlen) {
-            rawlen = symlen;
-            raw    = p;
-        }
-
-        /* write the converted keylabel into the buffer */
-        g_string_append_len(str, raw, rawlen);
-
-        /* move p after the keylabel */
-        p += symlen - 1;
-
-        *len += rawlen;
-    }
-    dest = str->str;
-
-    /* don't free the character data of the GString */
-    g_string_free(str, false);
-
-    return dest;
-}
-
-/**
- * Translate given key string into a internal representation <cr> -> \n.
- * The len of the translated key sequence is put into given *len pointer.
- */
-static char *convert_keylabel(const char *in, int inlen, int *len)
-{
-    for (int i = 0; i < LENGTH(key_labels); i++) {
-        if (inlen == key_labels[i].len
-            && !strncmp(key_labels[i].label, in, inlen)
-        ) {
-            *len = key_labels[i].chlen;
-            return key_labels[i].ch;
-        }
-    }
-    *len = 0;
-
-    return NULL;
-}
-
-/**
- * Timeout function to signalize a key timeout to the map.
- */
-static gboolean do_timeout(gpointer data)
-{
-    /* signalize the timeout to the key handler */
-    map_handle_keys((guchar*)"", 0, true);
-
-    /* we return true to not automatically remove the resource - this is
-     * required to prevent critical error when we remove the source in
-     * map_handle_keys where we don't know if the timeout was called or not */
-    return true;
-}
-
-static void free_map(Map *map)
-{
-    g_free(map->in);
-    g_free(map->mapped);
-    g_slice_free(Map, map);
-}
index 0336666..4d5308a 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -26,12 +26,12 @@ typedef enum {
     MAP_NOMATCH
 } MapState;
 
-void map_init(void);
-void map_cleanup(void);
-gboolean map_keypress(GtkWidget *widget, GdkEventKey* event, gpointer data);
-MapState map_handle_keys(const guchar *keys, int keylen, gboolean use_map);
-void map_handle_string(const char *str, gboolean use_map);
-void map_insert(const char *in, const char *mapped, char mode, gboolean remap);
-gboolean map_delete(const char *in, char mode);
+void map_init(Client *c);
+void map_cleanup(Client *c);
+MapState map_handle_keys(Client *c, const guchar *keys, int keylen, gboolean use_map);
+void map_handle_string(Client *c, const char *str, gboolean use_map);
+void map_insert(Client *c, const char *in, const char *mapped, char mode, gboolean remap);
+gboolean map_delete(Client *c, const char *in, char mode);
+gboolean on_map_keypress(GtkWidget *widget, GdkEventKey* event, Client *c);
 
 #endif /* end of include guard: _MAP_H */
index 54548a1..2980ecf 100644 (file)
  */
 
 #include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include "ascii.h"
+#include "command.h"
 #include "config.h"
+#include "ext-proxy.h"
 #include "main.h"
 #include "normal.h"
-#include "ascii.h"
-#include "command.h"
-#include "hints.h"
-#include "dom.h"
-#include "history.h"
+#include "scripts/scripts.h"
 #include "util.h"
 
 typedef enum {
@@ -47,32 +48,31 @@ typedef struct NormalCmdInfo_s {
 
 static NormalCmdInfo info = {0, '\0', '\0', PHASE_START};
 
-typedef VbResult (*NormalCommand)(const NormalCmdInfo *info);
-
-static VbResult normal_clear_input(const NormalCmdInfo *info);
-static VbResult normal_descent(const NormalCmdInfo *info);
-static VbResult normal_ex(const NormalCmdInfo *info);
-static VbResult normal_focus_input(const NormalCmdInfo *info);
-static VbResult normal_g_cmd(const NormalCmdInfo *info);
-static VbResult normal_hint(const NormalCmdInfo *info);
-static VbResult normal_do_hint(const char *prompt);
-static VbResult normal_increment_decrement(const NormalCmdInfo *info);
-static VbResult normal_input_open(const NormalCmdInfo *info);
-static VbResult normal_mark(const NormalCmdInfo *info);
-static VbResult normal_navigate(const NormalCmdInfo *info);
-static VbResult normal_open_clipboard(const NormalCmdInfo *info);
-static VbResult normal_open(const NormalCmdInfo *info);
-static VbResult normal_pass(const NormalCmdInfo *info);
-static VbResult normal_prevnext(const NormalCmdInfo *info);
-static VbResult normal_queue(const NormalCmdInfo *info);
-static VbResult normal_quit(const NormalCmdInfo *info);
-static VbResult normal_scroll(const NormalCmdInfo *info);
-static VbResult normal_search(const NormalCmdInfo *info);
-static VbResult normal_search_selection(const NormalCmdInfo *info);
-static VbResult normal_view_inspector(const NormalCmdInfo *info);
-static VbResult normal_view_source(const NormalCmdInfo *info);
-static VbResult normal_yank(const NormalCmdInfo *info);
-static VbResult normal_zoom(const NormalCmdInfo *info);
+typedef VbResult (*NormalCommand)(Client *c, const NormalCmdInfo *info);
+
+static VbResult normal_clear_input(Client *c, const NormalCmdInfo *info);
+static VbResult normal_descent(Client *c, const NormalCmdInfo *info);
+static VbResult normal_ex(Client *c, const NormalCmdInfo *info);
+static VbResult normal_g_cmd(Client *c, const NormalCmdInfo *info);
+static VbResult normal_hint(Client *c, const NormalCmdInfo *info);
+static VbResult normal_do_hint(Client *c, const char *prompt);
+static VbResult normal_increment_decrement(Client *c, const NormalCmdInfo *info);
+static VbResult normal_input_open(Client *c, const NormalCmdInfo *info);
+static VbResult normal_mark(Client *c, const NormalCmdInfo *info);
+static VbResult normal_navigate(Client *c, const NormalCmdInfo *info);
+static VbResult normal_open_clipboard(Client *c, const NormalCmdInfo *info);
+static VbResult normal_open(Client *c, const NormalCmdInfo *info);
+static VbResult normal_pass(Client *c, const NormalCmdInfo *info);
+static VbResult normal_prevnext(Client *c, const NormalCmdInfo *info);
+static VbResult normal_queue(Client *c, const NormalCmdInfo *info);
+static VbResult normal_quit(Client *c, const NormalCmdInfo *info);
+static VbResult normal_scroll(Client *c, const NormalCmdInfo *info);
+static VbResult normal_search(Client *c, const NormalCmdInfo *info);
+static VbResult normal_search_selection(Client *c, const NormalCmdInfo *info);
+static VbResult normal_view_inspector(Client *c, const NormalCmdInfo *info);
+static VbResult normal_view_source(Client *c, const NormalCmdInfo *info);
+static VbResult normal_yank(Client *c, const NormalCmdInfo *info);
+static VbResult normal_zoom(Client *c, const NormalCmdInfo *info);
 
 static struct {
     NormalCommand func;
@@ -171,7 +171,7 @@ static struct {
 /* [   0x5b */ {normal_prevnext},
 /* \   0x5c */ {NULL},
 /* ]   0x5d */ {normal_prevnext},
-/* ^   0x5e */ {normal_scroll},
+/* ^   0x5e */ {NULL},
 /* _   0x5f */ {NULL},
 /* `   0x60 */ {NULL},
 /* a   0x61 */ {NULL},
@@ -207,34 +207,33 @@ static struct {
 /* DEL 0x7f */ {NULL},
 };
 
-extern VbCore vb;
+extern struct Vimb vb;
 
 /**
  * Function called when vimb enters the normal mode.
  */
-void normal_enter(void)
+void normal_enter(Client *c)
 {
-    dom_clear_focus(vb.gui.webview);
+    webkit_web_view_run_javascript(c->webview, "document.activeElement.blur();", NULL, NULL, NULL);
     /* Make sure that when the browser area becomes visible, it will get mouse
      * and keyboard events */
-    gtk_widget_grab_focus(GTK_WIDGET(vb.gui.webview));
-    hints_clear();
+    gtk_widget_grab_focus(GTK_WIDGET(c->webview));
+    /* TODO clear possible active hints */
 }
 
 /**
  * Called when the normal mode is left.
  */
-void normal_leave(void)
+void normal_leave(Client *c)
 {
-    command_search(&((Arg){0}));
+    command_search(c, &((Arg){0}));
 }
 
 /**
  * Handles the keypress events from webview and inputbox.
  */
-VbResult normal_keypress(int key)
+VbResult normal_keypress(Client *c, int key)
 {
-    State *s = &vb.state;
     VbResult res;
 
     switch (info.phase) {
@@ -248,10 +247,10 @@ VbResult normal_keypress(int key)
                 /* handle commands that needs additional char */
                 info.phase      = PHASE_KEY2;
                 info.key        = key;
-                vb.mode->flags |= FLAG_NOMAP;
+                c->mode->flags |= FLAG_NOMAP;
             } else if (key == '"') {
                 info.phase      = PHASE_REG;
-                vb.mode->flags |= FLAG_NOMAP;
+                c->mode->flags |= FLAG_NOMAP;
             } else {
                 info.key   = key;
                 info.phase = PHASE_COMPLETE;
@@ -264,7 +263,7 @@ VbResult normal_keypress(int key)
             /* hinting g; mode requires a third key */
             if (info.key == 'g' && info.key2 == ';') {
                 info.phase      = PHASE_KEY3;
-                vb.mode->flags |= FLAG_NOMAP;
+                c->mode->flags |= FLAG_NOMAP;
             } else {
                 info.phase = PHASE_COMPLETE;
             }
@@ -276,7 +275,7 @@ VbResult normal_keypress(int key)
             break;
 
         case PHASE_REG:
-            if (strchr(VB_REG_CHARS, key)) {
+            if (strchr(REG_CHARS, key)) {
                 info.reg   = key;
                 info.phase = PHASE_START;
             } else {
@@ -292,10 +291,10 @@ VbResult normal_keypress(int key)
         /* TODO allow more commands - some that are looked up via command key
          * direct and those that are searched via binary search */
         if ((guchar)info.key <= LENGTH(commands) && commands[(guchar)info.key].func) {
-            res = commands[(guchar)info.key].func(&info);
+            res = commands[(guchar)info.key].func(c, &info);
         } else {
             /* let gtk handle the keyevent if we have no command attached to it */
-            s->processed_key = false;
+            c->state.processed_key = FALSE;
             res = RESULT_COMPLETE;
         }
 
@@ -312,51 +311,48 @@ VbResult normal_keypress(int key)
 /**
  * Function called when vimb enters the passthrough mode.
  */
-void pass_enter(void)
+void pass_enter(Client *c)
 {
-    vb_update_mode_label("-- PASS THROUGH --");
+    vb_modelabel_update(c, "-- PASS THROUGH --");
 }
 
 /**
  * Called when passthrough mode is left.
  */
-void pass_leave(void)
+void pass_leave(Client *c)
 {
-    vb_update_mode_label("");
+    vb_modelabel_update(c, "");
 }
 
-VbResult pass_keypress(int key)
+VbResult pass_keypress(Client *c, int key)
 {
     if (key == CTRL('[')) { /* esc */
-        vb_enter('n');
+        vb_enter(c, 'n');
     }
-    vb.state.processed_key = false;
+    c->state.processed_key = FALSE;
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_clear_input(const NormalCmdInfo *info)
+static VbResult normal_clear_input(Client *c, const NormalCmdInfo *info)
 {
-    /* if there's a text selection, deselect it */
-    char *clipboard_text = gtk_clipboard_wait_for_text(PRIMARY_CLIPBOARD());
-    gtk_clipboard_clear(PRIMARY_CLIPBOARD());
-    if (clipboard_text) {
-        gtk_clipboard_set_text(PRIMARY_CLIPBOARD(), clipboard_text, -1);
-    }
-    g_free(clipboard_text);
+    gtk_widget_grab_focus(GTK_WIDGET(c->webview));
 
-    gtk_widget_grab_focus(GTK_WIDGET(vb.gui.webview));
-    vb_echo(VB_MSG_NORMAL, false, "");
-    command_search(&((Arg){0}));
+    /* Clear the inputbox and change the style to normal to reset also the
+     * possible colored error background. */
+    vb_echo(c, MSG_NORMAL, FALSE, "");
+
+    /* Unset search highlightning. */
+    command_search(c, &((Arg){0}));
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_descent(const NormalCmdInfo *info)
+static VbResult normal_descent(Client *c, const NormalCmdInfo *info)
 {
     int count = info->count ? info->count : 1;
     const char *uri, *p = NULL, *domain = NULL;
 
-    uri = vb.state.uri;
+    uri = c->state.uri;
 
     /* get domain part */
     if (!uri || !*uri
@@ -396,73 +392,68 @@ static VbResult normal_descent(const NormalCmdInfo *info)
         p = domain;
     }
 
-    Arg a = {VB_TARGET_CURRENT};
+    Arg a = {TARGET_CURRENT};
     a.s   = g_strndup(uri, p - uri + 1);
 
-    vb_load_uri(&a);
+    vb_load_uri(c, &a);
     g_free(a.s);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_ex(const NormalCmdInfo *info)
+static VbResult normal_ex(Client *c, const NormalCmdInfo *info)
 {
     if (info->key == 'F') {
-        vb_enter_prompt('c', ";t", true);
+        vb_enter_prompt(c, 'c', ";t", TRUE);
     } else if (info->key == 'f') {
-        vb_enter_prompt('c', ";o", true);
+        vb_enter_prompt(c, 'c', ";o", TRUE);
     } else {
         char prompt[2] = {info->key, '\0'};
-        vb_enter_prompt('c', prompt, true);
+        vb_enter_prompt(c, 'c', prompt, TRUE);
     }
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_focus_input(const NormalCmdInfo *info)
-{
-    dom_focus_input(webkit_web_view_get_dom_document(vb.gui.webview));
-    return RESULT_COMPLETE;
-}
-
-static VbResult normal_g_cmd(const NormalCmdInfo *info)
+static VbResult normal_g_cmd(Client *c, const NormalCmdInfo *info)
 {
     Arg a;
     switch (info->key2) {
         case ';': {
             const char prompt[4] = {'g', ';', info->key3, 0};
 
-            return normal_do_hint(prompt);
+            return normal_do_hint(c, prompt);
         }
 
         case 'F':
-            return normal_view_inspector(info);
+            return normal_view_inspector(c, info);
 
         case 'f':
-            normal_view_source(info);
+            normal_view_source(c, info);
 
         case 'g':
-            return normal_scroll(info);
+            return normal_scroll(c, info);
 
         case 'H':
         case 'h':
-            a.i = info->key2 == 'H' ? VB_TARGET_NEW : VB_TARGET_CURRENT;
+            a.i = info->key2 == 'H' ? TARGET_NEW : TARGET_CURRENT;
             a.s = NULL;
-            vb_load_uri(&a);
+            vb_load_uri(c, &a);
             return RESULT_COMPLETE;
 
         case 'i':
-            return normal_focus_input(info);
+            ext_proxy_focus_input(c);
+            return RESULT_COMPLETE;
 
         case 'U':
         case 'u':
-            return normal_descent(info);
+            return normal_descent(c, info);
     }
 
     return RESULT_ERROR;
 }
 
-static VbResult normal_hint(const NormalCmdInfo *info)
+static VbResult normal_hint(Client *c, const NormalCmdInfo *info)
 {
     const char prompt[3] = {info->key, info->key2, 0};
 
@@ -471,92 +462,75 @@ static VbResult normal_hint(const NormalCmdInfo *info)
      * somewhere else - it's only use is for hinting. It might be better to
      * allow to set various data to the mode itself to avoid toggling
      * variables in global skope. */
-    vb.state.current_register = info->reg;
-    return normal_do_hint(prompt);
+    c->state.current_register = info->reg;
+    return normal_do_hint(c, prompt);
 }
 
-static VbResult normal_do_hint(const char *prompt)
+static VbResult normal_do_hint(Client *c, const char *prompt)
 {
-    /* check if this is a valid hint mode */
-    if (!hints_parse_prompt(prompt, NULL, NULL)) {
-        return RESULT_ERROR;
-    }
+    /* TODO check if the prompt is of a valid hint mode */
 
-    vb_enter_prompt('c', prompt, true);
+    vb_enter_prompt(c, 'c', prompt, TRUE);
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_increment_decrement(const NormalCmdInfo *info)
+static VbResult normal_increment_decrement(Client *c, const NormalCmdInfo *info)
 {
+    char *js;
     int count = info->count ? info->count : 1;
-    hints_increment_uri(info->key == CTRL('A') ? count : -count);
+
+    js = g_strdup_printf(INCREMENT_URI_NUMBER, info->key == CTRL('A') ? count : -count);
+    webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL);
+    g_free(js);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_input_open(const NormalCmdInfo *info)
+static VbResult normal_input_open(Client *c, const NormalCmdInfo *info)
 {
     if (strchr("ot", info->key)) {
-        vb_set_input_text(info->key == 't' ? ":tabopen " : ":open ");
+        vb_input_set_text(c, info->key == 't' ? ":tabopen " : ":open ");
     } else {
-        vb_echo(
-            VB_MSG_NORMAL, false,
-            ":%s %s", info->key == 'T' ? "tabopen" : "open", vb.state.uri
-        );
+        vb_echo(c, MSG_NORMAL, FALSE,
+                ":%s %s", info->key == 'T' ? "tabopen" : "open", c->state.uri);
     }
     /* switch mode after setting the input text to not trigger the
      * commands modes input change handler */
-    vb_enter_prompt('c', ":", false);
+    vb_enter_prompt(c, 'c', ":", FALSE);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_mark(const NormalCmdInfo *info)
+static VbResult normal_mark(Client *c, const NormalCmdInfo *info)
 {
-    gdouble current;
-    char *mark;
-    int  idx;
-
-    /* check if the second char is a valid mark char */
-    if (!(mark = strchr(VB_MARK_CHARS, info->key2))) {
-        return RESULT_ERROR;
-    }
-
-    /* get the index of the mark char */
-    idx = mark - VB_MARK_CHARS;
-
-    if ('m' == info->key) {
-        vb.state.marks[idx] = gtk_adjustment_get_value(vb.gui.adjust_v);
-    } else {
-        /* check if the mark was set */
-        if ((int)(vb.state.marks[idx] - .5) < 0) {
-            return RESULT_ERROR;
-        }
-
-        current = gtk_adjustment_get_value(vb.gui.adjust_v);
-
-        /* jump to the location */
-        gtk_adjustment_set_value(vb.gui.adjust_v, vb.state.marks[idx]);
-
-        /* save previous adjust as last position */
-        vb.state.marks[VB_MARK_TICK] = current;
-    }
+    /* TODO implement setting of marks - we need to get the position in the pagee from the Webextension */
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_navigate(const NormalCmdInfo *info)
+static VbResult normal_navigate(Client *c, const NormalCmdInfo *info)
 {
     int count;
+    WebKitBackForwardList *list;
+    WebKitBackForwardListItem *item;
 
-    WebKitWebView *view = vb.gui.webview;
+    WebKitWebView *view = c->webview;
     switch (info->key) {
         case CTRL('I'): /* fall through */
         case CTRL('O'):
             count = info->count ? info->count : 1;
             if (info->key == CTRL('O')) {
-                count *= -1;
+                if (webkit_web_view_can_go_back(view)) {
+                    list = webkit_web_view_get_back_forward_list(view);
+                    item = webkit_back_forward_list_get_nth_item(list, -1 * count);
+                    webkit_web_view_go_to_back_forward_list_item(view, item);
+                }
+            } else {
+                if (webkit_web_view_can_go_forward(view)) {
+                    list = webkit_web_view_get_back_forward_list(view);
+                    item = webkit_back_forward_list_get_nth_item(list, count);
+                    webkit_web_view_go_to_back_forward_list_item(view, item);
+                }
             }
-            webkit_web_view_go_back_or_forward(view, count);
             break;
 
         case 'r':
@@ -575,23 +549,23 @@ static VbResult normal_navigate(const NormalCmdInfo *info)
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_open_clipboard(const NormalCmdInfo *info)
+static VbResult normal_open_clipboard(Client *c, const NormalCmdInfo *info)
 {
-    Arg a = {info->key == 'P' ? VB_TARGET_NEW : VB_TARGET_CURRENT};
+    Arg a = {info->key == 'P' ? TARGET_NEW : TARGET_CURRENT};
 
     /* if register is not the default - read out of the internal register */
     if (info->reg) {
-        a.s = g_strdup(vb_register_get(info->reg));
+        a.s = g_strdup(vb_register_get(c, info->reg));
     } else {
         /* if no register is given use the system clipboard */
-        a.s = gtk_clipboard_wait_for_text(PRIMARY_CLIPBOARD());
+        a.s = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
         if (!a.s) {
-            a.s = gtk_clipboard_wait_for_text(SECONDARY_CLIPBOARD());
+            a.s = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_NONE));
         }
     }
 
     if (a.s) {
-        vb_load_uri(&a);
+        vb_load_uri(c, &a);
         g_free(a.s);
 
         return RESULT_COMPLETE;
@@ -600,155 +574,155 @@ static VbResult normal_open_clipboard(const NormalCmdInfo *info)
     return RESULT_ERROR;
 }
 
-static VbResult normal_open(const NormalCmdInfo *info)
+/**
+ * Open the last closed page.
+ */
+static VbResult normal_open(Client *c, const NormalCmdInfo *info)
 {
-    char *uri;
     Arg a;
+    char *file;
 
-    uri = util_file_pop_line(vb.files[FILES_CLOSED], NULL);
-    if (!uri) {
-        return RESULT_ERROR;
-    }
+    file = g_build_filename(util_get_config_dir(), FILE_CLOSED, NULL);
+    a.i = info->key == 'U' ? TARGET_NEW : TARGET_CURRENT;
+    a.s = util_get_file_contents(file, NULL);
+    g_free(file);
 
-    /* open last closed */
-    a.i = info->key == 'U' ? VB_TARGET_NEW : VB_TARGET_CURRENT;
-    a.s = uri;
-    vb_load_uri(&a);
-    g_free(uri);
+    vb_load_uri(c, &a);
+    g_free(a.s);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_pass(const NormalCmdInfo *info)
+static VbResult normal_pass(Client *c, const NormalCmdInfo *info)
 {
-    vb_enter('p');
+    vb_enter(c, 'p');
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_prevnext(const NormalCmdInfo *info)
+static VbResult normal_prevnext(Client *c, const NormalCmdInfo *info)
 {
+#if 0 /* TODO need hinting to be available */
     int count = info->count ? info->count : 1;
     if (info->key2 == ']') {
-        hints_follow_link(false, count);
+        hints_follow_link(FALSE, count);
     } else if (info->key2 == '[') {
-        hints_follow_link(true, count);
+        hints_follow_link(TRUE, count);
     } else {
         return RESULT_ERROR;
     }
+#endif
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_queue(const NormalCmdInfo *info)
+static VbResult normal_queue(Client *c, const NormalCmdInfo *info)
 {
-#ifdef FEATURE_QUEUE
-    command_queue(&((Arg){COMMAND_QUEUE_POP}));
-#endif
+    /* TODO run next uri from queu and remove it from */
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_quit(const NormalCmdInfo *info)
+static VbResult normal_quit(Client *c, const NormalCmdInfo *info)
 {
-    vb_quit(false);
+    vb_quit(c, FALSE);
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_scroll(const NormalCmdInfo *info)
+static VbResult normal_scroll(Client *c, const NormalCmdInfo *info)
 {
-    GtkAdjustment *adjust;
-    gdouble value, max, new;
-    int count = info->count ? info->count : 1;
+    int x = 0, y = 0, page_height = 0, count = info->count ? info->count : 1;
+    char *js;
+    GtkAllocation alloc;
+
+    /* The overall page height is only required for the <C-*> commands. */
+    if (VB_IS_CTRL(info->key)) {
+        gtk_widget_get_allocation(GTK_WIDGET(c->webview), &alloc);
+        page_height = (int)alloc.height;
+    }
 
-    /* TODO split this into more functions - reduce similar code */
     switch (info->key) {
-        case 'h':
-            adjust = vb.gui.adjust_h;
-            value  = vb.config.scrollstep;
-            new    = gtk_adjustment_get_value(adjust) - value * count;
-            break;
         case 'j':
-            adjust = vb.gui.adjust_v;
-            value  = vb.config.scrollstep;
-            new    = gtk_adjustment_get_value(adjust) + value * count;
+            y = count * c->config.scrollstep;
+            break;
+        case 'h':
+            x = -count * c->config.scrollstep;
             break;
         case 'k':
-            adjust = vb.gui.adjust_v;
-            value  = vb.config.scrollstep;
-            new    = gtk_adjustment_get_value(adjust) - value * count;
+            y = -count * c->config.scrollstep;
             break;
         case 'l':
-            adjust = vb.gui.adjust_h;
-            value  = vb.config.scrollstep;
-            new    = gtk_adjustment_get_value(adjust) + value * count;
+            x = count * c->config.scrollstep;
             break;
         case CTRL('D'):
-            adjust = vb.gui.adjust_v;
-            value  = gtk_adjustment_get_page_size(adjust) / 2;
-            new    = gtk_adjustment_get_value(adjust) + value * count;
+            y = count * page_height / 2;
             break;
         case CTRL('U'):
-            adjust = vb.gui.adjust_v;
-            value  = gtk_adjustment_get_page_size(adjust) / 2;
-            new    = gtk_adjustment_get_value(adjust) - value * count;
+            y = -count * page_height / 2;
             break;
         case CTRL('F'):
-            adjust = vb.gui.adjust_v;
-            value  = gtk_adjustment_get_page_size(adjust);
-            new    = gtk_adjustment_get_value(adjust) + value * count;
+            y = count * page_height;
             break;
         case CTRL('B'):
-            adjust = vb.gui.adjust_v;
-            value  = gtk_adjustment_get_page_size(adjust);
-            new    = gtk_adjustment_get_value(adjust) - value * count;
+            y = -count * page_height;
             break;
         case 'G':
-            adjust = vb.gui.adjust_v;
-            max    = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
-            new    = info->count ? (max * info->count / 100) : gtk_adjustment_get_upper(adjust);
-            /* save the position to mark ' */
-            vb.state.marks[VB_MARK_TICK] = gtk_adjustment_get_value(adjust);
-            break;
-        case '0': /* fall through */
-        case '^':
-            adjust = vb.gui.adjust_h;
-            new    = gtk_adjustment_get_lower(adjust);
-            break;
-        case '$':
-            adjust = vb.gui.adjust_h;
-            new    = gtk_adjustment_get_upper(adjust);
-            break;
+            if (info->count) {
+                js = g_strdup_printf(
+                        "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));",
+                        info->count);
+                webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL);
+                g_free(js);
+                return RESULT_COMPLETE;
+            }
 
+            /* Without count scroll to the end of the page. */
+            webkit_web_view_run_javascript(c->webview, "window.scroll(window.scrollX, document.body.scrollHeight);", NULL, NULL, NULL);
+            return RESULT_COMPLETE;
+        case '0':
+            webkit_web_view_run_javascript(c->webview, "window.scroll(0, window.scrollY);", NULL, NULL, NULL);
+            return RESULT_COMPLETE;
+        case '$':
+            webkit_web_view_run_javascript(c->webview, "window.scroll(document.body.scrollWidth, window.scrollY);", NULL, NULL, NULL);
+            return RESULT_COMPLETE;
         default:
             if (info->key2 == 'g') {
-                adjust = vb.gui.adjust_v;
-                max    = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
-                new    = info->count ? (max * info->count / 100) : gtk_adjustment_get_lower(adjust);
-                break;
+                if (info->count) {
+                    js = g_strdup_printf(
+                            "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));",
+                            info->count);
+                    webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL);
+                    g_free(js);
+                    return RESULT_COMPLETE;
+                }
+                /* Without count gg scrolls to the top of the page. */
+                webkit_web_view_run_javascript(c->webview, "window.scroll(window.scrollX, 0);", NULL, NULL, NULL);
+                return RESULT_COMPLETE;
             }
             return RESULT_ERROR;
     }
-    max = gtk_adjustment_get_upper(adjust) - gtk_adjustment_get_page_size(adjust);
-    gtk_adjustment_set_value(adjust, new > max ? max : new);
+    js = g_strdup_printf("window.scrollBy(%d,%d);", x, y);
+    webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL);
+    g_free(js);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_search(const NormalCmdInfo *info)
+static VbResult normal_search(Client *c, const NormalCmdInfo *info)
 {
     int count = (info->count > 0) ? info->count : 1;
 
-    command_search(&((Arg){info->key == 'n' ? count : -count}));
+    command_search(c, &((Arg){info->key == 'n' ? count : -count}));
+
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_search_selection(const NormalCmdInfo *info)
+static VbResult normal_search_selection(Client *c, const NormalCmdInfo *info)
 {
     int count;
     char *query;
 
     /* there is no function to get the selected text so we copy current
      * selection to clipboard */
-    webkit_web_view_copy_clipboard(vb.gui.webview);
-    query = gtk_clipboard_wait_for_text(PRIMARY_CLIPBOARD());
+    webkit_web_view_execute_editing_command(c->webview, WEBKIT_EDITING_COMMAND_COPY);
+    query = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
     if (!query) {
         return RESULT_ERROR;
     }
@@ -756,52 +730,54 @@ static VbResult normal_search_selection(const NormalCmdInfo *info)
 
     /* stopp possible existing search and the search highlights before
      * starting the new search query */
-    command_search(&((Arg){0}));
-    command_search(&((Arg){info->key == '*' ? count : -count, query}));
+    command_search(c, &((Arg){0}));
+    command_search(c, &((Arg){info->key == '*' ? count : -count, query}));
     g_free(query);
 
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_view_inspector(const NormalCmdInfo *info)
+static VbResult normal_view_inspector(Client *c, const NormalCmdInfo *info)
 {
-    gboolean enabled;
-    WebKitWebSettings *settings = webkit_web_view_get_settings(vb.gui.webview);
+    WebKitWebInspector *inspector;
+    WebKitSettings *settings;
+
+    settings  = webkit_web_view_get_settings(c->webview);
+    inspector = webkit_web_view_get_inspector(c->webview);
+
+    /* Try to get the inspected uri to identify if the inspector is shown at
+     * the time or not. */
+    if (webkit_web_inspector_get_inspected_uri(inspector)) {
+        webkit_web_inspector_close(inspector);
+    } else if (webkit_settings_get_enable_developer_extras(settings)) {
+        webkit_web_inspector_show(inspector);
+    } else {
+        /* Inform the user on attempt to enable webinspector when the
+         * developer extra are not enabled. */
+        vb_echo(c, MSG_ERROR, TRUE, "webinspector is not enabled");
 
-    g_object_get(G_OBJECT(settings), "enable-developer-extras", &enabled, NULL);
-    if (enabled) {
-        if (vb.state.is_inspecting) {
-            webkit_web_inspector_close(vb.gui.inspector);
-        } else {
-            webkit_web_inspector_show(vb.gui.inspector);
-        }
-        return RESULT_COMPLETE;
+        return RESULT_ERROR;
     }
-
-    vb_echo(VB_MSG_ERROR, true, "webinspector is not enabled");
-    return RESULT_ERROR;
+    return RESULT_COMPLETE;
 }
 
-static VbResult normal_view_source(const NormalCmdInfo *info)
+static VbResult normal_view_source(Client *c, const NormalCmdInfo *info)
 {
-    gboolean mode = webkit_web_view_get_view_source_mode(vb.gui.webview);
-    webkit_web_view_set_view_source_mode(vb.gui.webview, !mode);
-    webkit_web_view_reload(vb.gui.webview);
+    /* TODO the source mode isn't supported anymore use external editor for this */
     return RESULT_COMPLETE;
 }
 
-static VbResult normal_yank(const NormalCmdInfo *info)
+static VbResult normal_yank(Client *c, const NormalCmdInfo *info)
 {
     Arg a = {info->key == 'Y' ? COMMAND_YANK_SELECTION : COMMAND_YANK_URI};
 
-    return command_yank(&a, info->reg) ? RESULT_COMPLETE : RESULT_ERROR;
+    return command_yank(c, &a, info->reg) ? RESULT_COMPLETE : RESULT_ERROR;
 }
 
-static VbResult normal_zoom(const NormalCmdInfo *info)
+static VbResult normal_zoom(Client *c, const NormalCmdInfo *info)
 {
-    float step, level, count;
-    WebKitWebSettings *setting;
-    WebKitWebView *view = vb.gui.webview;
+    float step = 0.1, level, count;
+    WebKitWebView *view = c->webview;
 
     /* check if the second key is allowed */
     if (!strchr("iIoOz", info->key2)) {
@@ -811,20 +787,12 @@ static VbResult normal_zoom(const NormalCmdInfo *info)
     count = info->count ? (float)info->count : 1.0;
 
     if (info->key2 == 'z') { /* zz reset zoom */
-#ifdef FEATURE_HIGH_DPI
-        /* to set the zoom for high dpi displays we need full content zoom */
-        webkit_web_view_set_full_content_zoom(view, true);
-#endif
-        webkit_web_view_set_zoom_level(view, vb.config.default_zoom);
+        webkit_web_view_set_zoom_level(view, 1.0);
 
         return RESULT_COMPLETE;
     }
 
-    level   = webkit_web_view_get_zoom_level(view);
-    setting = webkit_web_view_get_settings(view);
-    g_object_get(G_OBJECT(setting), "zoom-step", &step, NULL);
-
-    webkit_web_view_set_full_content_zoom(view, VB_IS_UPPER(info->key2));
+    level= webkit_web_view_get_zoom_level(view);
 
     /* calculate the new zoom level */
     if (info->key2 == 'i' || info->key2 == 'I') {
@@ -834,6 +802,7 @@ static VbResult normal_zoom(const NormalCmdInfo *info)
     }
 
     /* apply the new zoom level */
+    webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(view), VB_IS_LOWER(info->key2));
     webkit_web_view_set_zoom_level(view, level);
 
     return RESULT_COMPLETE;
index 710a553..e4f626c 100644 (file)
 #include "config.h"
 #include "main.h"
 
-void normal_enter(void);
-void normal_leave(void);
-VbResult normal_keypress(int key);
-void pass_enter(void);
-void pass_leave(void);
-VbResult pass_keypress(int key);
+void normal_enter(Client *c);
+void normal_leave(Client *c);
+VbResult normal_keypress(Client *c, int key);
+void pass_enter(Client *c);
+void pass_leave(Client *c);
+VbResult pass_keypress(Client *c, int key);
 
 #endif /* end of include guard: _NORMAL_H */
diff --git a/src/scripts/.gitignore b/src/scripts/.gitignore
new file mode 100644 (file)
index 0000000..763376e
--- /dev/null
@@ -0,0 +1 @@
+scripts.h
diff --git a/src/scripts/Makefile b/src/scripts/Makefile
new file mode 100644 (file)
index 0000000..32af191
--- /dev/null
@@ -0,0 +1,19 @@
+BASEDIR=../..
+include $(BASEDIR)/config.mk
+
+JSFILES = $(wildcard *.js)
+
+all: scripts.h
+
+clean:
+       $(RM) scripts.h
+
+scripts.h: $(JSFILES)
+       @echo "create $@ from *.js"
+       @echo > $@
+       @for file in $(JSFILES); do \
+               ./js2h.sh $$file >> $@; \
+       done
+
+.PHONY: all clean
+
diff --git a/src/scripts/increment_uri_number.js b/src/scripts/increment_uri_number.js
new file mode 100644 (file)
index 0000000..d90adfb
--- /dev/null
@@ -0,0 +1,16 @@
+/* TODO maybe it's better to inject this in the webview as method */
+/* and to call it if needed */
+var c = %d, on, nn, m = location.href.match(/(.*?)(\d+)(\D*)$/);
+if (m) {
+    on = m[2];
+    nn = String(Math.max(parseInt(on) + c, 0));
+    /* keep prepending zeros */
+    if (/^0/.test(on)) {
+        while (nn.length < on.length) {
+            nn = "0" + nn;
+        }
+    }
+    m[2] = nn;
+
+    location.href = m.slice(1).join("");
+}
diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh
new file mode 100755 (executable)
index 0000000..b177085
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Defined a constant for given JavaScript file. The file name is used to get
+# the constant name and the contents are minifed and escaped as the value.
+# ./js.h do_fancy_stuff.js creates somethings like
+# #define DO_FANCY_STUFF "Escaped JavaScriptCode"
+
+FILE="$1"
+
+# Check if the file exists.
+if [ ! -r "$FILE" ]; then
+    echo "File $FILE does not exist or is not readable" >&2
+    exit 1
+fi
+
+# Remove the .js file extension and turn all chars to upper case to get the
+# constant name.
+CONSTANT=$(echo "$FILE" | sed 's:.js$::g' | tr a-z A-Z)
+
+# minify the script
+cat $FILE | \
+# removove single line comments
+sed -e 's|^//.*$||g' | \
+# remove linebreaks
+tr '\n\r' ' ' | \
+# remove unneeded whitespace
+sed -e 's:/\*[^*]*\*/::g' \
+    -e 's|[ ]\{2,\}| |g' \
+    -e 's|^ ||g' \
+    -e "s|[ ]\{0,\}\([-!?<>:=(){};+\&\"',\|]\)[ ]\{0,\}|\1|g" \
+    -e 's|"+"||g' | \
+# ecaspe
+sed -e 's|\\x20| |g' \
+    -e 's|\\|\\\\|g' \
+    -e 's|"|\\"|g' | \
+# write opener with the starting and ending quote char
+sed -e "1s/^/#define $CONSTANT \"/" \
+    -e '$s/$/"\n/'
index 9c0492f..79d3ebb 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
+#include <glib.h>
 #include <string.h>
+
+#include "completion.h"
+#include "config.h"
+#include "ext-proxy.h"
 #include "main.h"
 #include "setting.h"
 #include "shortcut.h"
-#include "handlers.h"
-#include "util.h"
-#include "completion.h"
-#include "js.h"
-#ifdef FEATURE_HSTS
-#include "hsts.h"
-#endif
-#include "arh.h"
-
-typedef enum {
-    TYPE_BOOLEAN,
-    TYPE_INTEGER,
-    TYPE_CHAR,
-    TYPE_COLOR,
-    TYPE_FONT,
-} Type;
 
 typedef enum {
     SETTING_SET,        /* :set option=value */
@@ -53,194 +41,111 @@ enum {
     FLAG_NODUP = (1<<2),    /* don't allow duplicate strings within list values */
 };
 
-extern VbCore vb;
-
-static int setting_set_value(Setting *prop, void *value, SettingType type);
+static int setting_set_value(Client *c, Setting *prop, void *value, SettingType type);
 static gboolean prepare_setting_value(Setting *prop, void *value, SettingType type, void **newvalue);
-static gboolean setting_add(const char *name, int type, void *value,
+static gboolean setting_add(Client *c, const char *name, DataType type, void *value,
     SettingFunction setter, int flags, void *data);
-static void setting_print(Setting *s);
+static void setting_print(Client *c, Setting *s);
 static void setting_free(Setting *s);
-static int webkit(const char *name, int type, void *value, void *data);
-static int pagecache(const char *name, int type, void *value, void *data);
-static int soup(const char *name, int type, void *value, void *data);
-static int internal(const char *name, int type, void *value, void *data);
-static int input_autohide(const char *name, int type, void *value, void *data);
-static int input_color(const char *name, int type, void *value, void *data);
-static int statusbar(const char *name, int type, void *value, void *data);
-static int status_color(const char *name, int type, void *value, void *data);
-static int input_font(const char *name, int type, void *value, void *data);
-static int status_font(const char *name, int type, void *value, void *data);
-gboolean setting_fill_completion(GtkListStore *store, const char *input);
-#ifdef FEATURE_COOKIE
-static int cookie_accept(const char *name, int type, void *value, void *data);
-#endif
-static int ca_bundle(const char *name, int type, void *value, void *data);
-static int proxy(const char *name, int type, void *value, void *data);
-static int user_style(const char *name, int type, void *value, void *data);
-static int headers(const char *name, int type, void *value, void *data);
-#ifdef FEATURE_ARH
-static int autoresponseheader(const char *name, int type, void *value, void *data);
-#endif
-static int prevnext(const char *name, int type, void *value, void *data);
-static int fullscreen(const char *name, int type, void *value, void *data);
-#ifdef FEATURE_HSTS
-static int hsts(const char *name, int type, void *value, void *data);
-#endif
-#ifdef FEATURE_SOUP_CACHE
-static int soup_cache(const char *name, int type, void *value, void *data);
-#endif
-static gboolean validate_js_regexp_list(const char *pattern);
 
-void setting_init()
+static int cookie_accept(Client *c, const char *name, DataType type, void *value, void *data);
+static int fullscreen(Client *c, const char *name, DataType type, void *value, void *data);
+static int input_autohide(Client *c, const char *name, DataType type, void *value, void *data);
+static int internal(Client *c, const char *name, DataType type, void *value, void *data);
+static int headers(Client *c, const char *name, DataType type, void *value, void *data);
+static int user_scripts(Client *c, const char *name, DataType type, void *value, void *data);
+static int user_style(Client *c, const char *name, DataType type, void *value, void *data);
+static int statusbar(Client *c, const char *name, DataType type, void *value, void *data);
+static int tls_policy(Client *c, const char *name, DataType type, void *value, void *data);
+static int webkit(Client *c, const char *name, DataType type, void *value, void *data);
+
+extern struct Vimb vb;
+
+
+void setting_init(Client *c)
 {
     int i;
-    gboolean on = true, off = false;
-
-    vb.config.settings = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)setting_free);
-#if WEBKIT_CHECK_VERSION(1, 7, 5)
-    setting_add("accelerated-compositing", TYPE_BOOLEAN, &off, webkit, 0, "enable-accelerated-compositing");
-#endif
-    setting_add("auto-resize-window", TYPE_BOOLEAN, &off, webkit, 0, "auto-resize-window");
-    setting_add("auto-shrink-images", TYPE_BOOLEAN, &on, webkit, 0, "auto-shrink-images");
-    setting_add("caret", TYPE_BOOLEAN, &off, webkit, 0, "enable-caret-browsing");
-    setting_add("cursivfont", TYPE_CHAR, &"serif", webkit, 0, "cursive-font-family");
-    setting_add("defaultencoding", TYPE_CHAR, &"utf-8", webkit, 0, "default-encoding");
-    setting_add("defaultfont", TYPE_CHAR, &"sans-serif", webkit, 0, "default-font-family");
-    setting_add("dns-prefetching", TYPE_BOOLEAN, &on, webkit, 0, "enable-dns-prefetching");
-    setting_add("dom-paste", TYPE_BOOLEAN, &off, webkit, 0, "enable-dom-paste");
-    setting_add("file-access-from-file-uris", TYPE_BOOLEAN, &off, webkit, 0, "enable-file-access-from-file-uris");
+    gboolean on = TRUE, off = FALSE;
+
+    /* TODO put the setting definitions into config.def.h and save them on vb
+     * struct. Use a hash table for fast name to key processing on the client.
+     * Separate the setting definition from the data.
+     * Don't set the webkit settings if they are the default on startup. */
+    c->config.settings = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)setting_free);
+    setting_add(c, "useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux i686) AppleWebKit/538.15+ (KHTML, like Gecko) " PROJECT "/" VERSION " Version/8.0 Safari/538.15", webkit, 0, "user-agent");
+    /* TODO use the real names for webkit settings */
+    i = 14;
+    setting_add(c, "fontsize", TYPE_INTEGER, &i, webkit, 0, "default-font-size");
+    setting_add(c, "caret", TYPE_BOOLEAN, &off, webkit, 0, "enable-caret-browsing");
+    setting_add(c, "cursivfont", TYPE_CHAR, &"serif", webkit, 0, "cursive-font-family");
+    setting_add(c, "default-charset", TYPE_CHAR, &"utf-8", webkit, 0, "default-charset");
+    setting_add(c, "defaultfont", TYPE_CHAR, &"sans-serif", webkit, 0, "default-font-family");
+    setting_add(c, "dns-prefetching", TYPE_BOOLEAN, &on, webkit, 0, "enable-dns-prefetching");
     i = SETTING_DEFAULT_FONT_SIZE;
-    setting_add("fontsize", TYPE_INTEGER, &i, webkit, 0, "default-font-size");
-    setting_add("frame-flattening", TYPE_BOOLEAN, &off, webkit, 0, "enable-frame-flattening");
-    setting_add("html5-database", TYPE_BOOLEAN, &on, webkit, 0, "enable-html5-database");
-    setting_add("html5-local-storage", TYPE_BOOLEAN, &on, webkit, 0, "enable-html5-local-storage");
-    setting_add("hyperlink-auditing", TYPE_BOOLEAN, &off, webkit, 0, "enable-hyperlink-auditing");
-    setting_add("images", TYPE_BOOLEAN, &on, webkit, 0, "auto-load-images");
-#if WEBKIT_CHECK_VERSION(2, 0, 0)
-    setting_add("insecure-content-show", TYPE_BOOLEAN, &off, webkit, 0, "enable-display-of-insecure-content");
-    setting_add("insecure-content-run", TYPE_BOOLEAN, &off, webkit, 0, "enable-running-of-insecure-content");
-#endif
-    setting_add("java-applet", TYPE_BOOLEAN, &on, webkit, 0, "enable-java-applet");
-    setting_add("javascript-can-access-clipboard", TYPE_BOOLEAN, &off, webkit, 0, "javascript-can-access-clipboard");
-    setting_add("javascript-can-open-windows-automatically", TYPE_BOOLEAN, &off, webkit, 0, "javascript-can-open-windows-automatically");
-    setting_add("media-playback-allows-inline", TYPE_BOOLEAN, &on, webkit, 0, "media-playback-allows-inline");
-    setting_add("media-playback-requires-user-gesture", TYPE_BOOLEAN, &off, webkit, 0, "media-playback-requires-user-gesture");
+    setting_add(c, "fontsize", TYPE_INTEGER, &i, webkit, 0, "default-font-size");
+    setting_add(c, "frame-flattening", TYPE_BOOLEAN, &off, webkit, 0, "enable-frame-flattening");
+    setting_add(c, "header", TYPE_CHAR, &"", headers, FLAG_LIST|FLAG_NODUP, "header");
+    setting_add(c, "html5-database", TYPE_BOOLEAN, &on, webkit, 0, "enable-html5-database");
+    setting_add(c, "html5-local-storage", TYPE_BOOLEAN, &on, webkit, 0, "enable-html5-local-storage");
+    setting_add(c, "hyperlink-auditing", TYPE_BOOLEAN, &off, webkit, 0, "enable-hyperlink-auditing");
+    setting_add(c, "images", TYPE_BOOLEAN, &on, webkit, 0, "auto-load-images");
+    setting_add(c, "javascript-can-access-clipboard", TYPE_BOOLEAN, &off, webkit, 0, "javascript-can-access-clipboard");
+    setting_add(c, "javascript-can-open-windows-automatically", TYPE_BOOLEAN, &off, webkit, 0, "javascript-can-open-windows-automatically");
+    setting_add(c, "media-playback-allows-inline", TYPE_BOOLEAN, &on, webkit, 0, "media-playback-allows-inline");
+    setting_add(c, "media-playback-requires-user-gesture", TYPE_BOOLEAN, &off, webkit, 0, "media-playback-requires-user-gesture");
 #if WEBKIT_CHECK_VERSION(2, 4, 0)
-    setting_add("media-stream", TYPE_BOOLEAN, &off, webkit, 0, "enable-media-stream");
-    setting_add("mediasource", TYPE_BOOLEAN, &off, webkit, 0, "enable-mediasource");
+    setting_add(c, "media-stream", TYPE_BOOLEAN, &off, webkit, 0, "enable-media-stream");
+    setting_add(c, "mediasource", TYPE_BOOLEAN, &off, webkit, 0, "enable-mediasource");
 #endif
     i = 5;
-    setting_add("minimumfontsize", TYPE_INTEGER, &i, webkit, 0, "minimum-font-size");
-    setting_add("monofont", TYPE_CHAR, &"monospace", webkit, 0, "monospace-font-family");
+    setting_add(c, "minimumfontsize", TYPE_INTEGER, &i, webkit, 0, "minimum-font-size");
+    setting_add(c, "monofont", TYPE_CHAR, &"monospace", webkit, 0, "monospace-font-family");
     i = SETTING_DEFAULT_FONT_SIZE;
-    setting_add("monofontsize", TYPE_INTEGER, &i, webkit, 0, "default-monospace-font-size");
-    setting_add("offlinecache", TYPE_BOOLEAN, &on, webkit, 0, "enable-offline-web-application-cache");
-    setting_add("pagecache", TYPE_BOOLEAN, &on, pagecache, 0, "enable-page-cache");
-    setting_add("plugins", TYPE_BOOLEAN, &on, webkit, 0, "enable-plugins");
-    setting_add("print-backgrounds", TYPE_BOOLEAN, &on, webkit, 0, "print-backgrounds");
-    setting_add("private-browsing", TYPE_BOOLEAN, &off, webkit, 0, "enable-private-browsing");
-    setting_add("resizable-text-areas", TYPE_BOOLEAN, &on, webkit, 0, "resizable-text-areas");
-    setting_add("respect-image-orientation", TYPE_BOOLEAN, &off, webkit, 0, "respect-image-orientation");
-    setting_add("sansfont", TYPE_CHAR, &"sans-serif", webkit, 0, "sans-serif-font-family");
-    setting_add("scripts", TYPE_BOOLEAN, &on, webkit, 0, "enable-scripts");
-    setting_add("seriffont", TYPE_CHAR, &"serif", webkit, 0, "serif-font-family");
-    setting_add("site-specific-quirks", TYPE_BOOLEAN, &off, webkit, 0, "enable-site-specific-quirks");
+    setting_add(c, "monofontsize", TYPE_INTEGER, &i, webkit, 0, "default-monospace-font-size");
+    setting_add(c, "offlinecache", TYPE_BOOLEAN, &on, webkit, 0, "enable-offline-web-application-cache");
+    setting_add(c, "plugins", TYPE_BOOLEAN, &on, webkit, 0, "enable-plugins");
+    setting_add(c, "print-backgrounds", TYPE_BOOLEAN, &on, webkit, 0, "print-backgrounds");
+    setting_add(c, "private-browsing", TYPE_BOOLEAN, &off, webkit, 0, "enable-private-browsing");
+    setting_add(c, "sansfont", TYPE_CHAR, &"sans-serif", webkit, 0, "sans-serif-font-family");
+    setting_add(c, "scripts", TYPE_BOOLEAN, &on, webkit, 0, "enable-javascript");
+    setting_add(c, "seriffont", TYPE_CHAR, &"serif", webkit, 0, "serif-font-family");
+    setting_add(c, "site-specific-quirks", TYPE_BOOLEAN, &off, webkit, 0, "enable-site-specific-quirks");
 #if WEBKIT_CHECK_VERSION(1, 9, 0)
-    setting_add("smooth-scrolling", TYPE_BOOLEAN, &off, webkit, 0, "enable-smooth-scrolling");
+    setting_add(c, "smooth-scrolling", TYPE_BOOLEAN, &off, webkit, 0, "enable-smooth-scrolling");
 #endif
-    setting_add("spacial-navigation", TYPE_BOOLEAN, &off, webkit, 0, "enable-spatial-navigation");
-    setting_add("spell-checking", TYPE_BOOLEAN, &off, webkit, 0, "enable-spell-checking");
-    setting_add("spell-checking-languages", TYPE_CHAR, NULL, webkit, 0, "spell-checking-languages");
-    setting_add("tab-key-cycles-through-elements", TYPE_BOOLEAN, &on, webkit, 0, "tab-key-cycles-through-elements");
-    setting_add("universal-access-from-file-uris", TYPE_BOOLEAN, &off, webkit, 0, "enable-universal-access-from-file-uris");
-    setting_add("useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux i686) AppleWebKit/538.15+ (KHTML, like Gecko) " PROJECT "/" VERSION " Version/8.0 Safari/538.15", webkit, 0, "user-agent");
-    setting_add("webaudio", TYPE_BOOLEAN, &off, webkit, 0, "enable-webaudio");
-    setting_add("webgl", TYPE_BOOLEAN, &off, webkit, 0, "enable-webgl");
-    setting_add("webinspector", TYPE_BOOLEAN, &off, webkit, 0, "enable-developer-extras");
-    setting_add("xssauditor", TYPE_BOOLEAN, &on, webkit, 0, "enable-xss-auditor");
+    setting_add(c, "spacial-navigation", TYPE_BOOLEAN, &off, webkit, 0, "enable-spatial-navigation");
+    setting_add(c, "enable-tabs-to-links", TYPE_BOOLEAN, &on, webkit, 0, "enable-tabs-to-links");
+    setting_add(c, "webaudio", TYPE_BOOLEAN, &off, webkit, 0, "enable-webaudio");
+    setting_add(c, "webgl", TYPE_BOOLEAN, &off, webkit, 0, "enable-webgl");
+    setting_add(c, "webinspector", TYPE_BOOLEAN, &on, webkit, 0, "enable-developer-extras");
+    setting_add(c, "xssauditor", TYPE_BOOLEAN, &on, webkit, 0, "enable-xss-auditor");
 
     /* internal variables */
-    setting_add("stylesheet", TYPE_BOOLEAN, &on, user_style, 0, NULL);
-    setting_add("proxy", TYPE_BOOLEAN, &on, proxy, 0, NULL);
-#ifdef FEATURE_COOKIE
-    setting_add("cookie-accept", TYPE_CHAR, &"always", cookie_accept, 0, NULL);
-    i = 4800;
-    setting_add("cookie-timeout", TYPE_INTEGER, &i, internal, 0, &vb.config.cookie_timeout);
-    i = -1;
-    setting_add("cookie-expire-time", TYPE_INTEGER, &i, internal, 0, &vb.config.cookie_expire_time);
-#endif
-    setting_add("strict-ssl", TYPE_BOOLEAN, &on, soup, 0, "ssl-strict");
-    setting_add("strict-focus", TYPE_BOOLEAN, &off, internal, 0, &vb.config.strict_focus);
+    setting_add(c, "stylesheet", TYPE_BOOLEAN, &on, user_style, 0, NULL);
+    setting_add(c, "userscripts", TYPE_BOOLEAN, &on, user_scripts, 0, NULL);
+    setting_add(c, "cookie-accept", TYPE_CHAR, &"always", cookie_accept, 0, NULL);
     i = 40;
-    setting_add("scrollstep", TYPE_INTEGER, &i, internal, 0, &vb.config.scrollstep);
-    setting_add("statusbar", TYPE_BOOLEAN, &on, statusbar, 0, NULL);
-    setting_add("status-color-bg", TYPE_COLOR, &"#000000", status_color, 0, &vb.style.status_bg[VB_STATUS_NORMAL]);
-    setting_add("status-color-fg", TYPE_COLOR, &"#ffffff", status_color, 0, &vb.style.status_fg[VB_STATUS_NORMAL]);
-    setting_add("status-font", TYPE_FONT, &SETTING_GUI_FONT_EMPH, status_font, 0, &vb.style.status_font[VB_STATUS_NORMAL]);
-    setting_add("status-ssl-color-bg", TYPE_COLOR, &"#95e454", status_color, 0, &vb.style.status_bg[VB_STATUS_SSL_VALID]);
-    setting_add("status-ssl-color-fg", TYPE_COLOR, &"#000000", status_color, 0, &vb.style.status_fg[VB_STATUS_SSL_VALID]);
-    setting_add("status-ssl-font", TYPE_FONT, &SETTING_GUI_FONT_EMPH, status_font, 0, &vb.style.status_font[VB_STATUS_SSL_VALID]);
-    setting_add("status-sslinvalid-color-bg", TYPE_COLOR, &"#ff7777", status_color, 0, &vb.style.status_bg[VB_STATUS_SSL_INVALID]);
-    setting_add("status-sslinvalid-color-fg", TYPE_COLOR, &"#000000", status_color, 0, &vb.style.status_fg[VB_STATUS_SSL_INVALID]);
-    setting_add("status-sslinvalid-font", TYPE_FONT, &SETTING_GUI_FONT_EMPH, status_font, 0, &vb.style.status_font[VB_STATUS_SSL_INVALID]);
-    i = 1000;
-    setting_add("timeoutlen", TYPE_INTEGER, &i, internal, 0, &vb.config.timeoutlen);
-    setting_add("input-autohide", TYPE_BOOLEAN, &off, input_autohide, 0, &vb.config.input_autohide);
-    setting_add("input-bg-normal", TYPE_COLOR, &"#ffffff", input_color, 0, &vb.style.input_bg[VB_MSG_NORMAL]);
-    setting_add("input-bg-error", TYPE_COLOR, &"#ff7777", input_color, 0, &vb.style.input_bg[VB_MSG_ERROR]);
-    setting_add("input-fg-normal", TYPE_COLOR, &"#000000", input_color, 0, &vb.style.input_fg[VB_MSG_NORMAL]);
-    setting_add("input-fg-error", TYPE_COLOR, &"#000000", input_color, 0, &vb.style.input_fg[VB_MSG_ERROR]);
-    setting_add("input-font-normal", TYPE_FONT, &SETTING_GUI_FONT_NORMAL, input_font, 0, &vb.style.input_font[VB_MSG_NORMAL]);
-    setting_add("input-font-error", TYPE_FONT, &SETTING_GUI_FONT_EMPH, input_font, 0, &vb.style.input_font[VB_MSG_ERROR]);
-    setting_add("completion-font", TYPE_FONT, &SETTING_GUI_FONT_NORMAL, input_font, 0, &vb.style.comp_font);
-    setting_add("completion-fg-normal", TYPE_COLOR, &"#f6f3e8", input_color, 0, &vb.style.comp_fg[VB_COMP_NORMAL]);
-    setting_add("completion-fg-active", TYPE_COLOR, &"#ffffff", input_color, 0, &vb.style.comp_fg[VB_COMP_ACTIVE]);
-    setting_add("completion-bg-normal", TYPE_COLOR, &"#656565", input_color, 0, &vb.style.comp_bg[VB_COMP_NORMAL]);
-    setting_add("completion-bg-active", TYPE_COLOR, &"#777777", input_color, 0, &vb.style.comp_bg[VB_COMP_ACTIVE]);
-    setting_add("ca-bundle", TYPE_CHAR, &SETTING_CA_BUNDLE, ca_bundle, 0, NULL);
-    setting_add("home-page", TYPE_CHAR, &SETTING_HOME_PAGE, NULL, 0, NULL);
-    i = 1000;
-    setting_add("hint-timeout", TYPE_INTEGER, &i, NULL, 0, NULL);
-    setting_add("hintkeys", TYPE_CHAR, &"0123456789", NULL, 0, NULL);
-    setting_add("hint-follow-last", TYPE_BOOLEAN, &on, NULL, 0, NULL);
-    setting_add("hint-number-same-length", TYPE_BOOLEAN, &off, NULL, 0, NULL);
-    setting_add("download-path", TYPE_CHAR, &"", internal, 0, &vb.config.download_dir);
-    i = 2000;
-    setting_add("history-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.history_max);
-    i = 10;
-    setting_add("closed-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.closed_max);
-    setting_add("editor-command", TYPE_CHAR, &"x-terminal-emulator -e -vi '%s'", NULL, 0, NULL);
-    setting_add("header", TYPE_CHAR, &"", headers, FLAG_LIST|FLAG_NODUP, NULL);
-#ifdef FEATURE_ARH
-    setting_add("auto-response-header", TYPE_CHAR, &"", autoresponseheader, FLAG_LIST|FLAG_NODUP, NULL);
-#endif
-    setting_add("nextpattern", TYPE_CHAR, &"/\\bnext\\b/i,/^(>\\|>>\\|»)$/,/^(>\\|>>\\|»)/,/(>\\|>>\\|»)$/,/\\bmore\\b/i", prevnext, FLAG_LIST|FLAG_NODUP, NULL);
-    setting_add("previouspattern", TYPE_CHAR, &"/\\bprev\\|previous\\b/i,/^(<\\|<<\\|«)$/,/^(<\\|<<\\|«)/,/(<\\|<<\\|«)$/", prevnext, FLAG_LIST|FLAG_NODUP, NULL);
-    setting_add("fullscreen", TYPE_BOOLEAN, &off, fullscreen, 0, NULL);
-    setting_add("download-command", TYPE_CHAR, &"/bin/sh -c \"curl -sLJOC - -A '$VIMB_USER_AGENT' -e '$VIMB_URI' -b '$VIMB_COOKIES' '%s'\"", NULL, 0, NULL);
-    setting_add("download-use-external", TYPE_BOOLEAN, &off, NULL, 0, NULL);
-#ifdef FEATURE_HSTS
-    setting_add("hsts", TYPE_BOOLEAN, &on, hsts, 0, NULL);
-#endif
-#ifdef FEATURE_SOUP_CACHE
+    setting_add(c, "scrollstep", TYPE_INTEGER, &i, internal, 0, &c->config.scrollstep);
+    setting_add(c, "home-page", TYPE_CHAR, &SETTING_HOME_PAGE, NULL, 0, NULL);
     i = 2000;
-    setting_add("maximum-cache-size", TYPE_INTEGER, &i, soup_cache, 0, NULL);
-#endif
-    setting_add("x-hint-command", TYPE_CHAR, &":o <C-R>;", NULL, 0, NULL);
+    /* TODO should be global and not overwritten by a new client */
+    setting_add(c, "history-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.history_max);
+    setting_add(c, "editor-command", TYPE_CHAR, &"x-terminal-emulator -e -vi '%s'", NULL, 0, NULL);
+    setting_add(c, "strict-ssl", TYPE_BOOLEAN, &on, tls_policy, 0, NULL);
+    setting_add(c, "statusbar", TYPE_BOOLEAN, &on, statusbar, 0, NULL);
+    i = 1000;
+    setting_add(c, "timeoutlen", TYPE_INTEGER, &i, internal, 0, &c->map.timeoutlen);
+    setting_add(c, "input-autohide", TYPE_BOOLEAN, &off, input_autohide, 0, &c->config.input_autohide);
+    setting_add(c, "fullscreen", TYPE_BOOLEAN, &off, fullscreen, 0, NULL);
 
     /* initialize the shortcuts and set the default shortcuts */
-    shortcut_init();
-    shortcut_add("dl", "https://duckduckgo.com/html/?q=$0");
-    shortcut_add("dd", "https://duckduckgo.com/?q=$0");
-    shortcut_set_default("dl");
-
-    /* initialize the handlers */
-    handlers_init();
-    handler_add("magnet", "xdg-open '%s'");
+    shortcut_init(c);
+    shortcut_add(c, "dl", "https://duckduckgo.com/html/?q=$0");
+    shortcut_add(c, "dd", "https://duckduckgo.com/?q=$0");
+    shortcut_set_default(c, "dl");
 }
 
-VbCmdResult setting_run(char *name, const char *param)
+VbCmdResult setting_run(Client *c, char *name, const char *param)
 {
     SettingType type = SETTING_SET;
     char modifier;
@@ -269,34 +174,34 @@ VbCmdResult setting_run(char *name, const char *param)
     }
 
     /* lookup a matching setting */
-    Setting *s = g_hash_table_lookup(vb.config.settings, name);
+    Setting *s = g_hash_table_lookup(c->config.settings, name);
     if (!s) {
-        vb_echo(VB_MSG_ERROR, true, "Config '%s' not found", name);
-        return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+        vb_echo(c, MSG_ERROR, TRUE, "Config '%s' not found", name);
+        return CMD_ERROR | CMD_KEEPINPUT;
     }
 
     if (type == SETTING_GET) {
-        setting_print(s);
-        return VB_CMD_SUCCESS | VB_CMD_KEEPINPUT;
+        setting_print(c, s);
+        return CMD_SUCCESS | CMD_KEEPINPUT;
     }
 
     if (type == SETTING_TOGGLE) {
         if (s->type != TYPE_BOOLEAN) {
-            vb_echo(VB_MSG_ERROR, true, "Could not toggle none boolean %s", s->name);
+            vb_echo(c, MSG_ERROR, TRUE, "Could not toggle none boolean %s", s->name);
 
-            return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+            return CMD_ERROR | CMD_KEEPINPUT;
         }
         gboolean value = !s->value.b;
-        res = setting_set_value(s, &value, SETTING_SET);
-        setting_print(s);
+        res = setting_set_value(c, s, &value, SETTING_SET);
+        setting_print(c, s);
 
         /* make sure the new value set by the toggle keep visible */
-        res |= VB_CMD_KEEPINPUT;
+        res |= CMD_KEEPINPUT;
     } else {
         if (!param) {
-            vb_echo(VB_MSG_ERROR, true, "No valid value");
+            vb_echo(c, MSG_ERROR, TRUE, "No valid value");
 
-            return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+            return CMD_ERROR | CMD_KEEPINPUT;
         }
 
         /* convert sting value into internal used data type */
@@ -306,49 +211,49 @@ VbCmdResult setting_run(char *name, const char *param)
             case TYPE_BOOLEAN:
                 boolvar = g_ascii_strncasecmp(param, "true", 4) == 0
                     || g_ascii_strncasecmp(param, "on", 2) == 0;
-                res = setting_set_value(s, &boolvar, type);
+                res = setting_set_value(c, s, &boolvar, type);
                 break;
 
             case TYPE_INTEGER:
                 intvar = g_ascii_strtoull(param, (char**)NULL, 10);
-                res = setting_set_value(s, &intvar, type);
+                res = setting_set_value(c, s, &intvar, type);
                 break;
 
             default:
-                res = setting_set_value(s, (void*)param, type);
+                res = setting_set_value(c, s, (void*)param, type);
                 break;
         }
     }
 
-    if (res & (VB_CMD_SUCCESS | VB_CMD_KEEPINPUT)) {
+    if (res & (CMD_SUCCESS | CMD_KEEPINPUT)) {
         return res;
     }
 
-    vb_echo(VB_MSG_ERROR, true, "Could not set %s", s->name);
-    return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+    vb_echo(c, MSG_ERROR, TRUE, "Could not set %s", s->name);
+    return CMD_ERROR | CMD_KEEPINPUT;
 }
 
-gboolean setting_fill_completion(GtkListStore *store, const char *input)
+gboolean setting_fill_completion(Client *c, GtkListStore *store, const char *input)
 {
-    GList *src = g_hash_table_get_keys(vb.config.settings);
-    gboolean found = util_fill_completion(store, input, src);
+    GList *src = g_hash_table_get_keys(c->config.settings);
+    gboolean found = completion_fill(store, input, src);
     g_list_free(src);
 
     return found;
 }
 
-void setting_cleanup(void)
+void setting_cleanup(Client *c)
 {
-    if (vb.config.settings) {
-        g_hash_table_destroy(vb.config.settings);
+    if (c->config.settings) {
+        g_hash_table_destroy(c->config.settings);
+        c->config.settings = NULL;
     }
-    shortcut_cleanup();
-    handlers_cleanup();
+    shortcut_cleanup(c);
 }
 
-static int setting_set_value(Setting *prop, void *value, SettingType type)
+static int setting_set_value(Client *c, Setting *prop, void *value, SettingType type)
 {
-    int res = VB_CMD_SUCCESS;
+    int res = CMD_SUCCESS;
     /* by default given value is also the new value */
     void *newvalue = NULL;
     gboolean free_newvalue;
@@ -359,9 +264,9 @@ static int setting_set_value(Setting *prop, void *value, SettingType type)
     /* if there is a setter defined - call this first to check if the value is
      * accepted */
     if (prop->setter) {
-        res = prop->setter(prop->name, prop->type, newvalue, prop->data);
+        res = prop->setter(c, prop->name, prop->type, newvalue, prop->data);
         /* break here on error and don't change the setting */
-        if (res & VB_CMD_ERROR) {
+        if (res & CMD_ERROR) {
             goto free;
         }
     }
@@ -390,12 +295,12 @@ free:
 
 /**
  * Prepares the value for the setting for the different setting types.
- * Return value true indicates that the memory of newvalue must be freed by
+ * Return value TRUE indicates that the memory of newvalue must be freed by
  * the caller.
  */
 static gboolean prepare_setting_value(Setting *prop, void *value, SettingType type, void **newvalue)
 {
-    gboolean islist, res = false;
+    gboolean islist, res = FALSE;
     int vlen, i = 0;
     char *p = NULL;
 
@@ -410,7 +315,7 @@ static gboolean prepare_setting_value(Setting *prop, void *value, SettingType ty
     /* perform arithmetic operation for integer values */
     if (prop->type == TYPE_INTEGER) {
         int *newint = g_malloc(sizeof(int));
-        res         = true;
+        res         = TRUE;
         if (type == SETTING_APPEND) {
             *newint = prop->value.i + *((int*)value);
         } else if (type == SETTING_PREPEND) {
@@ -473,7 +378,7 @@ static gboolean prepare_setting_value(Setting *prop, void *value, SettingType ty
         } else {
             *newvalue = g_strconcat(prop->value.s, value, NULL);
         }
-        res = true;
+        res = TRUE;
     } else if (type == SETTING_PREPEND) {
         if (islist && *(char*)value) {
             /* don't prepend a comma if the value is empty */
@@ -481,7 +386,7 @@ static gboolean prepare_setting_value(Setting *prop, void *value, SettingType ty
         } else {
             *newvalue = g_strconcat(value, prop->value.s, NULL);
         }
-        res = true;
+        res = TRUE;
     } else if (type == SETTING_REMOVE && p) {
         char *copy = g_strdup(prop->value.s);
         /* make p to point to the same position in the copy */
@@ -489,14 +394,14 @@ static gboolean prepare_setting_value(Setting *prop, void *value, SettingType ty
 
         memmove(p, p + i, 1 + strlen(p + vlen));
         *newvalue = copy;
-        res = true;
+        res = TRUE;
     }
 
     return res;
 }
 
-static gboolean setting_add(const char *name, int type, void *value,
-    SettingFunction setter, int flags, void *data)
+static gboolean setting_add(Client *c, const char *name, DataType type, void *value,
+        SettingFunction setter, int flags, void *data)
 {
     Setting *prop = g_slice_new0(Setting);
     prop->name   = name;
@@ -505,25 +410,25 @@ static gboolean setting_add(const char *name, int type, void *value,
     prop->flags  = flags;
     prop->data   = data;
 
-    setting_set_value(prop, value, SETTING_SET);
+    setting_set_value(c, prop, value, SETTING_SET);
 
-    g_hash_table_insert(vb.config.settings, (char*)name, prop);
-    return true;
+    g_hash_table_insert(c->config.settings, (char*)name, prop);
+    return TRUE;
 }
 
-static void setting_print(Setting *s)
+static void setting_print(Client *c, Setting *s)
 {
     switch (s->type) {
         case TYPE_BOOLEAN:
-            vb_echo(VB_MSG_NORMAL, false, "  %s=%s", s->name, s->value.b ? "true" : "false");
+            vb_echo(c, MSG_NORMAL, FALSE, "  %s=%s", s->name, s->value.b ? "true" : "false");
             break;
 
         case TYPE_INTEGER:
-            vb_echo(VB_MSG_NORMAL, false, "  %s=%d", s->name, s->value.i);
+            vb_echo(c, MSG_NORMAL, FALSE, "  %s=%d", s->name, s->value.i);
             break;
 
         default:
-            vb_echo(VB_MSG_NORMAL, false, "  %s=%s", s->name, s->value.s);
+            vb_echo(c, MSG_NORMAL, FALSE, "  %s=%s", s->name, s->value.s);
             break;
     }
 }
@@ -536,392 +441,192 @@ static void setting_free(Setting *s)
     g_slice_free(Setting, s);
 }
 
-static int webkit(const char *name, int type, void *value, void *data)
+static int cookie_accept(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    const char *property = (const char*)data;
-    WebKitWebSettings *web_setting = webkit_web_view_get_settings(vb.gui.webview);
-
-    switch (type) {
-        case TYPE_BOOLEAN:
-            g_object_set(G_OBJECT(web_setting), property, *((gboolean*)value), NULL);
-            break;
+    WebKitWebContext *ctx;
+    WebKitCookieManager *cm;
+    char *policy = (char*)value;
 
-        case TYPE_INTEGER:
-            g_object_set(G_OBJECT(web_setting), property, *((int*)value), NULL);
-            break;
+    ctx = webkit_web_view_get_context(c->webview);
+    cm  = webkit_web_context_get_cookie_manager(ctx);
+    if (strcmp("always", policy) == 0) {
+        webkit_cookie_manager_set_accept_policy(cm, WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
+    } else if (strcmp("origin", policy) == 0) {
+        webkit_cookie_manager_set_accept_policy(cm, WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY);
+    } else if (strcmp("never", policy) == 0) {
+        webkit_cookie_manager_set_accept_policy(cm, WEBKIT_COOKIE_POLICY_ACCEPT_NEVER);
+    } else {
+        vb_echo(c, MSG_ERROR, TRUE, "%s must be in [always, origin, never]", name);
 
-        default:
-            g_object_set(G_OBJECT(web_setting), property, (char*)value, NULL);
-            break;
+        return CMD_ERROR | CMD_KEEPINPUT;
     }
-    return VB_CMD_SUCCESS;
+
+    return CMD_SUCCESS;
 }
 
-static int pagecache(const char *name, int type, void *value, void *data)
+static int fullscreen(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    int res;
-    gboolean on = *((gboolean*)value);
-
-    /* first set the setting on the web settings */
-    res = webkit(name, type, value, data);
-
-    if (res == VB_CMD_SUCCESS && on) {
-        webkit_set_cache_model(WEBKIT_CACHE_MODEL_WEB_BROWSER);
+    if (*(gboolean*)value) {
+        gtk_window_fullscreen(GTK_WINDOW(c->window));
     } else {
-        /* reduce memory usage if caching is not used */
-        webkit_set_cache_model(WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER);
+        gtk_window_unfullscreen(GTK_WINDOW(c->window));
     }
 
-    return res;
-}
-
-static int soup(const char *name, int type, void *value, void *data)
-{
-    const char *property = (const char*)data;
-    switch (type) {
-        case TYPE_BOOLEAN:
-            g_object_set(G_OBJECT(vb.session), property, *((gboolean*)value), NULL);
-            break;
-
-        case TYPE_INTEGER:
-            g_object_set(G_OBJECT(vb.session), property, *((int*)value), NULL);
-            break;
-
-        default:
-            g_object_set(G_OBJECT(vb.session), property, (char*)value, NULL);
-            break;
-    }
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-static int internal(const char *name, int type, void *value, void *data)
+/**
+ * Allow to set user defined http headers.
+ *
+ * :set header=NAME1=VALUE!,NAME2=,NAME3
+ *
+ * Note that these headers will replace already existing headers. If there is
+ * no '=' after the header name, than the complete header will be removed from
+ * the request (NAME3), if the '=' is present means that the header value is
+ * set to empty value.
+ */
+static int headers(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    char **str;
-    switch (type) {
-        case TYPE_BOOLEAN:
-            *(gboolean*)data = *(gboolean*)value;
-            break;
-
-        case TYPE_INTEGER:
-            *(int*)data = *(int*)value;
-            break;
+    ext_proxy_set_header(c, (char*)value);
 
-        default:
-            str = (char**)data;
-            OVERWRITE_STRING(*str, (char*)value);
-            break;
-    }
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-static int input_autohide(const char *name, int type, void *value, void *data)
+static int input_autohide(Client *c, const char *name, DataType type, void *value, void *data)
 {
     char *text;
+
     /* save selected value in internal variable */
     *(gboolean*)data = *(gboolean*)value;
 
     /* if autohide is on and inputbox contains no text - hide it now */
     if (*(gboolean*)value) {
-        text = vb_get_input_text();
+        text = vb_input_get_text(c);
         if (!*text) {
-            gtk_widget_set_visible(GTK_WIDGET(vb.gui.input), false);
+            gtk_widget_set_visible(GTK_WIDGET(c->input), FALSE);
         }
         g_free(text);
     } else {
         /* autohide is off - make sure the input box is shown */
-        gtk_widget_set_visible(GTK_WIDGET(vb.gui.input), true);
-    }
-
-    return VB_CMD_SUCCESS;
-}
-
-static int input_color(const char *name, int type, void *value, void *data)
-{
-    VB_COLOR_PARSE((VbColor*)data, (char*)value);
-    vb_update_input_style();
-
-    return VB_CMD_SUCCESS;
-}
-
-static int statusbar(const char *name, int type, void *value, void *data)
-{
-    gtk_widget_set_visible(GTK_WIDGET(vb.gui.statusbar.box), *(gboolean*)value);
-
-    return VB_CMD_SUCCESS;
-}
-
-static int status_color(const char *name, int type, void *value, void *data)
-{
-    VB_COLOR_PARSE((VbColor*)data, (char*)value);
-    vb_update_status_style();
-
-    return VB_CMD_SUCCESS;
-}
-
-static int input_font(const char *name, int type, void *value, void *data)
-{
-    PangoFontDescription **font = (PangoFontDescription**)data;
-    if (*font) {
-        /* free previous font description */
-        pango_font_description_free(*font);
+        gtk_widget_set_visible(GTK_WIDGET(c->input), TRUE);
     }
-    *font = pango_font_description_from_string((char*)value);
-    vb_update_input_style();
 
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-static int status_font(const char *name, int type, void *value, void *data)
+static int internal(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    PangoFontDescription **font = (PangoFontDescription**)data;
-    if (*font) {
-        /* free previous font description */
-        pango_font_description_free(*font);
-    }
-    *font = pango_font_description_from_string((char*)value);
-    vb_update_status_style();
+    char **str;
+    switch (type) {
+        case TYPE_BOOLEAN:
+            *(gboolean*)data = *(gboolean*)value;
+            break;
 
-    return VB_CMD_SUCCESS;
-}
+        case TYPE_INTEGER:
+            *(int*)data = *(int*)value;
+            break;
 
-#ifdef FEATURE_COOKIE
-static int cookie_accept(const char *name, int type, void *value, void *data)
-{
-    char *policy = (char*)value;
-    int i;
-    SoupCookieJar *jar;
-    static struct {
-        SoupCookieJarAcceptPolicy policy;
-        char*                     name;
-    } map[] = {
-        {SOUP_COOKIE_JAR_ACCEPT_ALWAYS,         "always"},
-        {SOUP_COOKIE_JAR_ACCEPT_NEVER,          "never"},
-        {SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, "origin"},
-    };
-
-    jar = (SoupCookieJar*)soup_session_get_feature(vb.session, SOUP_TYPE_COOKIE_JAR);
-
-    for (i = 0; i < LENGTH(map); i++) {
-        if (!strcmp(map[i].name, policy)) {
-            g_object_set(jar, SOUP_COOKIE_JAR_ACCEPT_POLICY, map[i].policy, NULL);
-
-            return VB_CMD_SUCCESS;
-        }
+        default:
+            str = (char**)data;
+            OVERWRITE_STRING(*str, (char*)value);
+            break;
     }
-    vb_echo(VB_MSG_ERROR, true, "%s must be in [always, origin, never]", name);
-
-    return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
+    return CMD_SUCCESS;
 }
-#endif
 
-static int ca_bundle(const char *name, int type, void *value, void *data)
+static int user_scripts(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    char *expanded;
-    GError *error = NULL;
-    /* expand the given file and set it to the file database */
-    expanded         = util_expand((char*)value, UTIL_EXP_TILDE|UTIL_EXP_DOLLAR);
-    vb.config.tls_db = g_tls_file_database_new(expanded, &error);
-    g_free(expanded);
-    if (error) {
-        g_warning("Could not load ssl database '%s': %s", (char*)value, error->message);
-        g_error_free(error);
-
-        return VB_CMD_ERROR;
-    }
+    WebKitUserContentManager *ucm;
+    WebKitUserScript *script;
+    gchar *source;
 
-    /* there is no function to get the file back from tls file database so
-     * it's saved as separate configuration */
-    g_object_set(vb.session, "tls-database", vb.config.tls_db, NULL);
-
-    return VB_CMD_SUCCESS;
-}
-
-
-static int proxy(const char *name, int type, void *value, void *data)
-{
     gboolean enabled = *(gboolean*)value;
-#if SOUP_CHECK_VERSION(2, 42, 2)
-    GProxyResolver *proxy = NULL;
-#else
-    SoupURI *proxy = NULL;
-#endif
-
-    if (enabled) {
-        const char *http_proxy = g_getenv("http_proxy");
-
-        if (http_proxy != NULL && *http_proxy != '\0') {
-            char *proxy_new = strstr(http_proxy, "://")
-                ? g_strdup(http_proxy)
-                : g_strconcat("http://", http_proxy, NULL);
-
-#if SOUP_CHECK_VERSION(2, 42, 2)
-            const char *no_proxy;
-            char **ignored_hosts = NULL;
-            /* check for no_proxy environment variable that contains comma
-             * separated domains or ip addresses to skip from proxy */
-            if ((no_proxy = g_getenv("no_proxy"))) {
-                ignored_hosts = g_strsplit(no_proxy, ",", 0);
-            }
-
-            proxy = g_simple_proxy_resolver_new(proxy_new, ignored_hosts);
-            if (proxy) {
-                g_object_set(vb.session, "proxy-resolver", proxy, NULL);
-            }
-            g_strfreev(ignored_hosts);
-            g_object_unref(proxy);
-#else
-            proxy = soup_uri_new(proxy_new);
-            if (proxy && SOUP_URI_VALID_FOR_HTTP(proxy)) {
-                g_object_set(vb.session, "proxy-uri", proxy, NULL);
-            }
-            soup_uri_free(proxy);
-#endif
-            g_free(proxy_new);
-        }
-    } else {
-        /* disable the proxy */
-#if SOUP_CHECK_VERSION(2, 42, 2)
-        g_object_set(vb.session, "proxy-resolver", NULL, NULL);
-#else
-        g_object_set(vb.session, "proxy-uri", NULL, NULL);
-#endif
-    }
-
-    return VB_CMD_SUCCESS;
-}
 
-static int user_style(const char *name, int type, void *value, void *data)
-{
-    gboolean enabled = *(gboolean*)value;
-    WebKitWebSettings *web_setting = webkit_web_view_get_settings(vb.gui.webview);
+    ucm = webkit_web_view_get_user_content_manager(c->webview);
 
     if (enabled) {
-        char *uri = g_strconcat("file://", vb.files[FILES_USER_STYLE], NULL);
-        g_object_set(web_setting, "user-stylesheet-uri", uri, NULL);
-        g_free(uri);
+        if (g_file_get_contents(vb.files[FILES_SCRIPT], &source, NULL, NULL)) {
+            script = webkit_user_script_new(
+                source, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+                WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL
+            );
+
+            webkit_user_content_manager_add_script(ucm, script);
+            webkit_user_script_unref(script);
+            g_free(source);
+        }
     } else {
-        g_object_set(web_setting, "user-stylesheet-uri", NULL, NULL);
-    }
-
-    return VB_CMD_SUCCESS;
-}
-
-
-/**
- * Allow to set user defined http headers.
- *
- * :set header=NAME1=VALUE!,NAME2=,NAME3
- *
- * Note that these headers will replace already existing headers. If there is
- * no '=' after the header name, than the complete header will be removed from
- * the request (NAME3), if the '=' is present means that the header value is
- * set to empty value.
- */
-static int headers(const char *name, int type, void *value, void *data)
-{
-    /* remove previous parsed headers */
-    if (vb.config.headers) {
-        soup_header_free_param_list(vb.config.headers);
-        vb.config.headers = NULL;
+        webkit_user_content_manager_remove_all_scripts(ucm);
     }
-    vb.config.headers = soup_header_parse_param_list((char*)value);
 
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-#ifdef FEATURE_ARH
-static int autoresponseheader(const char *name, int type, void *value, void *data)
+static int user_style(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    const char *error = NULL;
-
-    GSList *new = arh_parse((char *)value, &error);
-
-    if (! error) {
-        /* remove previous parsed headers */
-        arh_free(vb.config.autoresponseheader);
+    WebKitUserContentManager *ucm;
+    WebKitUserStyleSheet *style;
+    gchar *source;
 
-        /* add the new one */
-        vb.config.autoresponseheader = new;
+    gboolean enabled = *(gboolean*)value;
 
-        return VB_CMD_SUCCESS;
+    ucm = webkit_web_view_get_user_content_manager(c->webview);
 
-    } else {
-        vb_echo(VB_MSG_ERROR, true, "auto-response-header: %s", error);
-        return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
-    }
-}
-#endif
+    if (enabled && vb.files[FILES_USER_STYLE]) {
+        if (g_file_get_contents(vb.files[FILES_USER_STYLE], &source, NULL, NULL)) {
+            style = webkit_user_style_sheet_new(
+                source, WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
+                WEBKIT_USER_STYLE_LEVEL_USER, NULL, NULL
+            );
 
-static int prevnext(const char *name, int type, void *value, void *data)
-{
-    if (validate_js_regexp_list((char*)value)) {
-        if (*name == 'n') {
-            OVERWRITE_STRING(vb.config.nextpattern, (char*)value);
+            webkit_user_content_manager_add_style_sheet(ucm, style);
+            webkit_user_style_sheet_unref(style);
+            g_free(source);
         } else {
-            OVERWRITE_STRING(vb.config.prevpattern, (char*)value);
+            g_warning("Could not reed style file: %s", vb.files[FILES_USER_STYLE]);
         }
-        return VB_CMD_SUCCESS;
-    }
-
-    return VB_CMD_ERROR | VB_CMD_KEEPINPUT;
-}
-
-static int fullscreen(const char *name, int type, void *value, void *data)
-{
-    if (*(gboolean*)value) {
-        gtk_window_fullscreen(GTK_WINDOW(vb.gui.window));
     } else {
-        gtk_window_unfullscreen(GTK_WINDOW(vb.gui.window));
+        webkit_user_content_manager_remove_all_style_sheets(ucm);
     }
 
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
 
-#ifdef FEATURE_HSTS
-static int hsts(const char *name, int type, void *value, void *data)
+static int statusbar(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    if (*(gboolean*)value) {
-        soup_session_add_feature(vb.session, SOUP_SESSION_FEATURE(vb.config.hsts_provider));
-    } else {
-        soup_session_remove_feature(vb.session, SOUP_SESSION_FEATURE(vb.config.hsts_provider));
-    }
-    return VB_CMD_SUCCESS;
+    gtk_widget_set_visible(GTK_WIDGET(c->statusbar.box), *(gboolean*)value);
+
+    return CMD_SUCCESS;
 }
-#endif
 
-#ifdef FEATURE_SOUP_CACHE
-static int soup_cache(const char *name, int type, void *value, void *data)
+static int tls_policy(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    int kilobytes = *(int*)value;
+    gboolean strict = *((gboolean*)value);
 
-    soup_cache_set_max_size(vb.config.soup_cache, kilobytes * 1000);
+    webkit_web_context_set_tls_errors_policy(
+        webkit_web_context_get_default(),
+        strict ? WEBKIT_TLS_ERRORS_POLICY_FAIL : WEBKIT_TLS_ERRORS_POLICY_IGNORE);
 
-    /* clear the cache if maximum-cache-size is set to zero - note that this
-     * will also effect other vimb instances */
-    if (!kilobytes) {
-        soup_cache_clear(vb.config.soup_cache);
-    }
-    return VB_CMD_SUCCESS;
+    return CMD_SUCCESS;
 }
-#endif
 
-/**
- * Validated syntax given list of JavaScript RegExp patterns.
- * If validation fails, the error is shown to the user.
- */
-static gboolean validate_js_regexp_list(const char *pattern)
+static int webkit(Client *c, const char *name, DataType type, void *value, void *data)
 {
-    gboolean result;
-    char *js, *value = NULL;
-    WebKitWebFrame *frame = webkit_web_view_get_main_frame(vb.gui.webview);
+    const char *property = (const char*)data;
+    WebKitSettings *web_setting = webkit_web_view_get_settings(c->webview);
+
+    switch (type) {
+        case TYPE_BOOLEAN:
+            g_object_set(G_OBJECT(web_setting), property, *((gboolean*)value), NULL);
+            break;
 
-    js     = g_strdup_printf("var i;for(i=0;i<[%s].length;i++);", pattern);
-    result = js_eval(webkit_web_frame_get_global_context(frame), js, NULL, &value);
-    g_free(js);
+        case TYPE_INTEGER:
+            g_object_set(G_OBJECT(web_setting), property, *((int*)value), NULL);
+            break;
 
-    if (!result) {
-        vb_echo(VB_MSG_ERROR, true, "%s", value);
+        default:
+            g_object_set(G_OBJECT(web_setting), property, (char*)value, NULL);
+            break;
     }
-    g_free(value);
-    return result;
+    return CMD_SUCCESS;
 }
index 9631920..cd1d8d5 100644 (file)
 #ifndef _SETTING_H
 #define _SETTING_H
 
+#include <gtk/gtk.h>
+
 #include "main.h"
 
-void setting_init(void);
-void setting_cleanup(void);
-VbCmdResult setting_run(char* name, const char* param);
-gboolean setting_fill_completion(GtkListStore *store, const char *input);
+void setting_init(Client *c);
+void setting_cleanup(Client *c);
+VbCmdResult setting_run(Client *c, char *name, const char *param);
+gboolean setting_fill_completion(Client *c, GtkListStore *store, const char *input);
 
 #endif /* end of include guard: _SETTING_H */
index 37b083f..0663b9f 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
+#include <glib.h>
+#include <string.h>
+
+#include "ascii.h"
 #include "main.h"
 #include "shortcut.h"
 #include "util.h"
-#include "ascii.h"
-
-extern VbCore vb;
 
-static GHashTable *shortcuts = NULL;
-static char *default_key = NULL;
+extern struct Vimb vb;
 
 static int get_max_placeholder(const char *str);
-static const char *shortcut_lookup(const char *string, const char **query);
+static const char *shortcut_lookup(Client *c, const char *string, const char **query);
 
 
-void shortcut_init(void)
+void shortcut_init(Client *c)
 {
-    shortcuts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    c->shortcut.table    = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+    c->shortcut.fallback = NULL;
 }
 
-void shortcut_cleanup(void)
+void shortcut_cleanup(Client *c)
 {
-    if (shortcuts) {
-        g_hash_table_destroy(shortcuts);
+    if (c->shortcut.table) {
+        g_hash_table_destroy(c->shortcut.table);
     }
 }
 
-gboolean shortcut_add(const char *key, const char *uri)
+gboolean shortcut_add(Client *c, const char *key, const char *uri)
 {
-    g_hash_table_insert(shortcuts, g_strdup(key), g_strdup(uri));
+    g_hash_table_insert(c->shortcut.table, g_strdup(key), g_strdup(uri));
 
-    return true;
+    return TRUE;
 }
 
-gboolean shortcut_remove(const char *key)
+gboolean shortcut_remove(Client *c, const char *key)
 {
-    return g_hash_table_remove(shortcuts, key);
+    return g_hash_table_remove(c->shortcut.table, key);
 }
 
-gboolean shortcut_set_default(const char *key)
+gboolean shortcut_set_default(Client *c, const char *key)
 {
     /* do not check if the shortcut exists to be able to set the default
      * before defining the shortcut */
-    OVERWRITE_STRING(default_key, key);
+    OVERWRITE_STRING(c->shortcut.fallback, key);
 
-    return true;
+    return TRUE;
 }
 
 /**
  * Retrieves the uri for given query string. Not that the memory of the
  * returned uri must be freed.
  */
-char *shortcut_get_uri(const char *string)
+char *shortcut_get_uri(Client *c, const char *string)
 {
     const char *tmpl, *query = NULL;
     char *uri, *quoted_param;
     int max_num, current_num;
     GString *token;
 
-    tmpl = shortcut_lookup(string, &query);
+    tmpl = shortcut_lookup(c, string, &query);
     if (!tmpl) {
         return NULL;
     }
@@ -151,19 +152,21 @@ char *shortcut_get_uri(const char *string)
         }
         current_num++;
     }
-    g_string_free(token, true);
+    g_string_free(token, TRUE);
 
     return uri;
 }
 
+#if 0
 gboolean shortcut_fill_completion(GtkListStore *store, const char *input)
 {
-    GList *src = g_hash_table_get_keys(shortcuts);
+    GList *src = g_hash_table_get_keys(c->shortcut.table);
     gboolean found = util_fill_completion(store, input, src);
     g_list_free(src);
 
     return found;
 }
+#endif
 
 /**
  * Retrieves th highest placeholder number used in given string.
@@ -190,20 +193,21 @@ static int get_max_placeholder(const char *str)
  * pointer with the query part of the given string (everything except of the
  * shortcut identifier).
  */
-static const char *shortcut_lookup(const char *string, const char **query)
+static const char *shortcut_lookup(Client *c, const char *string, const char **query)
 {
     char *p, *uri = NULL;
 
     if ((p = strchr(string, ' '))) {
         char *key  = g_strndup(string, p - string);
         /* is the first word might be a shortcut */
-        if ((uri = g_hash_table_lookup(shortcuts, key))) {
+        if ((uri = g_hash_table_lookup(c->shortcut.table, key))) {
             *query = p + 1;
         }
         g_free(key);
     }
 
-    if (!uri && default_key && (uri = g_hash_table_lookup(shortcuts, default_key))) {
+    if (!uri && c->shortcut.fallback
+            && (uri = g_hash_table_lookup(c->shortcut.table, c->shortcut.fallback))) {
         *query = string;
     }
 
index c521984..f932abf 100644 (file)
 #ifndef _SHORTCUT_H
 #define _SHORTCUT_H
 
-void shortcut_init(void);
-void shortcut_cleanup(void);
-gboolean shortcut_add(const char *key, const char *uri);
-gboolean shortcut_remove(const char *key);
-gboolean shortcut_set_default(const char *key);
-char *shortcut_get_uri(const char *key);
-gboolean shortcut_fill_completion(GtkListStore *store, const char *input);
+void shortcut_init(Client *c);
+void shortcut_cleanup(Client *c);
+gboolean shortcut_add(Client *c, const char *key, const char *uri);
+gboolean shortcut_remove(Client *c, const char *key);
+gboolean shortcut_set_default(Client *c, const char *key);
+char *shortcut_get_uri(Client *c, const char *key);
+/*gboolean shortcut_fill_completion(Client *c, GtkListStore *store, const char *input);*/
 
 #endif /* end of include guard: _SHORTCUT_H */
+
index 4207598..9670b84 100644 (file)
  * along with this program. If not, see http://www.gnu.org/licenses/.
  */
 
-#include "config.h"
+#include <ctype.h>
 #include <fcntl.h>
-#include <sys/file.h>
-#include <stdio.h>
+#include <glib.h>
 #include <pwd.h>
-#include <ctype.h>
-#include "main.h"
-#include "util.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+
 #include "ascii.h"
 #include "completion.h"
+#include "util.h"
 
-extern VbCore vb;
+static struct {
+    char    *config_dir;
+} util;
+
+static void create_dir_if_not_exists(const char *dirpath);
 
-static gboolean match(const char *pattern, int patlen, const char *subject);
-static gboolean match_list(const char *pattern, int patlen, const char *subject);
 
 /**
- * Retrieves newly allocated string with vimb config directory with profilename. 
- * If profilename is NULL, path to default directory is returned.
- * Returned string must be freed.
+ * Free memory for allocated path strings.
  */
-char *util_get_config_dir(const char *profilename)
+void util_cleanup(void)
 {
-    char *path = g_build_filename(g_get_user_config_dir(), PROJECT, G_DIR_SEPARATOR_S, profilename, NULL);
-    util_create_dir_if_not_exists(path);
-
-    return path;
+    if (util.config_dir) {
+        g_free(util.config_dir);
+    }
 }
 
 /**
- * Retrieves the path to the cache dir with profilename
- * If profilename is NULL, path to default directory is returned.
- * Returned string must be freed.
+ * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated
+ * string.
+ *
+ * Returned path must be g_freed.
  */
-char *util_get_cache_dir(const char *profilename)
+char *util_expand(Client *c, const char *src, int expflags)
 {
-    char *path = g_build_filename(g_get_user_cache_dir(), PROJECT, G_DIR_SEPARATOR_S, profilename, NULL);
-    util_create_dir_if_not_exists(path);
+    const char **input = &src;
+    char *result;
+    GString *dst = g_string_new("");
+    int flags    = expflags;
 
-    return path;
-}
+    while (**input) {
+        util_parse_expansion(c, input, dst, flags, "\\");
+        if (VB_IS_SEPARATOR(**input)) {
+            /* after space the tilde expansion is allowed */
+            flags = expflags;
+        } else {
+            /* remove tile expansion for next loop */
+            flags &= ~UTIL_EXP_TILDE;
+        }
+        /* move pointer to the next char */
+        (*input)++;
+    }
 
-/**
- * Retrieves the path to the socket dir with profilename
- * If profilename is NULL, path to default directory is returned.
- * Returned string must be freed.
- */
-char *util_get_runtime_dir(const char *profilename)
-{
-    char *path = g_build_filename(g_get_user_runtime_dir(), PROJECT, G_DIR_SEPARATOR_S, profilename, NULL);
-    util_create_dir_if_not_exists(path);
+    result = dst->str;
+    g_string_free(dst, FALSE);
 
-    return path;
+    return result;
 }
 
 /**
- * Retrieves the users home directory.
+ * Append new data to file.
+ *
+ * @file:   File to append the data
+ * @format: Format string used to process va_list
  */
-const char *util_get_home_dir(void)
+gboolean util_file_append(const char *file, const char *format, ...)
 {
-    const char *dir = g_getenv("HOME");
+    va_list args;
+    FILE *f;
 
-    if (!dir) {
-        dir = g_get_home_dir();
-    }
+    if ((f = fopen(file, "a+"))) {
+        flock(fileno(f), LOCK_EX);
 
-    return dir;
-}
+        va_start(args, format);
+        vfprintf(f, format, args);
+        va_end(args);
 
-void util_create_dir_if_not_exists(const char *dirpath)
-{
-    if (!g_file_test(dirpath, G_FILE_TEST_IS_DIR)) {
-        g_mkdir_with_parents(dirpath, 0755);
+        flock(fileno(f), LOCK_UN);
+        fclose(f);
+
+        return TRUE;
     }
+    return FALSE;
 }
 
-void util_create_file_if_not_exists(const char *filename)
+/**
+ * Retrieves the config directory path.
+ * Returnes string must be freed.
+ */
+char *util_get_config_dir(void)
 {
-    if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
-        FILE *f = fopen(filename, "a");
-        fclose(f);
-    }
+    char *path = g_build_filename(g_get_user_config_dir(), PROJECT, NULL);
+    create_dir_if_not_exists(path);
+
+    return path;
 }
 
 /**
  * Retrieves the length bytes from given file.
  *
- * The memory of returned string have to be freed!
+ * The memory of returned string have to be freed with g_free().
  */
 char *util_get_file_contents(const char *filename, gsize *length)
 {
-    GError *error  = NULL;
+    GError *error = NULL;
     char *content = NULL;
-    if (!(g_file_test(filename, G_FILE_TEST_IS_REGULAR)
-        && g_file_get_contents(filename, &content, length, &error))
-    ) {
-        g_warning("Cannot open %s: %s", filename, error ? error->message : "file not found");
-        g_clear_error(&error);
+
+    if (!g_file_get_contents(filename, &content, length, &error)) {
+        g_warning("Cannot open %s: %s", filename, error->message);
+        g_error_free(error);
     }
     return content;
 }
 
+/**
+ * Buil the path from given directory and filename and checks if the file
+ * exists. If the file does not exists and the create option is not set, this
+ * function returns NULL.
+ * If the file exists or the create option was given the full generated path
+ * is returned as newly allocated string.
+ *
+ * The return value must be freed with g_free.
+ *
+ * @dir:        Directory in which the file is searched.
+ * @filename:   Filename to built the absolute path with.
+ * @create:     If TRUE, the file is created if it does not already exist.
+ */
+char *util_get_filepath(const char *dir, const char *filename, gboolean create)
+{
+    char *fullpath;
+
+    /* Built the full path out of config dir and given file name. */
+    fullpath = g_build_filename(util_get_config_dir(), filename, NULL);
+
+    if (g_file_test(fullpath, G_FILE_TEST_IS_REGULAR)) {
+        return fullpath;
+    } else if (create) {
+        /* If create option was given - create the file. */
+        fclose(fopen(fullpath, "a"));
+        return fullpath;
+    }
+
+    g_free(fullpath);
+    return NULL;
+}
+
+
 /**
  * Retrieves the file content as lines.
  *
@@ -147,7 +194,7 @@ char **util_get_lines(const char *filename)
  *               unlimited items
  */
 GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
-    guint max_items)
+        guint max_items)
 {
     char *line, **lines;
     int i, len;
@@ -213,247 +260,57 @@ GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
 }
 
 /**
- * Append new data to file.
- *
- * @file:   File to append the data
- * @format: Format string used to process va_list
- */
-gboolean util_file_append(const char *file, const char *format, ...)
-{
-    va_list args;
-    FILE *f;
-
-    if ((f = fopen(file, "a+"))) {
-        flock(fileno(f), LOCK_EX);
-
-        va_start(args, format);
-        vfprintf(f, format, args);
-        va_end(args);
-
-        flock(fileno(f), LOCK_UN);
-        fclose(f);
-
-        return true;
-    }
-    return false;
-}
-
-/**
- * Prepend new data to file.
- *
- * @file:   File to prepend the data
- * @format: Format string used to process va_list
- */
-gboolean util_file_prepend(const char *file, const char *format, ...)
-{
-    gboolean res = false;
-    va_list args;
-    char *content;
-    FILE *f;
-
-    content = util_get_file_contents(file, NULL);
-    if ((f = fopen(file, "w"))) {
-        flock(fileno(f), LOCK_EX);
-
-        va_start(args, format);
-        /* write new content to the file */
-        vfprintf(f, format, args);
-        va_end(args);
-
-        /* append previous file content */
-        fputs(content, f);
-
-        flock(fileno(f), LOCK_UN);
-        fclose(f);
-
-        res = true;
-    }
-    g_free(content);
-
-    return res;
-}
-
-/**
- * Retrieves the first line from file and delete it from file.
- *
- * @file:       file to read from
- * @item_count: will be filled with the number of remaining lines in file if it
- *              is not NULL.
- *
- * Returned string must be freed with g_free.
- */
-char *util_file_pop_line(const char *file, int *item_count)
-{
-    char **lines = util_get_lines(file);
-    char *line = NULL;
-    int count = 0;
-
-    if (lines) {
-        int len = g_strv_length(lines);
-        if (len) {
-            line = g_strdup(lines[0]);
-            /* minus one for last empty item and one for popped item */
-            count = len - 2;
-            char *new = g_strjoinv("\n", lines + 1);
-            g_file_set_contents(file, new, -1, NULL);
-            g_free(new);
-        }
-        g_strfreev(lines);
-    }
-
-    if (item_count) {
-        *item_count = count;
-    }
-    return line;
-}
-
-char *util_strcasestr(const char *haystack, const char *needle)
-{
-    guchar c1, c2;
-    int i, j;
-    int nlen = strlen(needle);
-    int hlen = strlen(haystack) - nlen + 1;
-
-    for (i = 0; i < hlen; i++) {
-        for (j = 0; j < nlen; j++) {
-            c1 = haystack[i + j];
-            c2 = needle[j];
-            if (toupper(c1) != toupper(c2)) {
-                goto next;
-            }
-        }
-        return (char*)haystack + i;
-next:
-        ;
-    }
-    return NULL;
-}
-
-/**
- * Replaces appearances of search in string by given replace.
- * Returns a new allocated string if search was found.
- */
-char *util_str_replace(const char* search, const char* replace, const char* string)
-{
-    if (!string) {
-        return NULL;
-    }
-
-    char **buf = g_strsplit(string, search, -1);
-    char *ret  = g_strjoinv(replace, buf);
-    g_strfreev(buf);
-
-    return ret;
-}
-
-/**
- * Creates a temporary file with given content.
- *
- * Upon success, and if file is non-NULL, the actual file path used is
- * returned in file. This string should be freed with g_free() when not
- * needed any longer.
+ * Fills file path completion entries into given list store for also given
+ * input.
  */
-gboolean util_create_tmp_file(const char *content, char **file)
+gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const char *input)
 {
-    int fp;
-    ssize_t bytes, len;
-
-    fp = g_file_open_tmp(PROJECT "-XXXXXX", file, NULL);
-    if (fp == -1) {
-        g_critical("Could not create temp file %s", *file);
-        g_free(*file);
-        return false;
-    }
-
-    len = strlen(content);
-
-    /* write content into temporary file */
-    bytes = write(fp, content, len);
-    if (bytes < len) {
-        close(fp);
-        unlink(*file);
-        g_critical("Could not write temp file %s", *file);
-        g_free(*file);
-
-        return false;
-    }
-    close(fp);
+    gboolean found = FALSE;
+    GError *error  = NULL;
+    char *input_dirname, *real_dirname;
+    const char *last_slash, *input_basename;
+    GDir *dir;
+
+    last_slash     = strrchr(input, '/');
+    input_basename = last_slash ? last_slash + 1 : input;
+    input_dirname  = g_strndup(input, input_basename - input);
+    real_dirname   = util_expand(
+        c,
+        *input_dirname ? input_dirname : ".",
+        UTIL_EXP_TILDE|UTIL_EXP_DOLLAR|UTIL_EXP_SPECIAL
+    );
 
-    return true;
-}
+    dir = g_dir_open(real_dirname, 0, &error);
+    if (error) {
+        /* Can't open directory, likely bad user input */
+        g_error_free(error);
+    } else {
+        GtkTreeIter iter;
+        const char *filename;
+        char *fullpath, *result;
 
-/**
- * Build the absolute file path of given path and possible given directory.
- *
- * Returned path must be freed.
- */
-char *util_build_path(const char *path, const char *dir)
-{
-    char *fullPath = NULL, *fexp, *dexp, *p;
-    int expflags   = UTIL_EXP_TILDE|UTIL_EXP_DOLLAR;
-
-    /* if the path could be expanded */
-    if ((fexp = util_expand(path, expflags))) {
-        if (*fexp == '/') {
-            /* path is already absolute, no need to use given dir - there is
-             * no need to free fexp, bacuse this should be done by the caller
-             * on fullPath later */
-            fullPath = fexp;
-        } else if (dir && *dir) {
-            /* try to expand also the dir given - this may be ~/path */
-            if ((dexp = util_expand(dir, expflags))) {
-                /* use expanded dir and append expanded path */
-                fullPath = g_build_filename(dexp, fexp, NULL);
-                g_free(dexp);
+        while ((filename = g_dir_read_name(dir))) {
+            if (g_str_has_prefix(filename, input_basename)) {
+                fullpath = g_build_filename(real_dirname, filename, NULL);
+                if (g_file_test(fullpath, G_FILE_TEST_IS_DIR)) {
+                    result = g_strconcat(input_dirname, filename, "/", NULL);
+                } else {
+                    result = g_strconcat(input_dirname, filename, NULL);
+                }
+                g_free(fullpath);
+                gtk_list_store_append(store, &iter);
+                gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, result, -1);
+                g_free(result);
+                found = TRUE;
             }
-            g_free(fexp);
-        }
-    }
-
-    /* if full path not found use current dir */
-    if (!fullPath) {
-        fullPath = g_build_filename(g_get_current_dir(), path, NULL);
-    }
-
-    if ((p = strrchr(fullPath, '/'))) {
-        *p = '\0';
-        util_create_dir_if_not_exists(fullPath);
-        *p = '/';
-    }
-
-    return fullPath;
-}
-
-/**
- * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated
- * string.
- *
- * Returned path must be g_freed.
- */
-char *util_expand(const char *src, int expflags)
-{
-    const char **input = &src;
-    char *result;
-    GString *dst = g_string_new("");
-    int flags    = expflags;
-
-    while (**input) {
-        util_parse_expansion(input, dst, flags, "\\");
-        if (VB_IS_SEPARATOR(**input)) {
-            /* after space the tilde expansion is allowed */
-            flags = expflags;
-        } else {
-            /* remove tile expansion for next loop */
-            flags &= ~UTIL_EXP_TILDE;
         }
-        /* move pointer to the next char */
-        (*input)++;
+        g_dir_close(dir);
     }
 
-    result = dst->str;
-    g_string_free(dst, false);
+    g_free(input_dirname);
+    g_free(real_dirname);
 
-    return result;
+    return found;
 }
 
 /**
@@ -462,19 +319,19 @@ char *util_expand(const char *src, int expflags)
  * not expanded char. If no expansion pattern was found, the first char is
  * appended to given GString.
  *
- * @input:    String pointer with the content to be parsed.
- * @str:      GString that will be filled with expanded content.
- * @flags     Flags that determine which expansion are processed.
- * @quoteable String of chars that are additionally escapable by \.
- * Returns true if input started with expandable pattern.
+ * @input:     String pointer with the content to be parsed.
+ * @str:       GString that will be filled with expanded content.
+ * @flags      Flags that determine which expansion are processed.
+ * @quoteable: String of chars that are additionally escapable by \.
+ * Returns TRUE if input started with expandable pattern.
  */
-gboolean util_parse_expansion(const char **input, GString *str, int flags,
-    const char *quoteable)
+gboolean util_parse_expansion(Client *c, const char **input, GString *str,
+        int flags, const char *quoteable)
 {
     GString *name;
     const char *env, *prev, quote = '\\';
     struct passwd *pwd;
-    gboolean expanded = false;
+    gboolean expanded = FALSE;
 
     prev = *input;
     if (flags & UTIL_EXP_TILDE && **input == '~') {
@@ -482,8 +339,8 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags,
         (*input)++;
 
         if (**input == '/') {
-            g_string_append(str, util_get_home_dir());
-            expanded = true;
+            g_string_append(str, g_get_home_dir());
+            expanded = TRUE;
             /* if there is no char or space after ~/ skip the / to get
              * /home/user instead of /home/user/ */
             if (!*(*input + 1) || VB_IS_SPACE(*(*input + 1))) {
@@ -501,9 +358,9 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags,
             /* append the name to the destination string */
             if ((pwd = getpwnam(name->str))) {
                 g_string_append(str, pwd->pw_dir);
-                expanded = true;
+                expanded = TRUE;
             }
-            g_string_free(name, true);
+            g_string_free(name, TRUE);
         }
         /* move pointer back to last expanded char */
         (*input)--;
@@ -540,13 +397,13 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags,
         /* move pointer back to last expanded char */
         (*input)--;
         /* variable are expanded even if they do not exists */
-        expanded = true;
-        g_string_free(name, true);
+        expanded = TRUE;
+        g_string_free(name, TRUE);
     } else if (flags & UTIL_EXP_SPECIAL && **input == '%') {
-        if (*vb.state.uri) {
+        if (*c->state.uri) {
             /* TODO check for modifiers like :h:t:r:e */
-            g_string_append(str, vb.state.uri);
-            expanded = true;
+            g_string_append(str, c->state.uri);
+            expanded = TRUE;
         }
     }
 
@@ -583,275 +440,48 @@ gboolean util_parse_expansion(const char **input, GString *str, int flags,
     return expanded;
 }
 
-/**
- * Compares given string against also given list of patterns.
- *
- * *         Matches any sequence of characters.
- * ?         Matches any single character except of '/'.
- * {foo,bar} Matches foo or bar - '{', ',' and '}' within this pattern must be
- *           escaped by '\'. '*' and '?' have no special meaning within the
- *           curly braces.
- * *?{}      these chars must always be escaped by '\' to match them literally
- */
-gboolean util_wildmatch(const char *pattern, const char *subject)
-{
-    const char *end;
-    int braces, patlen, count;
-
-    /* loop through all pattens */
-    for (count = 0; *pattern; pattern = (*end == ',' ? end + 1 : end), count++) {
-        /* find end of the pattern - but be careful with comma in curly braces */
-        braces = 0;
-        for (end = pattern; *end && (*end != ',' || braces || *(end - 1) == '\\'); ++end) {
-            if (*end == '{') {
-                braces++;
-            } else if (*end == '}') {
-                braces--;
-            }
-        }
-        /* ignore single comma */
-        if (*pattern == *end) {
-            continue;
-        }
-        /* calculate the length of the pattern */
-        patlen = end - pattern;
-
-        /* if this pattern matches - return */
-        if (match(pattern, patlen, subject)) {
-            return true;
-        }
-    }
-
-    if (!count) {
-        /* empty pattern matches only on empty subject */
-        return !*subject;
-    }
-    /* there where one or more patterns but none of them matched */
-    return false;
-}
-
-/**
- * Compares given subject string against the given pattern.
- * The pattern needs not to bee NUL terminated.
- */
-static gboolean match(const char *pattern, int patlen, const char *subject)
+char *util_strcasestr(const char *haystack, const char *needle)
 {
-    int i;
-    char sl, pl;
-
-    while (patlen > 0) {
-        switch (*pattern) {
-            case '?':
-                /* '?' matches a single char except of / and subject end */
-                if (*subject == '/' || !*subject) {
-                    return false;
-                }
-                break;
-
-            case '*':
-                /* easiest case - the '*' ist the last char in pattern - this
-                 * will always match */
-                if (patlen == 1) {
-                    return true;
-                }
-                /* Try to match as much as possible. Try to match the complete
-                 * uri, if that fails move forward in uri and check for a
-                 * match. */
-                i = strlen(subject);
-                while (i >= 0 && !match(pattern + 1, patlen - 1, subject + i)) {
-                    i--;
-                }
-                return i >= 0;
-
-            case '}':
-                /* spurious '}' in pattern */
-                return false;
-
-            case '{':
-                /* possible {foo,bar} pattern */
-                return match_list(pattern, patlen, subject);
-
-            case '\\':
-                /* '\' escapes next special char */
-                if (strchr("*?{}", pattern[1])) {
-                    pattern++;
-                    patlen--;
-                    if (*pattern != *subject) {
-                        return false;
-                    }
-                }
-                break;
+    guchar c1, c2;
+    int i, j;
+    int nlen = strlen(needle);
+    int hlen = strlen(haystack) - nlen + 1;
 
-            default:
-                /* compare case insensitive */
-                sl = *subject;
-                if (VB_IS_UPPER(sl)) {
-                    sl += 'a' - 'A';
-                }
-                pl = *pattern;
-                if (VB_IS_UPPER(pl)) {
-                    pl += 'a' - 'A';
-                }
-                if (sl != pl) {
-                    return false;
-                }
-                break;
+    for (i = 0; i < hlen; i++) {
+        for (j = 0; j < nlen; j++) {
+            c1 = haystack[i + j];
+            c2 = needle[j];
+            if (toupper(c1) != toupper(c2)) {
+                goto next;
+            }
         }
-        /* do another loop run with next pattern and subject char */
-        pattern++;
-        patlen--;
-        subject++;
+        return (char*)haystack + i;
+next:
+        ;
     }
-
-    /* on end of pattern only a also ended subject is a match */
-    return !*subject;
+    return NULL;
 }
 
 /**
- * Matches pattern starting with '{'.
- * This function can process also on none null terminated pattern.
+ * Replaces appearances of search in string by given replace.
+ * Returns a new allocated string if search was found.
  */
-static gboolean match_list(const char *pattern, int patlen, const char *subject)
+char *util_str_replace(const char* search, const char* replace, const char* string)
 {
-    int endlen;
-    const char *end, *s;
-
-    /* finde the next none escaped '}' */
-    for (end = pattern, endlen = patlen; endlen > 0 && *end != '}'; end++, endlen--) {
-        /* if escape char - move pointer one additional step */
-        if (*end == '\\') {
-            end++;
-            endlen--;
-        }
-    }
-
-    if (!*end) {
-        /* unterminated '{' in pattern */
-        return false;
-    }
-
-    s = subject;
-    end++;      /* skip over } */
-    endlen--;
-    pattern++;  /* skip over { */
-    patlen--;
-    while (true) {
-        switch (*pattern) {
-            case ',':
-                if (match(end, endlen, s)) {
-                    return true;
-                }
-                s = subject;
-                pattern++;
-                patlen--;
-                break;
-
-            case '}':
-                return match(end, endlen, s);
-
-            case '\\':
-                if (pattern[1] == ',' || pattern[1] == '}' || pattern[1] == '{') {
-                    pattern++;
-                    patlen--;
-                }
-                /* fall through */
-
-            default:
-                if (*pattern == *s) {
-                    pattern++;
-                    patlen--;
-                    s++;
-                } else {
-                    /* this item of the list does not match - move forward to
-                     * the next none escaped ',' or '}' */
-                    s = subject;
-                    for (s = subject; *pattern != ',' && *pattern != '}'; pattern++, patlen--) {
-                        /* if escape char is found - skip next char */
-                        if (*pattern == '\\') {
-                            pattern++;
-                            patlen--;
-                        }
-                    }
-                    /* found ',' skip over it to check the next list item */
-                    if (*pattern == ',') {
-                        pattern++;
-                        patlen--;
-                    }
-                }
-        }
+    if (!string) {
+        return NULL;
     }
-}
-
 
-/**
- * Fills the given list store by matching data of also given src list.
- */
-gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src)
-{
-    gboolean found = false;
-    GtkTreeIter iter;
-
-    if (!input || !*input) {
-        for (GList *l = src; l; l = l->next) {
-            gtk_list_store_append(store, &iter);
-            gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, l->data, -1);
-            found = true;
-        }
-    } else {
-        for (GList *l = src; l; l = l->next) {
-            char *value = (char*)l->data;
-            if (g_str_has_prefix(value, input)) {
-                gtk_list_store_append(store, &iter);
-                gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, l->data, -1);
-                found = true;
-            }
-        }
-    }
+    char **buf = g_strsplit(string, search, -1);
+    char *ret  = g_strjoinv(replace, buf);
+    g_strfreev(buf);
 
-    return found;
+    return ret;
 }
 
-gboolean util_filename_fill_completion(GtkListStore *store, const char *input)
+static void create_dir_if_not_exists(const char *dirpath)
 {
-    gboolean found = false;
-
-    const char *last_slash = strrchr(input, '/');
-    const char *input_basename = last_slash ? last_slash + 1 : input;
-    char *input_dirname = g_strndup(input, input_basename - input);
-    char *real_dirname = util_expand(
-        *input_dirname ? input_dirname : ".",
-        UTIL_EXP_TILDE|UTIL_EXP_DOLLAR|UTIL_EXP_SPECIAL
-    );
-
-    GError *error = NULL;
-    GDir *dir = g_dir_open(real_dirname, 0, &error);
-    if (error) {
-        /* Can't open directory, likely bad user input */
-        g_error_free(error);
-    } else {
-        const char *filename;
-        GtkTreeIter iter;
-        while ((filename = g_dir_read_name(dir))) {
-            if (g_str_has_prefix(filename, input_basename)) {
-                char *fullpath = g_build_filename(real_dirname, filename, NULL);
-                char *result;
-                if (g_file_test(fullpath, G_FILE_TEST_IS_DIR)) {
-                    result = g_strconcat(input_dirname, filename, "/", NULL);
-                } else {
-                    result = g_strconcat(input_dirname, filename, NULL);
-                }
-                g_free(fullpath);
-                gtk_list_store_append(store, &iter);
-                gtk_list_store_set(store, &iter, COMPLETION_STORE_FIRST, result, -1);
-                g_free(result);
-                found = true;
-            }
-        }
-        g_dir_close(dir);
+    if (!g_file_test(dirpath, G_FILE_TEST_IS_DIR)) {
+        g_mkdir_with_parents(dirpath, 0755);
     }
-
-    g_free(input_dirname);
-    g_free(real_dirname);
-
-    return found;
 }
index 9ed1236..c09b098 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef _UTIL_H
 #define _UTIL_H
 
+#include <glib.h>
 #include "main.h"
 
 enum {
@@ -27,31 +28,21 @@ enum {
     UTIL_EXP_DOLLAR  = 0x02, /* $ENV and ${ENV} expansion */
     UTIL_EXP_SPECIAL = 0x04, /* expand % to current URI */
 };
-
 typedef void *(*Util_Content_Func)(const char*, const char*);
 
-char* util_get_config_dir(const char* profilename);
-char* util_get_cache_dir(const char* profilename);
-char* util_get_runtime_dir(const char* profilename);
-const char* util_get_home_dir(void);
-void util_create_dir_if_not_exists(const char* dirpath);
-void util_create_file_if_not_exists(const char* filename);
-char* util_get_file_contents(const char* filename, gsize* length);
-char** util_get_lines(const char* filename);
-GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
-    guint max_items);
+void util_cleanup(void);
+char *util_expand(Client *c, const char *src, int expflags);
 gboolean util_file_append(const char *file, const char *format, ...);
-gboolean util_file_prepend(const char *file, const char *format, ...);
-char *util_file_pop_line(const char *file, int *item_count);
-char* util_strcasestr(const char* haystack, const char* needle);
+char *util_get_config_dir(void);
+char *util_get_file_contents(const char *filename, gsize *length);
+char *util_get_filepath(const char *dir, const char *filename, gboolean create);
+char **util_get_lines(const char *filename);
+GList *util_file_to_unique_list(const char *filename, Util_Content_Func func,
+        guint max_items);
+gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const char *input);
+gboolean util_parse_expansion(Client *c, const char **input, GString *str,
+        int flags, const char *quoteable);
 char *util_str_replace(const char* search, const char* replace, const char* string);
-gboolean util_create_tmp_file(const char *content, char **file);
-char *util_build_path(const char *path, const char *dir);
-char *util_expand(const char *src, int expflags);
-gboolean util_parse_expansion(const char **input, GString *str, int flags,
-    const char *quoteable);
-gboolean util_wildmatch(const char *pattern, const char *subject);
-gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src);
-gboolean util_filename_fill_completion(GtkListStore *store, const char *input);
+char *util_strcasestr(const char *haystack, const char *needle);
 
 #endif /* end of include guard: _UTIL_H */
diff --git a/src/webextension/Makefile b/src/webextension/Makefile
new file mode 100644 (file)
index 0000000..671c23f
--- /dev/null
@@ -0,0 +1,19 @@
+BASEDIR=../..
+include $(BASEDIR)/config.mk
+
+OBJ = $(patsubst %.c, %.lo, $(wildcard *.c))
+
+all: $(EXTTARGET)
+
+clean:
+       $(RM) -f $(EXTTARGET) *.lo
+
+$(EXTTARGET): $(OBJ)
+       @echo "$(CC) $@"
+       @$(CC) $(EXTLDFLAGS) ${OBJ} -o $@
+
+%.lo: %.c
+       @echo "${CC} $@"
+       @$(CC) $(EXTCFLAGS) -fPIC -c -o $@ $<
+
+.PHONY: all clean
diff --git a/src/webextension/ext-dom.c b/src/webextension/ext-dom.c
new file mode 100644 (file)
index 0000000..26aeaf0
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <glib.h>
+#include <webkitdom/webkitdom.h>
+
+#include "ext-main.h"
+#include "ext-dom.h"
+
+static gboolean is_element_visible(WebKitDOMHTMLElement *element);
+
+
+/**
+ * Checks if given dom element is an editable element.
+ */
+gboolean ext_dom_is_editable(WebKitDOMElement *element)
+{
+    char *type;
+    gboolean result = FALSE;
+    
+    if (!element) {
+        return FALSE;
+    }
+    
+    /* element is editable if it's a text area or input with no type, text or
+     * password */
+    if (webkit_dom_html_element_get_is_content_editable(WEBKIT_DOM_HTML_ELEMENT(element))
+            || WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(element)) {
+        return TRUE;
+    }
+
+    if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) {
+        type = webkit_dom_html_input_element_get_input_type(WEBKIT_DOM_HTML_INPUT_ELEMENT(element));
+        /* Input element without type attribute are rendered and behave like
+         * type = text and there are a lot of pages in the wild using input
+         * field without type attribute. */
+        if (!*type
+            || !g_ascii_strcasecmp(type, "text")
+            || !g_ascii_strcasecmp(type, "password")
+            || !g_ascii_strcasecmp(type, "color")
+            || !g_ascii_strcasecmp(type, "date")
+            || !g_ascii_strcasecmp(type, "datetime")
+            || !g_ascii_strcasecmp(type, "datetime-local")
+            || !g_ascii_strcasecmp(type, "email")
+            || !g_ascii_strcasecmp(type, "month")
+            || !g_ascii_strcasecmp(type, "number")
+            || !g_ascii_strcasecmp(type, "search")
+            || !g_ascii_strcasecmp(type, "tel")
+            || !g_ascii_strcasecmp(type, "time")
+            || !g_ascii_strcasecmp(type, "url")
+            || !g_ascii_strcasecmp(type, "week"))
+        {
+            result = TRUE;
+        }
+
+        g_free(type);
+    }
+
+    return result;
+}
+
+/**
+ * Find the first editable element and set the focus on it and enter input
+ * mode.
+ * Returns true if there was an editable element focused.
+ */
+gboolean ext_dom_focus_input(WebKitDOMDocument *doc)
+{
+    WebKitDOMNode *html, *node;
+    WebKitDOMNodeList *list;
+    WebKitDOMXPathNSResolver *resolver;
+    WebKitDOMXPathResult* result;
+    WebKitDOMDocument *frame_doc;
+    guint i, len;
+
+    list = webkit_dom_document_get_elements_by_tag_name(doc, "html");
+    if (!list) {
+        return FALSE;
+    }
+
+    html = webkit_dom_node_list_item(list, 0);
+    g_object_unref(list);
+
+    resolver = webkit_dom_document_create_ns_resolver(doc, html);
+    if (!resolver) {
+        return FALSE;
+    }
+
+    /* Use translate to match xpath expression case insensitive so that also
+     * intput filed of type="TEXT" are matched. */
+    result = webkit_dom_document_evaluate(
+        doc, "//input[not(@type) "
+        "or translate(@type,'ETX','etx')='text' "
+        "or translate(@type,'ADOPRSW','adoprsw')='password' "
+        "or translate(@type,'CLOR','clor')='color' "
+        "or translate(@type,'ADET','adet')='date' "
+        "or translate(@type,'ADEIMT','adeimt')='datetime' "
+        "or translate(@type,'ACDEILMOT','acdeilmot')='datetime-local' "
+        "or translate(@type,'AEILM','aeilm')='email' "
+        "or translate(@type,'HMNOT','hmnot')='month' "
+        "or translate(@type,'BEMNRU','bemnru')='number' "
+        "or translate(@type,'ACEHRS','acehrs')='search' "
+        "or translate(@type,'ELT','elt')='tel' "
+        "or translate(@type,'EIMT','eimt')='time' "
+        "or translate(@type,'LRU','lru')='url' "
+        "or translate(@type,'EKW','ekw')='week' "
+        "]|//textarea",
+        html, resolver, 5, NULL, NULL
+    );
+    if (!result) {
+        return FALSE;
+    }
+    while ((node = webkit_dom_xpath_result_iterate_next(result, NULL))) {
+        if (is_element_visible(WEBKIT_DOM_HTML_ELEMENT(node))) {
+            webkit_dom_element_focus(WEBKIT_DOM_ELEMENT(node));
+            return TRUE;
+        }
+    }
+
+    /* Look for editable elements in frames too. */
+    list = webkit_dom_document_get_elements_by_tag_name(doc, "iframe");
+    len  = webkit_dom_node_list_get_length(list);
+
+    for (i = 0; i < len; i++) {
+        node      = webkit_dom_node_list_item(list, i);
+        frame_doc = webkit_dom_html_iframe_element_get_content_document(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node));
+        /* Stop on first frame with focused element. */
+        if (ext_dom_focus_input(frame_doc)) {
+            g_object_unref(list);
+            return TRUE;
+        }
+    }
+    g_object_unref(list);
+
+    return FALSE;
+}
+
+/**
+ * Retrieves the content of given editable element.
+ * Not that the returned value must be freed.
+ */
+char *ext_dom_editable_get_value(WebKitDOMElement *element)
+{
+    char *value = NULL;
+
+    if ((webkit_dom_html_element_get_is_content_editable(WEBKIT_DOM_HTML_ELEMENT(element)))) {
+        value = webkit_dom_html_element_get_inner_text(WEBKIT_DOM_HTML_ELEMENT(element));
+    } else if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(WEBKIT_DOM_HTML_INPUT_ELEMENT(element))) {
+        value = webkit_dom_html_input_element_get_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(element));
+    } else {
+        value = webkit_dom_html_text_area_element_get_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(element));
+    }
+
+    return value;
+}
+
+/**
+ * Indicates if the give nelement is visible.
+ */
+static gboolean is_element_visible(WebKitDOMHTMLElement *element)
+{
+    return TRUE;
+}
diff --git a/src/webextension/ext-dom.h b/src/webextension/ext-dom.h
new file mode 100644 (file)
index 0000000..657821d
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef _EXT_DOM_H
+#define _EXT_DOM_H
+
+#include <glib.h>
+#include <webkitdom/webkitdom.h>
+
+gboolean ext_dom_is_editable(WebKitDOMElement *element);
+gboolean ext_dom_focus_input(WebKitDOMDocument *doc);
+char *ext_dom_editable_get_value(WebKitDOMElement *element);
+
+#endif /* end of include guard: _EXT-DOM_H */
diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c
new file mode 100644 (file)
index 0000000..669fd7d
--- /dev/null
@@ -0,0 +1,360 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <JavaScriptCore/JavaScript.h>
+#include <gio/gio.h>
+#include <glib.h>
+#include <libsoup/soup.h>
+#include <webkit2/webkit-web-extension.h>
+
+#include "ext-main.h"
+#include "ext-dom.h"
+#include "ext-util.h"
+
+static void add_onload_event_observers(WebKitDOMDocument *doc);
+static void dbus_emit_signal(const char *name, GVariant *data);
+static void dbus_handle_method_call(GDBusConnection *conn, const char *sender,
+        const char *object_path, const char *interface_name, const char *method,
+        GVariant *parameters, GDBusMethodInvocation *invocation, gpointer data);
+static gboolean dbus_own_name_sync(GDBusConnection *connection, const char *name,
+        GBusNameOwnerFlags flags);
+static void on_dbus_name_acquire(GDBusConnection *connection, const char *name, gpointer data);
+static void on_editable_change_focus(WebKitDOMEventTarget *target, WebKitDOMEvent *event);
+static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *page, gpointer data);
+static void on_web_page_document_loaded(WebKitWebPage *page, gpointer data);
+static gboolean on_web_page_send_request(WebKitWebPage *page, WebKitURIRequest *request,
+        WebKitURIResponse *response, gpointer data);
+
+static const GDBusInterfaceVTable interface_vtable = {
+    dbus_handle_method_call,
+    NULL,
+    NULL
+};
+
+static const char introspection_xml[] =
+    "<node>"
+    " <interface name='" VB_WEBEXTENSION_INTERFACE "'>"
+    "  <signal name='EditableChangeFocus'>"
+    "   <arg type='b' name='focused' direction='out'/>"
+    "  </signal>"
+    "  <method name='FocusInput'>"
+    "  </method>"
+    "  <signal name='PageCreated'>"
+    "   <arg type='t' name='page_id' direction='out'/>"
+    "  </signal>"
+    "  <method name='SetHeaderSetting'>"
+    "   <arg type='s' name='headers' direction='in'/>"
+    "  </method>"
+    " </interface>"
+    "</node>";
+
+/* Global struct to hold internal used variables. */
+struct Ext {
+    guint               regid;
+    GDBusConnection     *connection;
+    WebKitWebPage       *webpage;
+    WebKitDOMElement    *active;
+    GHashTable          *headers;
+    GHashTable          *documents;
+    gboolean            input_focus;
+};
+struct Ext ext = {0};
+
+
+/**
+ * Webextension entry point.
+ */
+G_MODULE_EXPORT
+void webkit_web_extension_initialize_with_user_data(WebKitWebExtension *extension, GVariant *data)
+{
+    char *extid, *service_name;
+    g_variant_get(data, "(s)", &extid);
+
+    /* Get the DBus connection for the bus type. It would be a better to use
+     * the async g_bus_own_name for this, but this leads to cases where pages
+     * are created and documents are loaded before we get a name and are able
+     * to call to the UI process. */
+    ext.connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
+
+    service_name = g_strdup_printf("%s-%s", VB_WEBEXTENSION_SERVICE_NAME, extid);
+
+    /* Try to own name synchronously. */
+    if (ext.connection && dbus_own_name_sync(ext.connection, service_name, G_BUS_NAME_OWNER_FLAGS_NONE)) {
+        on_dbus_name_acquire(ext.connection, service_name, extension);
+    }
+
+    g_free(service_name);
+
+    g_signal_connect(extension, "page-created", G_CALLBACK(on_page_created), NULL);
+}
+
+/**
+ * Add observers to doc event for given document and all the contained iframes
+ * too.
+ */
+static void add_onload_event_observers(WebKitDOMDocument *doc)
+{
+    WebKitDOMEventTarget *target;
+
+    /* Add the document to the table of known documents or if already exists
+     * return to not apply observers multiple times. */
+    if (!g_hash_table_add(ext.documents, doc)) {
+        return;
+    }
+   
+    /* We have to use default view instead of the document itself in case this
+     * function is called with content document of an iframe. Else the event
+     * observing does not work. */
+    target = WEBKIT_DOM_EVENT_TARGET(webkit_dom_document_get_default_view(doc)); 
+
+    webkit_dom_event_target_add_event_listener(target, "focus",
+            G_CALLBACK(on_editable_change_focus), TRUE, NULL);
+    webkit_dom_event_target_add_event_listener(target, "blur",
+            G_CALLBACK(on_editable_change_focus), TRUE, NULL);
+    /* Check for focused editable elements also if they where focused before
+     * the event observer where set up. */
+    /* TODO this is not needed for strict-focus=on */
+    on_editable_change_focus(target, NULL);
+}
+
+/**
+ * Emits a signal over dbus.
+ *
+ * @name:   Signal name to emit.
+ * @data:   GVariant value used as value for the signal or NULL.
+ */
+static void dbus_emit_signal(const char *name, GVariant *data)
+{
+    GError *error = NULL;
+
+    /* Don't do anythings if the dbus connection was not established. */
+    if (!ext.connection) {
+        return;
+    }
+
+    /* propagate the signal over dbus */
+    g_dbus_connection_emit_signal(ext.connection, NULL,
+            VB_WEBEXTENSION_OBJECT_PATH, VB_WEBEXTENSION_INTERFACE, name,
+            data, &error);
+
+    /* check for error */
+    if (error) {
+        g_warning("Failed to emit signal '%s': %s", name, error->message);
+        g_error_free(error);
+    }
+}
+
+/**
+ * Handle dbus method calls.
+ */
+static void dbus_handle_method_call(GDBusConnection *conn, const char *sender,
+        const char *object_path, const char *interface_name, const char *method,
+        GVariant *parameters, GDBusMethodInvocation *invocation, gpointer data)
+{
+    char *value;
+
+    if (!g_strcmp0(method, "FocusInput")) {
+        ext_dom_focus_input(webkit_web_page_get_dom_document(ext.webpage));
+        g_dbus_method_invocation_return_value(invocation, NULL);
+    } else if (!g_strcmp0(method, "SetHeaderSetting")) {
+        g_variant_get(parameters, "(s)", &value);
+
+        if (ext.headers) {
+            soup_header_free_param_list(ext.headers);
+            ext.headers = NULL;
+        }
+        ext.headers = soup_header_parse_param_list(value);
+        g_dbus_method_invocation_return_value(invocation, NULL);
+    }
+}
+
+/**
+ * The synchronous and blocking pendent to the g_bus_own_name().
+ *
+ * @bus_type: The type of bus to own a name on.
+ * @name:     The well-known name to own.
+ * @flags:    A set of flags from the #GBusNameOwnerFlags enumeration.
+ */
+static gboolean dbus_own_name_sync(GDBusConnection *connection, const char *name,
+        GBusNameOwnerFlags flags)
+{
+    GError *error = NULL;
+    guint32 request_name_reply = 0;
+    GVariant *result;
+
+    result = g_dbus_connection_call_sync(
+            connection,
+            "org.freedesktop.DBus",
+            "/org/freedesktop/DBus",
+            "org.freedesktop.DBus",
+            "RequestName",
+            g_variant_new("(su)", name, flags),
+            G_VARIANT_TYPE("(u)"),
+            G_DBUS_CALL_FLAGS_NONE,
+            -1,
+            NULL,
+            &error);
+
+    if (result) {
+        g_variant_get(result, "(u)", &request_name_reply);
+        g_variant_unref(result);
+
+        if (1 == request_name_reply) {
+            return TRUE;
+        }
+    } else {
+        g_warning("Failed to acquire DBus name: %s", error->message);
+        g_error_free(error);
+    }
+    return FALSE;
+}
+
+/**
+ * Called when the dbus name is aquired and registers our object.
+ */
+static void on_dbus_name_acquire(GDBusConnection *connection, const char *name, gpointer data)
+{
+    GError *error = NULL;
+    static GDBusNodeInfo *node_info = NULL;
+
+    g_return_if_fail(G_IS_DBUS_CONNECTION(connection));
+
+    if (!node_info) {
+        node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+    }
+
+    /* register the webextension object */
+    ext.connection = connection;
+    ext.regid      = g_dbus_connection_register_object(
+            connection,
+            VB_WEBEXTENSION_OBJECT_PATH,
+            node_info->interfaces[0],
+            &interface_vtable,
+            WEBKIT_WEB_EXTENSION(data),
+            NULL,
+            &error);
+
+    if (!ext.regid) {
+        g_warning("Failed to register web extension object: %s", error->message);
+        g_error_free(error);
+    }
+}
+
+/**
+ * Callback called if a editable element changes it focus state.
+ * Event target may be a WebKitDOMDocument (in case of iframe) or a
+ * WebKitDOMDOMWindow.
+ */
+static void on_editable_change_focus(WebKitDOMEventTarget *target, WebKitDOMEvent *event)
+{
+    gboolean input_focus;
+    WebKitDOMDocument *doc;
+    WebKitDOMElement *active;
+
+    if (WEBKIT_DOM_IS_DOM_WINDOW(target)) {
+        g_object_get(target, "document", &doc, NULL);
+    } else {
+        /* target is a doc document */
+        doc = WEBKIT_DOM_DOCUMENT(target);
+    }
+    active = webkit_dom_document_get_active_element(doc);
+    /* Don't do anything if there is no active element or the active element
+     * is the same as before. */
+    if (!active || active == ext.active) {
+        return;
+    }
+    if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(active)) {
+        WebKitDOMHTMLIFrameElement *iframe;
+        WebKitDOMDocument *subdoc;
+
+        iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active);
+        subdoc = webkit_dom_html_iframe_element_get_content_document(iframe);
+        add_onload_event_observers(subdoc);
+        return;
+    }
+
+    ext.active = active;
+
+    /* Check if the active element is an editable element. */
+    input_focus = ext_dom_is_editable(active);
+    if (input_focus != ext.input_focus) {
+        ext.input_focus = input_focus;
+
+        dbus_emit_signal("EditableChangeFocus", g_variant_new("(b)", input_focus));
+    }
+}
+
+/**
+ * Callback for web extensions page-created signal.
+ */
+static void on_page_created(WebKitWebExtension *extension, WebKitWebPage *page, gpointer data)
+{
+    /* Save the new created page in the extension data for later use. */
+    ext.webpage = page;
+
+    g_object_connect(page,
+            "signal::send-request", G_CALLBACK(on_web_page_send_request), NULL,
+            "signal::document-loaded", G_CALLBACK(on_web_page_document_loaded), NULL,
+            NULL);
+
+    dbus_emit_signal("PageCreated", g_variant_new("(t)", webkit_web_page_get_id(page)));
+}
+
+/**
+ * Callback for web pages document-loaded signal.
+ */
+static void on_web_page_document_loaded(WebKitWebPage *page, gpointer data)
+{
+    /* If there is a hashtable of known document - detroy this and create a
+     * new hashtable. */
+    if (ext.documents) {
+        g_hash_table_unref(ext.documents);
+    }
+    ext.documents = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    add_onload_event_observers(webkit_web_page_get_dom_document(page));
+}
+
+/**
+ * Callback for web pages send-request signal.
+ */
+static gboolean on_web_page_send_request(WebKitWebPage *page, WebKitURIRequest *request,
+        WebKitURIResponse *response, gpointer data)
+{
+    /* Change request headers according to the users preferences. */
+    if (ext.headers) {
+        char *name, *value;
+        SoupMessageHeaders *headers;
+        GHashTableIter iter;
+
+        headers = webkit_uri_request_get_http_headers(request);
+        g_hash_table_iter_init(&iter, ext.headers);
+        while (g_hash_table_iter_next(&iter, (gpointer*)&name, (gpointer*)&value)) {
+            /* Null value is used to indicate that the header should be
+             * removed completely. */
+            if (value == NULL) {
+                soup_message_headers_remove(headers, name);
+            } else {
+                soup_message_headers_replace(headers, name, value);
+            }
+        }
+    }
+
+    return FALSE;
+}
diff --git a/src/webextension/ext-main.h b/src/webextension/ext-main.h
new file mode 100644 (file)
index 0000000..6178800
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef _EXT_MAIN_H
+#define _EXT_MAIN_H
+
+#define VB_WEBEXTENSION_SERVICE_NAME "org.vimb.browser.WebExtension"
+#define VB_WEBEXTENSION_OBJECT_PATH  "/org/vimb/browser/WebExtension"
+#define VB_WEBEXTENSION_INTERFACE    "org.vimb.browser.WebExtension"
+
+#endif /* end of include guard: _EXT_MAIN_H */
diff --git a/src/webextension/ext-util.c b/src/webextension/ext-util.c
new file mode 100644 (file)
index 0000000..0bb40e4
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include "../config.h"
+#include "ext-util.h"
+
+/**
+ * Creates a temporary file with given content.
+ *
+ * Upon success, and if file is non-NULL, the actual file path used is
+ * returned in file. This string should be freed with g_free() when not
+ * needed any longer.
+ */
+gboolean ext_util_create_tmp_file(const char *content, char **file)
+{
+    int fp;
+    ssize_t bytes, len;
+
+    fp = g_file_open_tmp(PROJECT "-XXXXXX", file, NULL);
+    if (fp == -1) {
+        g_critical("Could not create temp file %s", *file);
+        g_free(*file);
+        return FALSE;
+    }
+
+    len = strlen(content);
+
+    /* write content into temporary file */
+    bytes = write(fp, content, len);
+    if (bytes < len) {
+        close(fp);
+        unlink(*file);
+        g_critical("Could not write temp file %s", *file);
+        g_free(*file);
+
+        return FALSE;
+    }
+    close(fp);
+
+    return TRUE;
+}
diff --git a/src/webextension/ext-util.h b/src/webextension/ext-util.h
new file mode 100644 (file)
index 0000000..452c427
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2015 Daniel Carl
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see http://www.gnu.org/licenses/.
+ */
+
+#ifndef _EXT_UTIL_H
+#define _EXT_UTIL_H
+
+#include <glib.h>
+
+gboolean ext_util_create_tmp_file(const char *content, char **file);
+
+#endif /* end of include guard: _EXT_UTIL_H */
diff --git a/tests/Makefile b/tests/Makefile
deleted file mode 100644 (file)
index 766f66e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-BASEDIR=..
-SRCDIR=$(BASEDIR)/src
-include $(BASEDIR)/config.mk
-
-CPPFLAGS += -I $(BASEDIR)/
-CFLAGS   += -fPIC -pedantic
-
-TEST_PROGS = test-handlers \
-                        test-map      \
-                        test-shortcut \
-                        test-util
-
-all: $(TEST_PROGS)
-       LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):." gtester --verbose $(TEST_PROGS)
-
-${TEST_PROGS}: $(SRCDIR)/$(LIBTARGET)
-
-clean:
-       $(RM) -f $(TEST_PROGS)
diff --git a/tests/manual/112-editable-focus.html b/tests/manual/112-editable-focus.html
deleted file mode 100644 (file)
index 3accc37..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<html>
-<head>
-<title>Input mode Switching (#112 #237)</title>
-<script type="text/javascript">
-//<![CDATA[
-function setFocus() {
-    document.getElementById("text").focus();
-}
-//]]>
-</script>
-</head>
-<body onload="setFocus();">
-    <p>
-    Run with <code>scripts=on</code> and <code>strict-focus=off</code>
-    <ol>
-        <li>If page is loade, vimb should be in input mode.</li>
-        <li>Set <code>strict-focus=on</code> and reload page. Vimb should keep
-            in normal mode</li>
-        <li>Independent from the <code>strict-focus</code> should vimb switch
-            to input mode if the button is clicked</li>
-    </ol>
-    </p>
-    <form action="#">
-        <div>
-            <textarea name="text" id="text" rows="11" cols="50"></textarea><br/>
-            <input type="button" value="Focus Textarea" onclick="document.getElementById('text').focus();"/>
-        </div>
-    </form>
-    <p>
-    Also the following element using <code>contenteditable="true"</code>
-    should switch vimb into input mode on click.
-    </p>
-    <div contenteditable="true" style="width:50%;height:3em;border:1px solid #000">
-    Clicking this element using contenteditable="true" should
-    switch vimb into input mode too.
-    </div>
-</body>
-</html>
-
diff --git a/tests/manual/146-hsts-iframe.html b/tests/manual/146-hsts-iframe.html
deleted file mode 100644 (file)
index 7775b75..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<html>
-<head>
-<title>iFrame URI change by HSTS (#146)</title>
-</head>
-<body>
-    <p>
-    The hsts domain used in iFrame with http must not lead to load the
-    iframe content as page.
-    </p>
-    <iframe src="http://github.com/fanglingsu/vimb" width="600" height="400">
-    Github
-    </iframe>
-</body>
-</html>
diff --git a/tests/manual/201-editable-focus-in-iframes.html b/tests/manual/201-editable-focus-in-iframes.html
deleted file mode 100644 (file)
index d02c0fb..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<html>
-<head>
-<title>Track Focu/Blur also within iFrames</title>
-</head>
-<body>
-<iframe style="border: 1px solid #000; width: 500px; height: 500px;" src="./112-editable-focus.html"></iframe>
-</body>
-</html>
-
diff --git a/tests/test-handlers.c b/tests/test-handlers.c
deleted file mode 100644 (file)
index 8cff0b1..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include <gtk/gtk.h>
-#include <src/handlers.h>
-
-#define TEST_URI "http://fanglingsu.github.io/vimb/"
-
-static void test_handler_add(void)
-{
-    /* check none handled http first */
-    g_assert_true(handler_add("http", "echo -n 'handled uri %s'"));
-}
-
-static void test_handler_remove(void)
-{
-    handler_add("http", "e");
-
-    g_assert_true(handler_remove("http"));
-    g_assert_false(handler_remove("http"));
-}
-
-static void test_handler_run_success(void)
-{
-    if (g_test_subprocess()) {
-        handler_add("http", "echo -n 'handled uri %s'");
-        handle_uri(TEST_URI);
-        return;
-    }
-    g_test_trap_subprocess(NULL, 0, 0);
-    g_test_trap_assert_passed();
-    g_test_trap_assert_stdout("handled uri " TEST_URI);
-}
-
-static void test_handler_run_failed(void)
-{
-    if (g_test_subprocess()) {
-        handler_add("http", "unknown-program %s");
-        handle_uri(TEST_URI);
-        return;
-    }
-    g_test_trap_subprocess(NULL, 0, 0);
-    g_test_trap_assert_failed();
-    g_test_trap_assert_stderr("*Can't run *unknown-program*");
-}
-
-int main(int argc, char *argv[])
-{
-    int result;
-    handlers_init();
-
-    g_test_init(&argc, &argv, NULL);
-
-    g_test_add_func("/test-handlers/add", test_handler_add);
-    g_test_add_func("/test-handlers/remove", test_handler_remove);
-    g_test_add_func("/test-handlers/handle_uri/success", test_handler_run_success);
-    g_test_add_func("/test-handlers/handle_uri/failed", test_handler_run_failed);
-
-    result = g_test_run();
-
-    handlers_cleanup();
-
-    return result;
-}
diff --git a/tests/test-map.c b/tests/test-map.c
deleted file mode 100644 (file)
index 8fe5ace..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-#include <gdk/gdkkeysyms-compat.h>
-#include <src/map.h>
-#include <src/main.h>
-
-static char queue[20];  /* receives the keypresses */
-static int  qpos = 0;   /* points to the queue entry for the next keypress */
-
-#define QUEUE_APPEND(c) {    \
-    queue[qpos++] = (char)c; \
-    queue[qpos]   = '\0';    \
-}
-#define QUEUE_CLEAR() {queue[(qpos = 0)] = '\0';}
-#define ASSERT_MAPPING(s, e) {  \
-    QUEUE_CLEAR();                   \
-    map_handle_string(s, true);      \
-    g_assert_cmpstr(queue, ==, e);   \
-}
-
-typedef struct {
-    guint state;
-    guint keyval;
-} TestKeypress;
-
-VbResult keypress(int key)
-{
-    /* put the key into the queue */
-    QUEUE_APPEND(key);
-
-    return RESULT_COMPLETE;
-}
-
-static void test_handle_string_simple(void)
-{
-    /* test simple mappings */
-    ASSERT_MAPPING("a", "[a]");
-    ASSERT_MAPPING("b", "[b]");
-    ASSERT_MAPPING("[c]", "c");
-    ASSERT_MAPPING("d", " [d]");
-    ASSERT_MAPPING("<Tab>", "[tab]");
-    ASSERT_MAPPING("<S-Tab>", "[shift-tab]");
-    ASSERT_MAPPING("<C-F>", "[ctrl-f]");
-    ASSERT_MAPPING("<C-f>", "[ctrl-f]");
-    ASSERT_MAPPING("<CR>", "[cr]");
-    ASSERT_MAPPING("foobar", "[baz]");
-
-    /* key sequences that are not changed by mappings */
-    ASSERT_MAPPING("fghi", "fghi");
-}
-
-static void test_handle_string_alias(void)
-{
-    /* CTRL-I is the same like <Tab> and CTRL-M like <CR> */
-    ASSERT_MAPPING("<C-I>", "[tab]");
-    ASSERT_MAPPING("<C-M>", "[cr]");
-}
-
-static void test_handle_string_remapped(void)
-{
-    /* test multiple mappings together */
-    ASSERT_MAPPING("ba", "[b][a]");
-    ASSERT_MAPPING("ab12345[c]", "[a][b]12345c");
-
-    /* incomplete ambiguous sequences are not matched jet */
-    ASSERT_MAPPING("foob", "");
-    ASSERT_MAPPING("ar", "[baz]");
-
-    /* test remapping */
-    map_insert("c", "baza", 't', true);
-    ASSERT_MAPPING("c", "[b][a]z[a]");
-    map_insert("d", "cki", 't', true);
-    ASSERT_MAPPING("d", "[b][a]z[a]ki");
-
-    /* remove the no more needed mappings */
-    map_delete("c", 't');
-    map_delete("d", 't');
-}
-
-static void test_handle_string_overrule(void)
-{
-    /* add another map for 'a' and check if this overrules the previous set */
-    map_insert("a", "overruled", 't', false);
-    ASSERT_MAPPING("a", "overruled");
-}
-
-static void test_remove(void)
-{
-    map_insert("x", "[x]", 't', false);
-    /* make sure that the mapping works */
-    ASSERT_MAPPING("x", "[x]");
-
-    map_delete("x", 't');
-
-    /* make sure the mapping  removed */
-    ASSERT_MAPPING("x", "x");
-}
-
-static void test_keypress_single(void)
-{
-    QUEUE_CLEAR();
-
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_Tab, .state = GDK_SHIFT_MASK}), NULL);
-    g_assert_cmpstr(queue, ==, "[shift-tab]");
-}
-
-static void test_keypress_sequence(void)
-{
-    QUEUE_CLEAR();
-
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_f}), NULL);
-    g_assert_cmpstr(queue, ==, "");
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_o}), NULL);
-    g_assert_cmpstr(queue, ==, "");
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_o}), NULL);
-    g_assert_cmpstr(queue, ==, "");
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_b}), NULL);
-    g_assert_cmpstr(queue, ==, "");
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_a}), NULL);
-    g_assert_cmpstr(queue, ==, "");
-    map_keypress(NULL, &((GdkEventKey){.keyval = GDK_r}), NULL);
-    g_assert_cmpstr(queue, ==, "[baz]");
-}
-
-int main(int argc, char *argv[])
-{
-    int result;
-    g_test_init(&argc, &argv, NULL);
-
-    /* add a test mode to handle the maped sequences */
-    vb_add_mode('t', NULL, NULL, keypress, NULL);
-    vb_enter('t');
-    map_init();
-
-    g_test_add_func("/test-map/handle_string/simple", test_handle_string_simple);
-    g_test_add_func("/test-map/handle_string/alias", test_handle_string_alias);
-    g_test_add_func("/test-map/handle_string/remapped", test_handle_string_remapped);
-    g_test_add_func("/test-map/handle_string/overrule", test_handle_string_overrule);
-    g_test_add_func("/test-map/remove", test_remove);
-    g_test_add_func("/test-map/keypress/single-char", test_keypress_single);
-    g_test_add_func("/test-map/keypress/sequence", test_keypress_sequence);
-
-    /* add some mappings to test */
-    map_insert("a", "[a]", 't', false);         /* inlen < mappedlen  */
-    map_insert("b", "[b]", 't', false);
-    map_insert("d", "<Space>[d]", 't', false);
-    map_insert("[c]", "c", 't', false);         /* inlen > mappedlen  */
-    map_insert("foobar", "[baz]", 't', false);
-    map_insert("<Tab>", "[tab]", 't', false);
-    map_insert("<S-Tab>", "[shift-tab]", 't', false);
-    map_insert("<C-F>", "[ctrl-f]", 't', false);
-    map_insert("<CR>", "[cr]", 't', false);
-
-    result = g_test_run();
-    map_cleanup();
-    return result;
-}
diff --git a/tests/test-shortcut.c b/tests/test-shortcut.c
deleted file mode 100644 (file)
index 697a04d..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include <gtk/gtk.h>
-#include <src/shortcut.h>
-
-static void test_shortcut_single(void)
-{
-    char *uri;
-
-    /* call with shortcut identifier */
-    uri = shortcut_get_uri("_vimb1_ zero one");
-    g_assert_cmpstr(uri, ==, "only-zero:zero%20one");
-    g_free(uri);
-
-    /* don't fail on unmatches quotes if there are only $0 placeholders */
-    uri = shortcut_get_uri("_vimb1_ 'unmatched quote");
-    g_assert_cmpstr(uri, ==, "only-zero:'unmatched%20quote");
-    g_free(uri);
-
-    /* check if all placeholders $0 are replaces */
-    uri = shortcut_get_uri("_vimb5_ one two");
-    g_assert_cmpstr(uri, ==, "double-zero:one%20two-one%20two");
-    g_free(uri);
-}
-
-static void test_shortcut_default(void)
-{
-    char *uri;
-
-    /* call without shortcut identifier and if the last placeholder become the
-     * none matched query words */
-    uri = shortcut_get_uri("zero one two three");
-    g_assert_cmpstr(uri, ==, "default:zero-two%20three");
-    g_free(uri);
-}
-
-static void test_shortcut_keep_unmatched(void)
-{
-    char *uri;
-
-    /* don't remove non matched placeholders */
-    uri = shortcut_get_uri("zero");
-    g_assert_cmpstr(uri, ==, "default:zero-$2");
-    g_free(uri);
-}
-
-static void test_shortcut_fullrange(void)
-{
-    char *uri;
-
-    /* check if all placeholders $0-$9 are replaced */
-    uri = shortcut_get_uri("_vimb3_ zero one two three four five six seven eight nine");
-    g_assert_cmpstr(uri, ==, "fullrange:zero-one-nine");
-    g_free(uri);
-}
-
-static void test_shortcut_shell_param(void)
-{
-    char *uri;
-
-    /* double quotes */
-    uri = shortcut_get_uri("_vimb6_ \"rail station\" city hall");
-    g_assert_cmpstr(uri, ==, "shell:rail%20station-city%20hall");
-    g_free(uri);
-
-    /* single quotes */
-    uri = shortcut_get_uri("_vimb6_ 'rail station' 'city hall'");
-    g_assert_cmpstr(uri, ==, "shell:rail%20station-city%20hall");
-    g_free(uri);
-
-    /* ignore none matching quote errors */
-    uri = shortcut_get_uri("_vimb6_ \"rail station\" \"city hall");
-    g_assert_cmpstr(uri, ==, "shell:rail%20station-city%20hall");
-    g_free(uri);
-
-    /* don't fill up quoted param with unquoted stuff */
-    uri = shortcut_get_uri("_vimb6_ \"param 1\" \"param 2\" ignored params");
-    g_assert_cmpstr(uri, ==, "shell:param%201-param%202");
-    g_free(uri);
-
-    /* allo quotes within tha last parameter */
-    uri = shortcut_get_uri("_vimb6_ param1 param2 \"containing quotes\"");
-    g_assert_cmpstr(uri, ==, "shell:param1-param2%20%22containing%20quotes%22");
-    g_free(uri);
-}
-
-static void test_shortcut_remove(void)
-{
-    char *uri;
-
-    g_assert_true(shortcut_remove("_vimb4_"));
-
-    /* check if the shortcut is really no used */
-    uri = shortcut_get_uri("_vimb4_ test");
-    g_assert_cmpstr(uri, ==, "default:_vimb4_-$2");
-}
-
-int main(int argc, char *argv[])
-{
-    int result;
-    shortcut_init();
-
-    g_assert_true(shortcut_add("_vimb1_", "only-zero:$0"));
-    g_assert_true(shortcut_add("_vimb2_", "default:$0-$2"));
-    g_assert_true(shortcut_add("_vimb3_", "fullrange:$0-$1-$9"));
-    g_assert_true(shortcut_add("_vimb4_", "for-remove:$0"));
-    g_assert_true(shortcut_add("_vimb5_", "double-zero:$0-$0"));
-    g_assert_true(shortcut_add("_vimb6_", "shell:$0-$1"));
-    g_assert_true(shortcut_set_default("_vimb2_"));
-
-    g_test_init(&argc, &argv, NULL);
-
-    g_test_add_func("/test-shortcut/get_uri/single", test_shortcut_single);
-    g_test_add_func("/test-shortcut/get_uri/default", test_shortcut_default);
-    g_test_add_func("/test-shortcut/get_uri/keep-unmatched", test_shortcut_keep_unmatched);
-    g_test_add_func("/test-shortcut/get_uri/fullrange", test_shortcut_fullrange);
-    g_test_add_func("/test-shortcut/get_uri/shell-param", test_shortcut_shell_param);
-    g_test_add_func("/test-shortcut/remove", test_shortcut_remove);
-
-    result = g_test_run();
-
-    shortcut_cleanup();
-
-    return result;
-}
diff --git a/tests/test-util.c b/tests/test-util.c
deleted file mode 100644 (file)
index 67b1954..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * vimb - a webkit based vim like browser.
- *
- * Copyright (C) 2012-2015 Daniel Carl
- *
- * This program 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.
- *
- * This program 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 this program. If not, see http://www.gnu.org/licenses/.
- */
-
-#include <gtk/gtk.h>
-#include <src/util.h>
-
-extern VbCore vb;
-
-static void check_expand(const char *str, const char *expected)
-{
-    char *result = util_expand(str, UTIL_EXP_DOLLAR|UTIL_EXP_TILDE|UTIL_EXP_SPECIAL);
-    g_assert_cmpstr(result, ==, expected);
-    g_free(result);
-}
-
-static void test_expand_evn(void)
-{
-    /* set environment var for testing expansion */
-    g_setenv("VIMB_VAR", "value", true);
-
-    check_expand("$VIMB_VAR", "value");
-    check_expand("$VIMB_VAR", "value");
-    check_expand("$VIMB_VAR$VIMB_VAR", "valuevalue");
-    check_expand("${VIMB_VAR}", "value");
-    check_expand("my$VIMB_VAR", "myvalue");
-    check_expand("'$VIMB_VAR'", "'value'");
-    check_expand("${VIMB_VAR}s ", "values ");
-
-    g_unsetenv("UNKNOWN");
-
-    check_expand("$UNKNOWN", "");
-    check_expand("${UNKNOWN}", "");
-    check_expand("'$UNKNOWN'", "''");
-}
-
-static void test_expand_escaped(void)
-{
-    g_setenv("VIMB_VAR", "value", true);
-
-    check_expand("\\$VIMB_VAR", "$VIMB_VAR");
-    check_expand("\\${VIMB_VAR}", "${VIMB_VAR}");
-
-    check_expand("\\~/", "~/");
-    check_expand("\\~/vimb", "~/vimb");
-    check_expand("\\~root", "~root");
-
-    check_expand("\\%", "%");
-
-    check_expand("\\\\$VIMB_VAR", "\\value");         /* \\$VAR becomes \ExpandedVar */
-    check_expand("\\\\\\$VIMB_VAR", "\\$VIMB_VAR");   /* \\\$VAR becomes \$VAR */
-}
-
-static void test_expand_tilde_home(void)
-{
-    char *dir;
-    const char *home = util_get_home_dir();
-
-    check_expand("~", "~");
-    check_expand("~/", home);
-    check_expand("foo~/bar", "foo~/bar");
-    check_expand("~/foo", (dir = g_strdup_printf("%s/foo", home)));
-    g_free(dir);
-
-    check_expand("foo ~/bar", (dir = g_strdup_printf("foo %s/bar", home)));
-    g_free(dir);
-
-    check_expand("~/~", (dir = g_strdup_printf("%s/~", home)));
-    g_free(dir);
-
-    check_expand("~/~/", (dir = g_strdup_printf("%s/~/", home)));
-    g_free(dir);
-}
-
-static void test_expand_tilde_user(void)
-{
-    const char *home = util_get_home_dir();
-    const char *user = g_get_user_name();
-    char *in, *out;
-
-    /* don't expand within words */
-    in = g_strdup_printf("foo~%s/bar", user);
-    check_expand(in, in);
-    g_free(in);
-
-    check_expand((in = g_strdup_printf("foo ~%s", user)), (out = g_strdup_printf("foo %s", home)));
-    g_free(in);
-    g_free(out);
-
-    check_expand((in = g_strdup_printf("~%s", user)), home);
-    g_free(in);
-
-    check_expand((in = g_strdup_printf("~%s/bar", user)), (out = g_strdup_printf("%s/bar", home)));
-    g_free(in);
-    g_free(out);
-}
-
-static void test_expand_speacial(void)
-{
-    vb.state.uri = "http://fanglingsu.github.io/vimb/";
-
-    check_expand("%", "http://fanglingsu.github.io/vimb/");
-    check_expand("'%'", "'http://fanglingsu.github.io/vimb/'");
-}
-
-static void test_strcasestr(void)
-{
-    g_assert_nonnull(util_strcasestr("Vim like Browser", "browser"));
-    g_assert_nonnull(util_strcasestr("Vim like Browser", "vim LIKE"));
-}
-
-static void test_str_replace(void)
-{
-    char *value;
-
-    value = util_str_replace("a", "uu", "foo bar baz");
-    g_assert_cmpstr(value, ==, "foo buur buuz");
-    g_free(value);
-
-    value = util_str_replace("$1", "placeholder", "string with $1");
-    g_assert_cmpstr(value, ==, "string with placeholder");
-    g_free(value);
-}
-
-static void test_wildmatch_simple(void)
-{
-    g_assert_true(util_wildmatch("", ""));
-    g_assert_true(util_wildmatch("w", "w"));
-    g_assert_true(util_wildmatch(".", "."));
-    g_assert_true(util_wildmatch("~", "~"));
-    g_assert_true(util_wildmatch("wildmatch", "WildMatch"));
-    g_assert_true(util_wildmatch("wild\\match", "wild\\match"));
-
-    /* no special meaning of . and ~ in pattern */
-    g_assert_false(util_wildmatch(".", "w"));
-    g_assert_false(util_wildmatch("~", "w"));
-    g_assert_false(util_wildmatch("wild", "wild "));
-    g_assert_false(util_wildmatch("wild", " wild"));
-    g_assert_false(util_wildmatch("wild", "\\ wild"));
-    g_assert_false(util_wildmatch("wild", "\\wild"));
-    g_assert_false(util_wildmatch("wild", "wild\\"));
-    g_assert_false(util_wildmatch("wild\\1", "wild\\2"));
-}
-
-static void test_wildmatch_questionmark(void)
-{
-    g_assert_true(util_wildmatch("wild?atch", "wildmatch"));
-    g_assert_true(util_wildmatch("wild?atch", "wildBatch"));
-    g_assert_true(util_wildmatch("wild?atch", "wild?atch"));
-    g_assert_true(util_wildmatch("?ild?atch", "MildBatch"));
-    g_assert_true(util_wildmatch("foo\\?bar", "foo?bar"));
-    g_assert_true(util_wildmatch("???", "foo"));
-    g_assert_true(util_wildmatch("???", "bar"));
-
-    g_assert_false(util_wildmatch("foo\\?bar", "foorbar"));
-    g_assert_false(util_wildmatch("?", ""));
-    g_assert_false(util_wildmatch("b??r", "bar"));
-    /* ? does not match / in contrast to * which does */
-    g_assert_false(util_wildmatch("user?share", "user/share"));
-}
-
-static void test_wildmatch_wildcard(void)
-{
-    g_assert_true(util_wildmatch("*", ""));
-    g_assert_true(util_wildmatch("*", "Match as much as possible"));
-    g_assert_true(util_wildmatch("*match", "prefix match"));
-    g_assert_true(util_wildmatch("match*", "match suffix"));
-    g_assert_true(util_wildmatch("match*", "match*"));
-    g_assert_true(util_wildmatch("match\\*", "match*"));
-    g_assert_true(util_wildmatch("match\\\\*", "match\\*"));
-    g_assert_true(util_wildmatch("do * match", "do a infix match"));
-    /* '*' matches also / in contrast to other implementations */
-    g_assert_true(util_wildmatch("start*end", "start/something/end"));
-    g_assert_true(util_wildmatch("*://*.io/*", "http://fanglingsu.github.io/vimb/"));
-    /* multiple * should act like a single one */
-    g_assert_true(util_wildmatch("**", ""));
-    g_assert_true(util_wildmatch("match **", "Match as much as possible"));
-    g_assert_true(util_wildmatch("f***u", "fu"));
-
-    g_assert_false(util_wildmatch("match\\*", "match fail"));
-    g_assert_false(util_wildmatch("f***u", "full"));
-}
-
-static void test_wildmatch_curlybraces(void)
-{
-    g_assert_true(util_wildmatch("{foo}", "foo"));
-    g_assert_true(util_wildmatch("{foo,bar}", "foo"));
-    g_assert_true(util_wildmatch("{foo,bar}", "bar"));
-    g_assert_true(util_wildmatch("foo{lish,t}bar", "foolishbar"));
-    g_assert_true(util_wildmatch("foo{lish,t}bar", "footbar"));
-    /* esacped special chars */
-    g_assert_true(util_wildmatch("foo\\{l\\}bar", "foo{l}bar"));
-    g_assert_true(util_wildmatch("ba{r,z\\{\\}}", "bar"));
-    g_assert_true(util_wildmatch("ba{r,z\\{\\}}", "baz{}"));
-    g_assert_true(util_wildmatch("test{one\\,two,three}", "testone,two"));
-    g_assert_true(util_wildmatch("test{one\\,two,three}", "testthree"));
-    /* backslash before none special char is a normal char */
-    g_assert_true(util_wildmatch("back{\\slash,}", "back\\slash"));
-    g_assert_true(util_wildmatch("one\\two", "one\\two"));
-    g_assert_true(util_wildmatch("\\}match", "}match"));
-    g_assert_true(util_wildmatch("\\{", "{"));
-    /* empty list parts */
-    g_assert_true(util_wildmatch("{}", ""));
-    g_assert_true(util_wildmatch("{,}", ""));
-    g_assert_true(util_wildmatch("{,foo}", ""));
-    g_assert_true(util_wildmatch("{,foo}", "foo"));
-    g_assert_true(util_wildmatch("{bar,}", ""));
-    g_assert_true(util_wildmatch("{bar,}", "bar"));
-    /* no special meaning of ? and * in curly braces */
-    g_assert_true(util_wildmatch("ab{*,cd}ef", "ab*ef"));
-    g_assert_true(util_wildmatch("ab{d,?}ef", "ab?ef"));
-
-    g_assert_false(util_wildmatch("{foo,bar}", "foo,bar"));
-    g_assert_false(util_wildmatch("}match{ it", "}match{ anything"));
-    /* don't match single parts that are seperated by escaped ',' */
-    g_assert_false(util_wildmatch("{a,b\\,c,d}", "b"));
-    g_assert_false(util_wildmatch("{a,b\\,c,d}", "c"));
-    /* lonesome braces - this is a syntax error and will always be false */
-    g_assert_false(util_wildmatch("}", "}"));
-    g_assert_false(util_wildmatch("}", ""));
-    g_assert_false(util_wildmatch("}suffix", "}suffux"));
-    g_assert_false(util_wildmatch("}suffix", "suffux"));
-    g_assert_false(util_wildmatch("{", "{"));
-    g_assert_false(util_wildmatch("{", ""));
-    g_assert_false(util_wildmatch("{foo", "{foo"));
-    g_assert_false(util_wildmatch("{foo", "foo"));
-    g_assert_false(util_wildmatch("foo{bar", "foo{bar"));
-}
-
-static void test_wildmatch_complete(void)
-{
-    g_assert_true(util_wildmatch("http{s,}://{fanglingsu.,}github.{io,com}/*vimb/", "http://fanglingsu.github.io/vimb/"));
-    g_assert_true(util_wildmatch("http{s,}://{fanglingsu.,}github.{io,com}/*vimb/", "https://github.com/fanglingsu/vimb/"));
-}
-
-static void test_wildmatch_multi(void)
-{
-    g_assert_true(util_wildmatch("foo,?", "foo"));
-    g_assert_true(util_wildmatch("foo,?", "f"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "foo"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "bar"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "bor"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "br"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "baz"));
-    g_assert_true(util_wildmatch("foo,b{a,o,}r,ba?", "bat"));
-
-    g_assert_false(util_wildmatch("foo,b{a,o,}r,ba?", "foo,"));
-    g_assert_false(util_wildmatch("foo,?", "fo"));
-}
-
-int main(int argc, char *argv[])
-{
-    g_test_init(&argc, &argv, NULL);
-
-    g_test_add_func("/test-util/expand-env", test_expand_evn);
-    g_test_add_func("/test-util/expand-escaped", test_expand_escaped);
-    g_test_add_func("/test-util/expand-tilde-home", test_expand_tilde_home);
-    g_test_add_func("/test-util/expand-tilde-user", test_expand_tilde_user);
-    g_test_add_func("/test-util/expand-spacial", test_expand_speacial);
-    g_test_add_func("/test-util/strcasestr", test_strcasestr);
-    g_test_add_func("/test-util/str_replace", test_str_replace);
-    g_test_add_func("/test-util/wildmatch-simple", test_wildmatch_simple);
-    g_test_add_func("/test-util/wildmatch-questionmark", test_wildmatch_questionmark);
-    g_test_add_func("/test-util/wildmatch-wildcard", test_wildmatch_wildcard);
-    g_test_add_func("/test-util/wildmatch-curlybraces", test_wildmatch_curlybraces);
-    g_test_add_func("/test-util/wildmatch-complete", test_wildmatch_complete);
-    g_test_add_func("/test-util/wildmatch-multi", test_wildmatch_multi);
-
-    return g_test_run();
-}